Ember JSONAPIAdapter
目前 Emberjs 框架中使用 JSONAPIAdapter 爲默認的 adapter,遵循 JSONAPI 的通信標準。目前本公司也默認使用的是此 adapter,所以一下 api 均是在此基礎上。
另如無特殊說明,文內的文件結構均是在 Pods 目錄結構下的。
文章目錄
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 |
---|---|---|
Find 1 | GET | /posts/123 |
Find All 2 | GET | /posts |
Update 3 | PATCH | /posts/123 |
Create 4 | POST | /posts |
Delete 5 | 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
export default {};
然後可以看到 請求發送的地址是/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() {
return this.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';
export default DS.JSONAPIAdapter.extend({
coalesceFindRequests: true
});
可以看到現在只發送一條請求:
GET '/comments?filter[id]=1,2
defaultSerializer
defaultSerializer
這個屬性設置使用的 serializer
:
// post/adapter.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
defaultSerializer: 'person'
});
將使用 person/serializer.js
中的設定對 post
進行設定。
需要注意的是此屬性起作用的時候只有在此 model 的serializer.js
以及 application/serializer.js
不存在的時候起作用(Pods目錄)。
header
HTTP 消息頭允許客戶端和服務器通過 request和 response傳遞附加信息6。某一些 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';
export default DS.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
- updateRecord()
- deleteRecord()
generateIdForRecord()
用於生成在客戶端生成的 Record 的id.返回的值將分配給 record 的primaryKey
.一般很少使用.比如:
// bj-company/adapter.js
generateIdForRecord(store, type, inputProperties) {
return 343;
}
新創建的 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 is only 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 by FrankWang.