简易版网络请求库
这个请求库目前还有很多问题,参考了axios写在这,主要是帮助没什么经验的兄得姐妹们参考下,大佬们轻喷使用了Fetch和XHR源码在https://github.com/Janenil/http-sc碰到的一些问题血燥前面:不同contentType对应不同数据结构application/x-www-form-urlencoded: key=value&ke...
这个请求库目前还有很多问题,参考了axios
写在这,主要是帮助没什么经验的兄得姐妹们参考下,大佬们轻喷
使用了Fetch和XHR
源码在 https://github.com/Janenil/http-sc
碰到的一些问题写在前面:
不同contentType对应不同数据结构
- application/x-www-form-urlencoded: key=value&key=value
- application/json: JSON.stringfy({"key", "value"})
- multipart/form-data: new FormData(分隔符、参数描述信息等内容的构造体)
- text/html:浏览器在获取到这种文件时会自动调用html的解析器对文件进行相应的处理
- text/plain:
自测时碰到的一个问题
fetch:FormData上传时,期望设置,'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',或者设置:'Content-Type': 'multipart/form-data' 在查看传参的时候会出现类似这样的情况:
上网查了资料以后, 原来 post 请求上传文件的时候是不需要自己设置 Content-Type,会自动给你添加一个 boundary ,用来分割消息主体中的每个字段,如果这个时候自己设置了 Content-Type, 服务器就不知道怎么分割各个字段,因此就会报错。
请求的实体数据是经过了 multipart/form-data 算法编码同时以 utf-8 作为显示字符编码。mime-type(Content-Type) 是”multipart/form-data;” 和 运行 multipart/form-data 算法生成的”boundary=xxx” 串联在一起的字符串。
所以使用 FormData 就自动给我们规定了这些内容,不需要我们自己再去指定了。
所以在封装fetch的时候:
因为需要根据不同的数据类型设置不同的 Content-Type,可是上传文件的时候又不能设置 Content-Type, 所以需要谨慎地封装 fetch ,一不小心可能就会造成上面的问题。
在查这个问题的时候,又看到一个博客,也是自测时碰到的问题
测试接口时:发起post请求,控制台查看的时候时options:403
很多时候发送一个post请求,是先发送一个option请求,然后再发送post请求。
OPTIONS请求方法的主要用途有两个:
- 获取服务器支持的HTTP请求方法;
- 用来检查服务器的性能。
例如:AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全。
跨域请求(CORS)
options请求也称为预检请求(preflight request)。但不是所有的cors都会发生预检请求,与预检请求相对应的是简单请求(simple request)。如果是简单请求,那么请求应该符合以下条件:
- 请求类型是GET/HEAD/POST之一
- 请求头除了用户代理(浏览器)自带的(Connection, User-Agent)和Fetch spec as a “forbidden header name之外,用户只允许设置以下请求头:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
Last-Event-ID
DPR
Downlink
Save-Data
Viewport-Width
Width
- Content-Type只能是以下类型:
application/x-www-form-urlencoded
multipart/form-data
text/plain
如果不满足上面三点任何一点,那么请求都是非简单请求。对于这样的CORS请求会先发出一个预检请求,如果后端响应头(后端会返回一些字段,比如Access-Control-Allow-Method)允许这个非简单请求,那么将会发起我们实际创建的请求。
经常可能会出现的一个问题是,我们在请求头设置了Content-Type: application/json,那么这个请求就变成了非简单请求。所以会首先发起预检请求。
- 熟悉xhr、fetch的api
- 熟悉xhr、fetch的参数
xhr
- method(String): 请求所使用的HTTP方法; 例如 "GET", "POST", "PUT", "DELETE"等. 如果下个参数是非HTTP(S)的URL,则忽略该参数
- url(String): 请求访问的URL地址
- async(Boolean): 配置是否为异步执行,默认值为true;
- ···
fetch
- method(String): HTTP请求方法,默认为GET
- body(String): HTTP的请求参数
- headers(Object): HTTP的请求头,默认为{}
- credentials(String): 默认为omit,忽略的意思,也就是不带cookie;还有两个参数,same-origin,意思就是同源请求带cookie;include,表示无论跨域还是同源请求都会带cookie
- mode: 请求模式
- headers: 请求头
- ···
- 处理请求参数
- 根据不同请求处理参数
- 统一处理错误
xhr
- onabort
- onerror
- ontimeout
fetch
- response.ok
选择优先fetch再使用xhr
Fetch API 和 XHR 做的事情很相似,大部分情况我们通过 XHR 就已经能够完成开发任务了。
但是看对比
// 用 XHR 发起一个GET请求
var xhr = new XHMHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function() {
console.log(xhr.response);
};
xhr.onerror = function() {
console.log('something wrong');
};
xhr.send();
// 用 Fetch 完成同样的请求
fetch(url).then(function(response) {
return response.json();
}).then(function(jsonData) {
console.log(jsonData);
}).catch(function() {
console.log('something wrong');
});
Fetch 是一个现代的概念, 等同于 XMLHttpRequest。它提供了许多与XMLHttpRequest相同的功能,但被设计成更具可扩展性和高效性。
Fetch 的核心在于对 HTTP 接口的抽象,包括 Request,Response,Headers,Body,以及用于初始化异步请求的 global fetch。得益于 JavaScript 实现的这些抽象好的 HTTP 模块,其他接口能够很方便的使用这些功能。 Service Workers 是一个利用了 Fetch 实现的接口的例子。 除此之外,Fetch 还利用到了请求的异步特性——它是基于 Promise 的。
目的
- 支持网络请求
- 简洁、易用的api
- 高度可拓展
简介
- 提供统一promiseAPI
- 支持浏览器环境
- 自动转换json数据
- 统一处理请求正常、异常(超时、请求错误、服务器)情况
支持网络请求
创建Request对象 -> 构建Request -> 发送Request -> 解析Response -> 监听回调,返回解析值
xhr介绍
- 可以发送跨域请求,在服务端允许的情况下;
- 支持发送和接收二进制数据;
- 新增formData对象,支持发送表单数据;
- 发送和获取数据时,可以获取进度信息;
- 可以设置请求的超时时间;
fetch介绍
- 简洁、易用、声明式
- 基于 Promise
过程
xhr
- 实例化XMLHttpRequest对象获得一个实例
- 通过实例open一个请求,设置发送类型和接口以及同异步
- 如有需要配置报文,以及各种事件(success,error,timeout等)
- 调用实例的send方法,发送http/https的请求
- 服务器回调,客户端接收,并做响应处理
let xmlHttp = new XMLHttpRequest();
xmlHttp.open(method, url);
xmlHttp.send('');
xmlHttp.onreadystatechange = () => {
// dosomething
}
fetch
因为fetch本身就是封装好的api,在这里只需要考虑:
- 捕捉异常
- 处理错误
- 统一数据返回格式
- 超时处理
- 取消(未完成)
fetch(params.url, Object.assign({method: params.method}, {params}))
.then(res => {
if (timeout) throw new Error('timeout!');
checkStatus(res, reject);
})
.then(res => parseJSON(res))
.then(data => {
let json = transformResponse(data, params.isShowError || params.isShowSystemError, params.errTipTime)
json.success ? resolve(json.json) : reject(json.json);
})
.catch(err =>{
errFetch(err, reject);
})
兼容情况
配置参数复用
- 请求接口后端定义一些公共参数,每次都要传输
- 为了一些业务,每次在http-header中设置 一些参数值
- 统一设置接口超时时间
- 统一错误、超时处理函数
- 发送请求前、获得请求参数后对参数处理(未完成)
- ....
// 配置单次请求配置
let http = new Http()
http.request("/test",{hh:5},{
method:"post",
timeout:5000 //超时设置为5s
})
后期添加实例配置,用于当前实例发起的所有请求。 单次配置和实例配置冲突,则会优先使用单次请求配置
export default {
// 接口请求地址
url: '',
// 接口请求方式
method: 'get',
// fetch 请求模式
mode: 'no-cors',
// fetch 是否发送cookie
credentials: 'same-origin',
// fetch 缓存
cache: 'no-cache',
// fetch 重定向
redirect: 'follow',
// fetch 请求参数
body: {},
// 统一追加前缀
baseUrl: '',
// 请求接口中公共数据
pulbicParams:{},
// 接口传输数据
data: {},
// 请求头
headers: {
},
// 请求接口中公共数据
publicData: {},
// 接口请求超时时间
timeout: 20000,
// 跨域接口请求中是否发送跨域凭证
withCredentials: false, // default
// 希望获得接口响应数据类型
responseType: 'json', // default
// 是否异步请求
async: true,
// 接口请求中可以发送的数据类型
// contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
contentType: 'application/json; charset=UTF-8',
// 接口传输数据的补充
params: {},
// 是否传值为json
isJson: true,
// 是否展示错误
isShowError: true,
// 是否展示所有错误
isShowSystemError: true,
// 错误提示显示时间
errTipTime: 2500,
// serviceDiscover default config
serviceDiscover: null,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
};
例子
- Http
import Http from 'http-souche';
let http = new Http()
http({
method: 'post',
url: '/user/12345',
data: {
ID: 1111
}
});
- get
import Http from 'http-souche';
let http = new Http()
http.get('/user?ID=11111')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
http.get('/user', {
ID: 1111
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
// 异步请求
async function getUser() {
try {
const response = await http.get('/user?ID=11111');
console.log(response);
} catch (error) {
console.error(error);
}
}
- post
import Http from 'http-souche';
let http = new Http()
http.post('http://workflow-dev.dasouche-inc.net/v2/workFlowConfig/close.json',
{
workFlowId: 'HvgRO4YGpv',
token: '91563240745175281',
bizCode: 'test'
}, {
isJson: false
})
- 请求二进制
统一处理错误
- xhr
根据 onabort|onerror|ontimeout 判断是否请求异常
- fetch
根据 fetch返回response.ok 判断是否请求异常
统一错误返回格式
{
success: false,
code: `404`,
msg: '未知异常',
err: 'Unknown Exception',
errCN: '未知异常'
}
API
Http.get(url, data, options)
发起 get 请求,url请求地址,data为请求数据,options为请求配置项。
Http.post(url, data, options)
参数意义如get
Http.request(url, data, options)
//GET请求
Http.request("/user/test" null, {method:"get"})
//DELETE 请求
Http.request("/user/test/delete", null, {method:"delete"})
//PUT请求
Http.request("/user/test/register", {name:"test"}, {method:"PUT"})
......
待优化
-
未设置拦截器 interceptors
-
不能中断请求
fetch中有考虑过 AbortController
但是兼容性太差
- 兼容node
amd、cmd、umd写法的不同和如何兼容
- 低版本兼容(ActiveXObject)
建议
- 修改isJson命名
- 增加请求中断
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)