使用 API
和后端交互是非常常见的,Godzilla UI 封装网络请求的 API,所有 ajax 都需要通过 API
这个类来发起调用,api 的风格采用 restful api(参考:http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html)
一、 使用 get/post/delete/put 方法
1. 初始化 API 实例
// 引入API组件
import { API } from "@gza/quantex-utils";
class Store {
// this.api = new API('auth'); // 不要这样初始化
constructor() {
// 初始化api实例,传入服务名称(一个服务名称可以映射到后端的真实服务,具体配置在BFF层)
// 注意,这个初始化一般要放在构造函数中(或者具体方法中),不能作为实例属性
this.api = new API("auth");
}
}
2. 调用后端服务
class Store {
constructor() {
this.api = new API("auth");
}
// GET方法,一般用来获取信息,不涉及新增、修改、删除数据的操作
fetchUserInfo = userCode => {
let params = {
code: userCode
};
// 其中url中的{code}会被params中的code属性值替换,并发起get请求
// 返回一个promise
return this.api.get("api/v2/users/{code}", params);
};
// POST方法,一般用来提交表单数据
login = params => {
// 所有的post请求,都会默认带loading效果,如果不想要,可以通过第三个参数关闭
// return this.api.post('api/v2/users/login', params, {loading: false});
return this.api.post("api/v2/users/login", params);
};
// PUT方法,一般用来修改表单数据
updateUserInfo = () => {
return this.api.put("api/v2/users/{code}", params);
};
}
一般与服务端的交互都会写在 Store 这一层,集中管理,强烈不建议放在组件层(组件只负责渲染,不负责处理业务逻辑)
3. 处理返回结果
import Store from "./Store";
class LoginComponent extends Component {
store = new Store();
login = () => {
let params = {};
this.store.login(params).then(res => {
if (res.code === 200) {
// 200是正常的返回码
// 处理登录成功逻辑
} else {
// 处理失败逻辑,失败了一般需要弹出框提示,一般调用组件Alert提供的静态方法error,传入整一个response对象
Alert.error(res);
}
});
};
}
二、 使用 list/query 方法
除了提供 get/post/delete/put 方法外,还提供了另外两个专用方法,list 和 query 方法,这是结合通用查询的方法,会跟服务端约定好数据格式
class Store {
constructor() {
this.api = new API("auth");
}
// list,查询满足条件的所有数据
getAllUser = params => {
this.api.list("/api/v2/users/list", params);
};
// query是分页接口
queryUser = params => {
this.api.query("/api/v2/users/query", params);
};
}
三、 使用 easyApi 方法
使用 easyApi 相关方法,默认由框架弹窗显示错误,并且成功的返回值由原来的 res 改为 res.data,开发人员仅需要关注成功返回的执行逻辑。
1. easyPost 的使用
class Store {
constructor() {
this.api = new Api("auth");
}
addRole = formData => {
return this.api.easyPost("/api/v2/roles", formData);
};
}
class RoleComponent {
// 采用async/await
handleAddRole = async () => {
let res = await this.store.addRole(formData);
// 关闭弹出框和刷新,如果上一步报错了,就不会执行如下两个方法了
this.search();
this.modal.close();
};
}
2. 其他API
easyGet
easyPost
easyPut
easyDelete
easyList
参数传递同之前 Api
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
url | 请求地址 | string | - |
params | 请求参数 | object | - |
config | 配置 | object | - |
处理正确返回,入参为 res.data
resolve(res.data)
四、 使用 adaptor
在某些场景下, 需要在请求发起时做一些判断, 从而拦截某些请求或者对返回数据做一些处理, 比如字典缓存.
需要注意的是, setAdaptor
之后, 所有 new API()
请求都会走 adaptor
函数, adaptor
只允许设置一个函数, 重复 setAdaptor
会覆盖之前的函数.
import { API } from "@gaz/quantex-utils";
const runConfQTUtil = () => {
const dictCache = new Map();
// request: Request对象, 包含 { method, url, headers, body } 等参数
// params: api实例 get/post/put/delete 请求发起时传入的params(第二个参数)
// adaptor 函数必须返回 Promise
API.setAdaptor(({ request, params }) => {
// 如果请求地址包含 /Roles/dict (用户角色字典请求) , 会去字典缓存(dictCache) 中查找
if (
request.url.includes("/Roles/dict") &&
params.dictKey &&
dictCache.get(params.dictKey)
) {
// 获取到缓存数据时, 直接返回缓存的值
// 限定 { ok , json } 格式, 这个对象会返回给 api 实例中, 进行后续的数据处理, 比如 logger 等
const cacheData = dictCache.get(params.dictKey);
return Promise.resolve({
ok: true,
json: () => cacheData
});
}
// 如果 缓存中 没有找到, 就发起请求
// 注意: adaptor函数中不允许使用 new API(), 需要用原生的 fetch
return fetch(request).then(async res => {
if (res.ok) {
// 在数据返回成功时, 可以对数据作 自定义的处理, 比如缓存字典
// 注意: res.json 方法只允许使用一次, 所以在 .json 之前必须使用 clone(), 因为 API 实例内部也需要 json() 处理
await res
.clone()
.json()
.then(result => {
if (
request.url.includes("/Roles/dict") &&
params.dictKey &&
result.code === 200
) {
// 对返回的字典数据进行缓存, 以 dictKey 作为 Map's key
dictCache.set(params.dictKey, result);
}
});
}
// API 实例内部仍然会对 res 作处理, 所以需要返回 res(Response对象)
return res;
});
});
};
export default runConfQTUtil;
// other file , ex: app/index.js
import runConfQTUtil from "./runConfQTUtil";
runConfQTUtil(); // 执行方法从而设置一个API全局的 adaptor
// api 请求中也允许使用 adaptor 来覆盖全局的 adaptor
import React from 'react';
import { API } from '@gza/quantex-utils';
class Comp extends React.component {
api = new API('auth');
component() {
this.doRequest();
}
doRequest() {
this.api.get('/Roles/dict', { dictKey: 'id_name' }, { adaptor: ({ request, params }) => {
return Promise.resolve({
ok: true,
json: () => {
code: 200,
msg: 'Data from adaptor',
data: {}
}
})
} }).then(res => {
// 打印 res: { code: 200, msg: 'Data from adaptor', data: {} }
console.log(res);
});
}
}
五、 config 具体属性
所有的 api 方法都会提供一个 config 配置参数,具体定义如下
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
loading | 数据未加载到时是否展示 Spin 组件 | boolean | true(get 请求默认为 false) |
timeout | 设置超时: timeout == -1 时,接口永不超时。timeout != -1 按照设置的时间 | string |number | - |
multipart | 设置为 true 时为富文本上传 | boolean | - |
transformResponse | 在传递给 then/catch 前,允许修改响应数据 | (res) => res | - |
adaptor | 可以拦截请求, 根据需求作自定义操作 | ({request: Request, params: any}) => Promise<any> | - |