antd request 通过jsessionid传参数_Umi-request源码阅读

8ebc5ee754c4440d2975b8469699a496.png

最近参照antd-pro脚手架进行开发,因此接触到了umi-request。

umijs/umi-request​github.com
43863863c4599980dc358b33a56d736f.png

umi-request对fetch进行了封装,简化了api的使用,结合了fetch和axios的特点,具体可参照umi-request的readme介绍。

文件结构

核心文件夹为src文件夹,内含

  • lib文件夹为修改后的fetch.js;
  • defaultInterceptor.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);charset=UTF-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;charset=UTF-8',...options.headers,};options.body = JSON.stringify(data);} else if (requestType === 'form') {options.headers = {Accept: 'application/json','Content-Type': 'application/x-www-form-urlencoded;charset=UTF-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方式对url,option进行处理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方式对url,option进行处理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)处理gbk(readerGBK)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.js

option里支持的参数有:(较fetch增加的)

* @param {string} requestType post类型, 用来简化写content-Type, 默认json
* @param {*} data post数据
* @param {object} params query参数
* @param {string} responseType 服务端返回的数据类型, 用来解析数据, 默认json
* @param {boolean} useCache 是否使用缓存,只有get时有效, 默认关闭, 启用后如果命中缓存, response中有useCache=true. 另: 内存缓存, 刷新就没。一个简单的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 字符集, 默认utf8

extend里支持的参数有:

* @param {number} maxCache 最大缓存数
* @param {string} prefix url前缀
* @param {function} errorHandler 统一错误处理方法
* @param {object} headers 统一的headers

method包括:'get', 'post', 'delete', 'put', 'patch','rpc'。其中,'get', 'post', 'delete', 'put', 'patch'属于REST风格,是http协议的一种直接应用,默认基于json作为传输格式,使用简单,学习成本低效率高。RPC是指远程过程调用直观说法就是A通过网络调用B的过程方法,是分布式系统中常见的方法。

  • GET操作是安全且等幂的。所谓安全是指不管进行多少次操作,资源的状态都不会改变。
  • PUT,DELETE操作是幂等的。所谓幂等是指不管进行多少次操作,结果都一样。比如我用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-pro,dva数据流处理方式。

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 概念 | DvaJS​dvajs.com
3e5274a9e3618419c1a4c51cc6db8028.png
antd-pro项目​github.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/301346.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

随手能做194个实验,不呆板,轻松撬动大智慧

▲数据汪特别推荐点击上图进入玩酷屋玩具和学习看似是两个对立的东西,孩子天性爱玩,家长却希望孩子能多学习。不一定非要啃课本才能汲取知识,有时候,在轻松有趣的游戏中也能学到课堂上学不到的知识。让学习变得有趣、高效——给孩…

面向.NET开发人员的Dapr——绑定

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理面向.NET开发人员的Dapr——发布和订阅Th…

每日一笑 | 为什么Python比Java更受欢迎?

全世界只有3.14 % 的人关注了数据与算法之美(图片来源于网络,侵权删)

传递集合对象_面试必备——Java集合框架

Java集合框架面试题常见集合集合可以看作是一种容器,用来存储对象信息。数组和集合的区别:(1)数组长度不可变化而且无法保存具有映射关系的数据;集合类用于保存数量不确定的数据,以及保存具有映射关系的数据。(2)数组元素既可以是…

面向.NET开发人员的Dapr——服务调用

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理The Dapr service invocation building b…

快速入门深度学习,其实并不难!

深度学习的概念源于人工神经网络的研究,而深度学习的过程就是使用多个处理层对数据进行高层抽象,得到多重非线性变换函数的过程。虽然深度学习的概念看似高大上,让人有种莫名的距离感,实际上它在日常生活中随处可见,比…

插入始终是1_插入式电磁流量计特点与应用

一、本文引言首先,插入式电磁流量计是电磁流量计中的一种安装类型,电磁流量计的安装方式有多种,包括法兰式安装,螺纹安装,卡箍式安装,插入式安装等四种类型,法兰式安装是常见普遍都在使用的安装…

面向.NET开发人员的Dapr——发布和订阅

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序面向.NET开发人员的Dapr——状态管理面向.NET开发人员的Dapr——服务调用The …

