番禺网站开发报价,房产抵押贷款,网站怎么产品做推广,建设通网站上的业绩能否有用最近参照antd-pro脚手架进行开发#xff0c;因此接触到了umi-request。umijs/umi-requestgithub.comumi-request对fetch进行了封装#xff0c;简化了api的使用#xff0c;结合了fetch和axios的特点#xff0c;具体可参照umi-request的readme介绍。文件结构核心文件夹为sr…最近参照antd-pro脚手架进行开发因此接触到了umi-request。umijs/umi-requestgithub.comumi-request对fetch进行了封装简化了api的使用结合了fetch和axios的特点具体可参照umi-request的readme介绍。文件结构核心文件夹为src文件夹内含lib文件夹为修改后的fetch.jsdefaultInterceptor.js用于注册拦截器index.js是外部调用的入口文件request.js提供request实例utils.js定义了处理缓存、请求异常、响应异常、处理gbk、json语法分析的功能类wrapped-fetch.js从文件名可以看出来为封装fetch实现更新版数据的传递wrapped-rpc.js从文件可以看出封装了rpc通信待实现代码分析原始代码里含的注释基本上已经很全面了 defaultInterceptor.js功能有数据提交方式简化针对常见两种数据传输格式补全文件头“Accept: application/json, Content-Type: application/json(或者x-www-form-urlencoded);charsetUTF-8”url 参数自动序列化主要是对传入的url和option进行格式化的处理以便后续的fetch.js进行处理。关于option的新增参数参见request.js。export default (url, originOptions {}) {const options { ...originOptions };// 默认get, 兼容method大小写let method options.method || get;method method.toLowerCase();if (method post || method put || method patch || method delete) {// requestType 简写默认值为 jsonconst { requestType json, data } options;// 数据使用类axios的新字段data, 避免引用后影响旧代码, 如将body stringify多次if (data) {const dataType Object.prototype.toString.call(data);if (dataType [object Object] || dataType [object Array]) {if (requestType json) {options.headers {Accept: application/json,Content-Type: application/json;charsetUTF-8,...options.headers,};options.body JSON.stringify(data);} else if (requestType form) {options.headers {Accept: application/json,Content-Type: application/x-www-form-urlencoded;charsetUTF-8,...options.headers,};options.body stringify(data);}} else {// 其他 requestType 自定义headeroptions.headers {Accept: application/json,...options.headers,};options.body data;}}}// 支持类似axios 参数自动拼装, 其他method也可用, 不冲突.if (options.params Object.keys(options.params).length 0) {const str url.indexOf(?) ! -1 ? : ?;url ${url}${str}${stringify(options.params)};}return {url,options,};
};
fetch.js主要是定义了request和response拦截器。拦截器在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前进行拦截然后在之前或之后加入某些操作。可以对http请求进行批量处理。import whatwg-fetch;
import defaultInterceptor from ../defaultInterceptor;const requestInterceptors [];
export const responseInterceptors [];function fetch(url, options {}) {if (typeof url ! string) throw new Error(url MUST be a string);// 执行 request 的拦截器使用defaultInterceptor依据handler方式对urloption进行处理requestInterceptors.concat([defaultInterceptor]).forEach(handler {const ret handler(url, options);url ret.url || url;options ret.options || options;});// 将 method 改为大写options.method options.method ? options.method.toUpperCase() : GET;// 请求数据let response window.fetch(url, options);// 执行 response 的拦截器依据handler方式对urloption进行处理responseInterceptors.forEach(handler {response response.then(res handler(res, options));});return response;
}
// 支持拦截器参考 axios 库的写法: https://github.com/axios/axios#interceptors
fetch.interceptors {request: {use: handler {requestInterceptors.push(handler);},},response: {use: handler {responseInterceptors.push(handler);},},
};export default fetch;
utils.js定义了处理缓存Mapcache、请求异常RequestError、响应异常ResponseError、处理gbkreaderGBK、json语法转换safeJsonParse的功能类处理缓存options.maxCache是option的extend参数可以在封装umi-request时指定如antd-pro. const request extend({maxCache: 50,});实现变量的获取、变量删除、缓存清空、value存入缓存其中key json化后为关键字export class MapCache {constructor(options) {this.cache new Map();this.timer {};this.maxCache options.maxCache || 0;}get(key) {return this.cache.get(JSON.stringify(key));}set(key, value, ttl 60000) {// 如果超过最大缓存数, 删除头部的第一个缓存.if (this.maxCache 0 this.cache.size this.maxCache) {const deleteKey [...this.cache.keys()][0];this.cache.delete(deleteKey);if (this.timer[deleteKey]) {clearTimeout(this.timer[deleteKey]);}}const cacheKey JSON.stringify(key);this.cache.set(cacheKey, value);if (ttl 0) {this.timer[cacheKey] setTimeout(() {this.cache.delete(cacheKey);delete this.timer[cacheKey];}, ttl);}}delete(key) {const cacheKey JSON.stringify(key);delete this.timer[cacheKey];return this.cache.delete(cacheKey);}clear() {this.timer {};return this.cache.clear();}
}
在wrapped-fetch.js中调用_wrappedCache(instance, useCache) {if (useCache) {const { params, ttl } this.options;//url请求参数params和缓存时长ttl为option传入参数return instance.then(response {// 只缓存状态码为 200的数据if (response.status 200) {const copy response.clone();copy.useCache true;//this.cache为通过request.js调用传入的参数const mapCache new MapCache(initOptions);this.cache.set({ url: this.url, params }, copy, ttl);}return response;});} else {return instance;}
}
请求异常export class RequestError extends Error {constructor(text) {super(text);this.name RequestError;}
}
在wrapped-fetch.js中如此使用用于输出超时信息其中timeout为option传入参数。需要注意的是超时后客户端虽然返回超时, 但api请求不会断开, 写操作慎用。return Promise.race([new Promise((_, reject) setTimeout(() reject(new RequestError(timeout of ${timeout}ms exceeded)), timeout)),instance,
]);
响应异常export class ResponseError extends Error {constructor(response, text, data) {super(text || response.statusText);this.name ResponseError;this.data data;this.response response;}
}
在wrapped-fetch.js中调用示例当异常时抛出异常catch (e) {throw new ResponseError(response, e.message);}
支持gbk并进行json格式的转换判断返回的数据时表示gbk编码的如果是将数据以gbk格式读入然后再利用safeJsonParse转化为json格式export function readerGBK(file) {return new Promise((resolve, reject) {const reader new FileReader();reader.onload () {resolve(reader.result);};reader.onerror reject;reader.readAsText(file, GBK); // setup GBK decoding});
}export function safeJsonParse(data) {try {return JSON.parse(data);} catch (e) {} // eslint-disable-linereturn data;
}
wrappedfetch.js核心部分基于fetch进行数据交互一个 Promise 就是一个对象它代表了一个异步操作的最终完成或者失败通过 .then() 形式添加的回调函数连续执行两个或者多个异步操作链式调用多个then一个catch接收异常。一个 Promise有以下几种状态:pending: 初始状态既不是成功也不是失败状态。fulfilled: 意味着操作成功完成。rejected: 意味着操作失败。当其中任一种情况出现时Promise 对象的 then 方法绑定的处理方法handlers 就会被调用then方法包含两个参数onfulfilled 和 onrejected它们都是 Function 类型。当Promise状态为fulfilled时调用 then 的 onfulfilled 方法当Promise状态为rejected时调用 then 的 onrejected 方法。Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象。但如果这个值是个thenable即带有then方法返回的promise会“跟随”这个thenable的对象采用它的最终状态指resolved/rejected/pending/settled如果传入的value本身就是promise对象则该对象作为Promise.resolve方法的返回值返回否则以该值为成功状态返回promise对象。Promise.reject(reason)返回一个状态为失败的Promise对象并将给定的失败信息传递给对应的处理方法export default class WrappedFetch {constructor(url, options, cache) {this.cache cache;//cache是MapCache的实例this.url url;this.options options;this._addfix();return this._doFetch();}_doFetch() {//如果使用cache只有在option的method是get且useCache为true时使用cacheif (useCache) {let response this.cache.get({url: this.url,params: this.options.params,});if (response) {response response.clone();let instance Promise.resolve(response);responseInterceptors.forEach(handler {instance instance.then(res handler(res, this.options));});return this._parseResponse(instance, true);}}let instance fetch(this.url, this.options);// 处理超时instance this._wrappedTimeout(instance);// 处理缓存 1.只有get 2.同时参数cache为true 才缓存instance this._wrappedCache(instance, useCache);// 返回解析好的数据return this._parseResponse(instance);}//对url进行自动序列化添加前缀和后缀_addfix() {} //调用RequestError在超时后输出信息_wrappedTimeout(instance) {}//调用MapCache在用户使用usecache时将response复制到擦车中并设置缓存时间_wrappedCache(instance, useCache) {}//处理返回类型, 并解析数据如果编码方式时gbk则利用readerGBK和safeJsonParse进行转化在读取response时加入responseError处理_parseResponse(instance, useCache false) {}//处理错误_handleError({ reject, resolve }, error) {}
}
request.jsoption里支持的参数有较fetch增加的* param {string} requestType post类型, 用来简化写content-Type, 默认json * param {*} data post数据 * param {object} params query参数 * param {string} responseType 服务端返回的数据类型, 用来解析数据, 默认json * param {boolean} useCache 是否使用缓存,只有get时有效, 默认关闭, 启用后如果命中缓存, response中有useCachetrue. 另: 内存缓存, 刷新就没。一个简单的Map cache, 提供session local map三种前端cache方式. * param {number} ttl 缓存生命周期, 默认60秒, 单位毫秒 * param {number} timeout 超时时长, 默认未设, 单位毫秒 * param {boolean} getResponse 是否获取response源 * param {function} errorHandler 错误处理 * param {string} prefix 前缀 * param {string} suffix 后缀 * param {string} charset 字符集, 默认utf8extend里支持的参数有* param {number} maxCache 最大缓存数 * param {string} prefix url前缀 * param {function} errorHandler 统一错误处理方法 * param {object} headers 统一的headersmethod包括get, post, delete, put, patch,rpc。其中get, post, delete, put, patch属于REST风格是http协议的一种直接应用默认基于json作为传输格式使用简单学习成本低效率高。RPC是指远程过程调用直观说法就是A通过网络调用B的过程方法是分布式系统中常见的方法。GET操作是安全且等幂的。所谓安全是指不管进行多少次操作资源的状态都不会改变。PUTDELETE操作是幂等的。所谓幂等是指不管进行多少次操作结果都一样。比如我用PUT修改一篇文章然后在做同样的操作每次操作后的结果并没有不同DELETE也是一样。POST操作既不是安全的也不是幂等的比如常见的POST重复加载问题当我们多次发出同样的POST请求后其结果是创建出了若干的资源。安全和幂等的意义在于当操作没有达到预期的目标时我们可以不停的重试而不会对资源产生副作用。从这个意义上说POST操作需要谨慎。还有一点需要注意的就是创建操作可以使用POST也可以使用PUT区别在于POST 是作用在一个集合资源之上的/uri而PUT操作是作用在一个具体资源之上的/uri/xxx再通俗点说如果URL可以在客户端确定那么就使用PUT如果是在服务端确定那么就使用POST比如说很多资源使用数据库自增主键作为标识信息而创建的资源的标识信息到底是什么只能由服务端提供这个时候就必须使用POST。PATCH新引入的是对PUT方法的补充用来对已知资源进行局部更新。在没有patch之前我们都是用put进行更新操作这时候我们的接口中通常会有一个逻辑规则如如果对象的的一个字符属性为NULL那么就是不更新该属性字段值如果对象的字符属性是“”那么就更新该属性字段的值通过这种方式来避免全部覆盖的操作。现在有了patch就解决了这种判断在put接口中不管属性是不是null都进行更新在patch接口中就对非null的进行更新import fetch from ./lib/fetch;
import { MapCache } from ./utils;
import WrappedFetch from ./wrapped-fetch;
import WrappedRpc from ./wrapped-rpc;/*** 获取request实例 调用参数可以覆盖初始化的参数. 用于一些情况的特殊处理.* param {*} initOptions 初始化参数*/
const request (initOptions {}) {const mapCache new MapCache(initOptions);const instance (input, options {}) {options.headers { ...initOptions.headers, ...options.headers };options.params { ...initOptions.params, ...options.params };options { ...initOptions, ...options };const method options.method || get;options.method method.toLowerCase();if (method rpc) {// call rpcreturn new WrappedRpc(input, options, mapCache);} else {return new WrappedFetch(input, options, mapCache);}};// 增加语法糖如: request.get request.post// 对应着对资源的查看创建删除创建或更新部分更新rpcconst methods [get, post, delete, put, patch,rpc,];methods.forEach(method {instance[method] (input, options) instance(input, { ...options, method });});// 给request 也增加一个interceptors引用;instance.interceptors fetch.interceptors;return instance;
};/*** extend 方法参考了ky, 让用户可以定制配置.* initOpions 初始化参数* param {number} maxCache 最大缓存数* param {string} prefix url前缀* param {function} errorHandler 统一错误处理方法* param {object} headers 统一的headers*/
export const extend initOptions request(initOptions);
export default request();
使用示例通过extend进行简单封装参照antd-prodva数据流处理方式。import { extend } from umi-request;
......
const request extend({errorHandler, // 默认错误处理函数credentials: include, // 默认请求是否带上cookie// useCache: false,// maxCache: 50,
});
export default request;
后续在api.js中的使用import request from /utils/request;
//get请求
export async function function1({ classId }) {return request(/api/classes/${classId}/teachers/);}
//post请求
export async function function2({ classId }) {return request.post(/api/classes/${classId}/teachers/, { data: {foo: bar}});}
// 使用缓存
export async function function3({ classId }) {return request(/api/classes/${classId}/teachers/, { useCache: true, ttl: 10000 });}
每一个页面对应的model中可以通过下面的方式进行使用* fetchBasic({ classId }, { call, put }) {//利用call方式调用api.js中声明的function1 const response yield call(fuction1, classId);//对接收到的response进行处理yield put({type: saveResponse,//reducers中对应的处理方法payload: response});
},
特点url 参数自动序列化post 数据提交方式简化response 返回处理简化api 超时支持api 请求缓存支持支持处理 gbk类 axios 的 request 和 response 拦截器(interceptors)支持更多内容查看umi-request项目、dva文档、antd-pro项目umi-request项目github.comDva 概念 | DvaJSdvajs.comantd-pro项目github.com