Ember.js 中 JSONAPIAdapter 的常用 API
Ember JSONAPIAdapter
目前 Emberjs 框架中使用 JSONAPIAdapter 為預設的 adapter,遵循 JSONAPI 的通訊標準。目前本公司也預設使用的是此 adapter,所以一下 api 均是在此基礎上。
Adapter
在 Ember Data 中,adapter 決定了如何向後端傳遞資料,提供了一些可以設定的介面,如 格式化請求的URL ,設定請求的header 等。
在Emberjs 專案中,你可以設定頂層的application/adapter.js
也可以在每個對應的model
(pods檔案目錄)的檔案中建立針對單個model
的adapter:modelName/adapter.js
。其中針對單個的adapter.js
的優先權大於application/adapter.js
。
URL Conventions
在 Ember Data 中預設使用的DS.JSONAPIAdapter
中,如果要請求資料,可以在route.js
中:
//route.js model() { return this.get('store').findAll('post'); }
上面的請求預設發向的 url為/posts
,也就是 JSONAPIAdapter 會預設為請求路徑轉換為複數。
提供了幾個預設的請求:
Action | HTTP Verb | URL |
---|---|---|
GET | /posts/123 | |
GET | /posts | |
PATCH | /posts/123 | |
POST | /posts | |
DELETE | /posts/123 |
請求過程中的複數轉換
上文也提到了,在使用 JSONAPIAdapter 過程中,會進行復數的轉換,包括對modelName
也是,會進行轉換,比如說 我們請求:
model(){ return this.get('store').findAll('campus'); }
在JSONAPIAdapter
中會發送請求到/campus
中,而尋找的modelName
則是campu
這顯然不對,所以我們需要對特殊字詞進行處理。
在 Ember Data 中使用的是Ember Inflector 控制的複數轉換。同樣的,我們也需要對它進行設定(pods目錄下):
// app/app.js import'./modules/custom-inflector-rules';
//app/modules/custom-inflector-rules.js import Inflector from'ember-inflector'; const inflector = Inflector.inflector; // Tell the inflector that the plural of "campus" is "campuses" inflector.irregular('campus', 'campuses'); // Modules must have an export, so we just export an empty object here exportdefault {};
然後可以看到 請求傳送的地址是/campuses
,尋找的modelName
也是campus
,現在變成正常的了,資料也是可以正常顯示的了。
properties
JSONAPIAdapter
提供了以下 porperties :
coalesceFindRequests
這有篇文章 講的這個屬性的使用。下面是具體的使用:
我們先來看不設定此屬性的時候:
//後端返回的資料 'data': { 'type':'post', 'id':'idPost1', 'attributes': { 'title':'post1', 'content':'post content' }, 'relationships': { 'comments': { 'data': [ { 'id':1, 'type':'comment' }, { 'id':2, 'type':'comment' } ] } } }
這是post資料,當我們請求post
資料的時候:
//route.js model() { returnthis.get('store').findRecord('post', 'idPost1'); }
這時候可以看到 Ember Data 向 mirage 傳送了兩條請求(需要設定{ async:true }
:
GET '/comments/1' GET '/comments/2'
在將coalesceFindRequests
屬性設定為true
的時候:
//comment/adapter.js import DS from'ember-data'; exportdefaultDS.JSONAPIAdapter.extend({ coalesceFindRequests:true });
可以看到現在只發送一條請求:
GET '/comments?filter[id]=1,2
defaultSerializer
defaultSerializer
這個屬性設定使用的serializer
:
// post/adapter.js import DS from'ember-data'; exportdefaultDS.JSONAPIAdapter.extend({ defaultSerializer: 'person' });
將使用person/serializer.js
中的設定對post
進行設定。
需要注意的是此屬性起作用的時候只有在此 model 的serializer.js
以及application/serializer.js
不存在的時候起作用(Pods目錄)。
header
HTTP 訊息頭允許客戶端和伺服器通過request
和response
傳遞附加資訊。某一些 API 會需要一些請求頭,比如現在專案中使用到的 token,就是在每次進行請求的時候都攜帶這些請求頭資料傳送給後端服務。一般不在init()
中設定header ,而是將其設為計算屬性:
//post/adapter.js headers:computed(function () { return { 'dataType':'json', 'contentType':'application/json', 'Content-Type':'application/json', 'Authorization':`bearer selfToken` }; })
請求頭就被改變了。
這樣就會在每次請求的時候攜帶本地的 token。
host
自定義主機,預設為本地作用域。
namespace
顧名思義,定義名稱空間的.
//adapter.js namespace: '/api/'
main method
pathForType(type)
格式化請求的路徑:
//router this.get('store').findAll('bjCompany');
如果不在adapter.js
中進行設定,傳送的請求是:
GET /bj-companies
也就是預設的轉換為中劃線以及進行復數化,如果不想進行中劃線的轉換:
//bj-company/adapter.js import DS from'ember-data'; import { camelize } from'@ember/string'; import { pluralize } from'ember-inflector'; exportdefaultDS.JSONAPIAdapter.extend({ pathForType(type) { let newType =pluralize(camelize(type)); return newType;// newType: bjCompanies } });
這樣就達到了我們的目的.
buildURL
對URL 進行格式化,主要是進行復數化,可以通過複寫pathForType()
方法來達到重寫 URL 的目的.
Record 相關
JSONAPIAdapter
提供的關於 record 的一些 hook,可以讓你複寫這些hook的邏輯來達到自己的目的,但是一般完全符合 JSONAPI 的資料規範後,這些基本不用重寫.更多關於 Record 的部分請查詢 相關API以及其他文件.
這裡列舉出來JSONAPIAdapter
中涉及 record 的一些 hook:
- [createRecord() ]()
-
fetchRecord
- findAll()
- findBelongsTo()
- findHasMany()
- findMany()
- findRecord()
- [query()]()
- [queryRecord()]()
- updateRecord()
- deleteRecord()
generateIdForRecord()
用於生成在客戶端生成的 Record 的id.返回的值將分配給 record 的primaryKey
.一般很少使用.比如:
//bj-company/adapter.js generateIdForRecord(store, type, inputProperties) { return343; }
新建立的 Record 的 id 就會變成 343(這裡只是演示作用).
handleResponse()
返回 ajax 請求的資料或錯誤,如果想修改返回的資料規範或錯誤提示可以在此處進行修改.
很少使用,視具體專案情況而使用.
isInvalid()
驗證如果是 422 錯誤,在handleResponse()
返回一個InvalidError()
的例項.
isSuccess()
請求返回成功,相應的status:
(status >=200&& status <300) || status ===304;
shouldBackgroundReloadAll()
store使用此方法來確定在store.findAll
使用快取的記錄陣列解析後,儲存是否應重新載入記錄陣列。
預設為true
.
設為false
之後,帶來的效果就是在本地兩個頁面同時顯示同一 model 例項,從一頁面跳轉到另一頁面的時候不會再次請求資料.
//adapter.js shouldBackgroundReloadAll(store, snapshotArray) { return false; }
注意這個方法只有在store 返回快取資料之後才被呼叫.也就是當第一次請求資料的時候此方法不會被執行.
This method isonly checked by the store when the store is returning a cached record array.
shouldBackgroundReloadRecord()
與上面同理.
shouldReloadAll()
當返回 true 的時候會立刻再次請求資料,如果返回false,會立即使用本地快取.具體使用例項可以檢視文件
shouldReloadRecord()
與上面同理.
sortQueryParams()
對查詢的 引數 進行自定義排列,預設使用的是正序.
urlForCreateRecord()
為通過store.createRecord()
建立的本地 record 在進行record.save()
操作的時候構建 相應的url
;
其他的api 也類似:
- urlForDeleteRecord(id, modelName, snapshot)
- urlForFindAll(modelName, snapshot)
- urlForFindBelongsTo(id, modelName, snapshot)
- urlForFindHasMany()
- urlForFindMany()
- urlForFindRecord()
- urlForQuery()
- urlForQueryRecord
- urlForUpdateRecord()
總結
JSONAPIAdapter 的相關API 的分析到此結束.
Written byFrankWang .
-
this.get('store').findRecord('post',1)
-
this.get('store').findAll('post')
-
postRecord.save()
-
this.get('store').createRecord('post').save()
-
postRecord.destroyRecord()
- 在MDN 中檢視