bootstrap的表单验证 vue_第45天:Web表单

在了解了 Flask Bootstrap 基本框架之后,我们来了解一下 Flask 框架的 表单( form ),以帮助我们创建交互式的 Web 应用,最后会有个提交个人信息的例子。Flask-WTF 是 Flask 框架的一个扩展,用来做表单的交互,是对 WTFo…

超火GitHub项目!一夜获得5000星,竟是微软开源的计算器

全世界只有3.14 % 的人关注了数据与算法之美昨天,微软在 MIT License 下开源了一个 GitHub 项目——Windows 计算器,短短 24 小时内就获得了接近 5000 颗星,并引发大量讨论。这个项目爆红的速度可能连微软自己都始料未及。科技媒体 The Verge…

面向.NET开发人员的Dapr——目录

今天上午的主题就是Dapr,别错过了哦。本系列部分翻译自 .NET团队编写的Dapr for .NET Developers | Microsoft Docs(https://docs.microsoft.com/en-us/dotnet/architecture/dapr-for-net-developers/),大部内容复制于面向 .NET 开发人员的 Dapr | Micro…

人体工学腰垫,保腰神器,改善久坐腰酸背痛

▲数据汪特别推荐点击上图进入玩酷屋之前小木推荐“德国MINICUTE人体工学腰垫”受到了大家一致的好评和争相购买。小木为什么推荐这一款原因是:据统计,我国腰椎病患者已经突破2亿人。30~40岁人群中,患有颈腰椎病的占比59.1%!而且有…

和flag_不怕立Flag,就怕没有Flag

转眼间就已经是今年的最后一天了,回想去年给自己立下的flag,看着自己手机上的减肥计划和体测成绩上的超重,我陷入了沉思,到底是哪个环节出问题了呢?我慢悠悠地在宿舍里转圈,看见舍友递过来的薯片&#xff0…

面向.NET开发人员的Dapr——状态管理

目录:面向.NET开发人员的Dapr——前言面向.NET开发人员的Dapr——分布式世界面向.NET开发人员的Dapr——俯瞰Dapr面向.NET开发人员的Dapr——入门面向.NET开发人员的Dapr——参考应用程序The Dapr state management building blockDapr 状态管理构建基块Distributed…

为什么网易云音乐总能知道你喜欢听什么歌?背后的原理竟然如此简单!

全世界只有3.14 % 的人关注了数据与算法之美无处不在的数据分析谷歌的数据分析可以预测一个地区即将爆发的流感,从而进行针对性的预防;淘宝可以根据你浏览和消费的数据进行分析,为你精准推荐商品;口碑极好的网易云音乐&#xff0c…

判断小数是否相等_五年级上册数学综合练习题(填空、判断、选择、文字题),覆盖全册知识点!...

五年级上册判断题练习1、整数乘法的运算定律对小数乘法同样适用 .( )2、小数不一定比整数小。( )3、两个小数相除 ,如果商大于被除数 ,那么除数一定大于.( )4、两个数相除 ,商是 .8,如果除数缩小10倍,被除数不变 ,商是 0.8。( )5、小数乘法的意义与整数乘法的意义完全相…

次优二叉树

在有序序列的查找中,如果各个元素的查找概率都是一样的,那么二分查找是最快的查找算法,但是如果查找元素的查找概率是不一样的,那么用二分查找就不一定是最快的查找方法了,可以通过计算ASL来得知。所以基于这种查找元素…

每日一笑 | 老板,黑凤梨真的能吃吗?

全世界只有3.14 % 的人关注了数据与算法之美(图片来源于网络,侵权删)

crontab执行shell脚本日志中出现乱码

使用nutch开源的搜索引擎抓取网页信息,通过计划任务crontab执行shell脚本,并将启动信息记录进日志文件,但是每次都会出现乱码,如图所示经过反复测试,排除了java程序问题,文件权限问题,系统字符集…

学好数学建模,走哪买菜都不怕!

随着新学期的开始,数学建模的热度也在逐步回温(据说大家开始了数学建模知识储备),常有小伙伴私聊小天咨询关于数学建模的事情。大大小小的数学建模比赛如火如荼开展着,而如何快速掌握数学建模,做好充足的准…