Emberjs中JSONAPIAdapter的常用API

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 消息頭允許客戶端和服務器通過 requestresponse傳遞附加信息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:

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 也類似:

總結

JSONAPIAdapter 的相關API 的分析到此結束.

Written by FrankWang.


  1. this.get('store').findRecord('post',1) ↩︎

  2. this.get('store').findAll('post') ↩︎

  3. postRecord.save() ↩︎

  4. this.get('store').createRecord('post').save() ↩︎

  5. postRecord.destroyRecord() ↩︎

  6. MDN 中查看 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章