Ember适配器与序列化器 #
一、适配器概述 #
适配器负责处理与后端API的通信,决定如何发送请求和接收响应。
1.1 适配器类型 #
| 类型 | 说明 |
|---|---|
| RESTAdapter | RESTful API |
| JSONAPIAdapter | JSON API规范 |
| 自定义Adapter | 自定义API格式 |
1.2 适配器层次 #
text
ApplicationAdapter (全局默认)
↓
ModelAdapter (特定模型)
二、RESTAdapter #
2.1 基本配置 #
javascript
// app/adapters/application.js
import RESTAdapter from '@ember-data/adapter/rest';
export default class ApplicationAdapter extends RESTAdapter {
namespace = 'api/v1';
host = 'https://api.example.com';
}
2.2 请求URL #
javascript
// findAll
GET /api/v1/posts
// findRecord
GET /api/v1/posts/1
// createRecord
POST /api/v1/posts
// updateRecord
PUT /api/v1/posts/1
// deleteRecord
DELETE /api/v1/posts/1
2.3 自定义headers #
javascript
export default class ApplicationAdapter extends RESTAdapter {
get headers() {
return {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.session.token}`,
};
}
}
2.4 路径自定义 #
javascript
export default class ApplicationAdapter extends RESTAdapter {
pathForType(modelName) {
const decamelized = modelName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
return decamelized.pluralize();
}
}
三、JSONAPIAdapter #
3.1 基本配置 #
javascript
// app/adapters/application.js
import JSONAPIAdapter from '@ember-data/adapter/json-api';
export default class ApplicationAdapter extends JSONAPIAdapter {
namespace = 'api/v1';
}
3.2 JSON API格式 #
json
{
"data": {
"type": "posts",
"id": "1",
"attributes": {
"title": "Hello World",
"body": "Content..."
},
"relationships": {
"author": {
"data": { "type": "users", "id": "1" }
}
}
}
}
四、自定义适配器 #
4.1 模型特定适配器 #
javascript
// app/adapters/post.js
import ApplicationAdapter from './application';
export default class PostAdapter extends ApplicationAdapter {
namespace = 'api/v2';
urlForFindRecord(id, modelName, snapshot) {
return `${this.host}/${this.namespace}/posts/${id}/detail`;
}
}
4.2 自定义请求方法 #
javascript
export default class PostAdapter extends ApplicationAdapter {
publish(id) {
const url = this.buildURL('post', id) + '/publish';
return this.ajax(url, 'POST');
}
archive(id) {
const url = this.buildURL('post', id) + '/archive';
return this.ajax(url, 'PUT');
}
}
4.3 查询参数处理 #
javascript
export default class PostAdapter extends ApplicationAdapter {
urlForQuery(query, modelName) {
if (query.category) {
return `${this.host}/${this.namespace}/categories/${query.category}/posts`;
}
return super.urlForQuery(...arguments);
}
}
五、序列化器概述 #
序列化器负责数据格式的转换,处理请求和响应的数据格式化。
5.1 序列化器类型 #
| 类型 | 说明 |
|---|---|
| RESTSerializer | REST API格式 |
| JSONAPISerializer | JSON API格式 |
| 自定义Serializer | 自定义格式 |
六、RESTSerializer #
6.1 基本配置 #
javascript
// app/serializers/application.js
import RESTSerializer from '@ember-data/serializer/rest';
export default class ApplicationSerializer extends RESTSerializer {
}
6.2 期望的响应格式 #
json
{
"posts": [
{
"id": 1,
"title": "Post 1",
"body": "Content...",
"author_id": 1
}
]
}
6.3 自定义键映射 #
javascript
export default class ApplicationSerializer extends RESTSerializer {
keyForAttribute(key) {
return key; // 保持驼峰命名
}
keyForRelationship(key) {
return key; // 保持驼峰命名
}
}
七、JSONAPISerializer #
7.1 基本配置 #
javascript
// app/serializers/application.js
import JSONAPISerializer from '@ember-data/serializer/json-api';
export default class ApplicationSerializer extends JSONAPISerializer {
}
7.2 自定义属性映射 #
javascript
export default class ApplicationSerializer extends JSONAPISerializer {
attrs = {
createdAt: 'created_at',
updatedAt: 'updated_at',
};
}
八、自定义序列化器 #
8.1 处理自定义响应 #
javascript
// app/serializers/post.js
import RESTSerializer from '@ember-data/serializer/rest';
export default class PostSerializer extends RESTSerializer {
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
// 处理自定义响应格式
if (payload.data) {
payload.posts = payload.data;
delete payload.data;
}
return super.normalizeResponse(...arguments);
}
normalize(modelClass, resourceHash) {
// 自定义字段映射
resourceHash.attributes = resourceHash.attributes || {};
resourceHash.attributes.title = resourceHash.title;
resourceHash.attributes.body = resourceHash.body;
return super.normalize(...arguments);
}
}
8.2 序列化请求 #
javascript
export default class PostSerializer extends RESTSerializer {
serialize(snapshot, options) {
const json = super.serialize(...arguments);
// 添加额外字段
json.clientTimestamp = Date.now();
return json;
}
serializeAttribute(snapshot, json, key, attribute) {
// 排除某些字段
if (key === 'internalField') {
return;
}
super.serializeAttribute(...arguments);
}
}
8.3 处理关联 #
javascript
export default class PostSerializer extends RESTSerializer {
serializeBelongsTo(snapshot, json, relationship) {
const key = relationship.key;
const belongsTo = snapshot.belongsTo(key);
if (belongsTo) {
json[`${key}Id`] = belongsTo.id;
}
}
serializeHasMany(snapshot, json, relationship) {
const key = relationship.key;
const hasMany = snapshot.hasMany(key);
if (hasMany) {
json[`${key}Ids`] = hasMany.map((item) => item.id);
}
}
}
九、常见场景 #
9.1 嵌套路由 #
javascript
// app/adapters/comment.js
import ApplicationAdapter from './application';
export default class CommentAdapter extends ApplicationAdapter {
buildURL(modelName, id, snapshot, requestType, query) {
let url = super.buildURL(...arguments);
if (snapshot && snapshot.record && snapshot.record.post) {
const postId = snapshot.record.post.get('id');
url = url.replace('/comments', `/posts/${postId}/comments`);
}
return url;
}
}
9.2 认证处理 #
javascript
// app/adapters/application.js
import RESTAdapter from '@ember-data/adapter/rest';
import { inject as service } from '@ember/service';
export default class ApplicationAdapter extends RESTAdapter {
@service session;
get headers() {
if (this.session.isAuthenticated) {
return {
Authorization: `Bearer ${this.session.token}`,
};
}
return {};
}
handleResponse(status, headers, payload) {
if (status === 401) {
this.session.invalidate();
}
return super.handleResponse(...arguments);
}
}
9.3 错误处理 #
javascript
export default class ApplicationAdapter extends RESTAdapter {
handleResponse(status, headers, payload) {
if (this.isInvalid(status, headers, payload)) {
return payload.errors || payload;
}
return super.handleResponse(...arguments);
}
isInvalid(status) {
return status === 422;
}
}
十、最佳实践 #
10.1 使用约定 #
javascript
// 好的做法 - 遵循约定
// 后端返回
{ "posts": [{ "id": 1, "title": "..." }] }
// 前端自动处理
10.2 合理分层 #
javascript
// application.js - 全局配置
export default class ApplicationAdapter extends RESTAdapter {
namespace = 'api/v1';
}
// post.js - 模型特定配置
export default class PostAdapter extends ApplicationAdapter {
// 只覆盖需要的方法
}
10.3 错误处理 #
javascript
export default class ApplicationAdapter extends RESTAdapter {
handleResponse(status, headers, payload) {
if (status >= 400) {
console.error('API Error:', payload);
}
return super.handleResponse(...arguments);
}
}
十一、总结 #
适配器与序列化器要点:
| 组件 | 职责 |
|---|---|
| Adapter | 处理API请求 |
| Serializer | 处理数据格式 |
| RESTAdapter | RESTful API |
| JSONAPIAdapter | JSON API规范 |
理解适配器和序列化器是对接各种后端API的关键。
最后更新:2026-03-28