legions-fetch
此库已经过严格的单元测试,请放心使用
Promise based HTTP client for the browser and node.js
what is legions-fetch?
legions-request 是一个支持浏览器及服务端的 http 请求工具库
Features
- 高版本浏览器直接基于 fetch
- 低版本不支持 fetch 时,自动降级兼容 fetch 所有特性
- nodejs 中通过 http 发送请求
- Supports the Promise API
- 支持注入 request,respone,reject 等拦截器
- 超时自动中断请求
Installing
Using npm and yarn :
npm i legions-requset -S
or
yarn add legions-requset -S
Using jsDelivr CDN:
<script src="dist/index.iife.js"></script>
Example
import { get, post } from 'legions-fetch';
get({
url: '/user?ID=12345',
}).then(function (response) {
// handle success
console.log(response);
});
POST Requests
How to perfrom POST requests with legions-fetch
Performing a POST request
import { get, post } from 'legions-fetch';
post({
url: '/user',
data: {
firstName: 'Fred',
lastName: 'Flintstone',
},
}).then(function (response) {
console.log(response);
});
Performing multiple concurrent requests
function getUserAccount() {
return get({
url: '/user/12345',
});
}
function getUserPermissions() {
return get({
url: '/user/12345/permissions',
});
}
Promise.all([getUserAccount(), getUserPermissions()]).then(function (results) {
const acct = results[0];
const perm = results[1];
});
legions-request api
The legions-request API Reference
legionFetch.instance.request(config)
// Send a POST request
legionFetch.instance.request({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone',
},
});
// GET request for remote image in node.js
legionFetch.instance
.request({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
responseType: 'stream',
})
.then(function (response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'));
});
Request method aliases
For convenience aliases have been provided for all supported request methods.
- legionFetch.instance.request({method: 'get'})
- legionFetch.instance.request({method: 'delete'})
- legionFetch.instance.request({method: 'head'})
- legionFetch.instance.request({method: 'options'})
- legionFetch.instance.request({method: 'post'})
- legionFetch.instance.request({method: 'put'})
- legionFetch.instance.request({method: 'patch'})
- legionFetch.instance.get(config)
- legionFetch.instance.post(config)
The legions-request Instance
Creating an instance
You can create a new instance of axios with a custom config.
import { legionFetch } from 'legions-fetch';
const instance = legionFetch.create();
Instance methods
- instance.request({method: 'get'})
- instance.request({method: 'delete'})
- instance.request({method: 'head'})
- instance.request({method: 'options'})
- instance.request({method: 'post'})
- instance.request({method: 'put'})
- instance.request({method: 'patch'})
- instance.get(config)
- instance.post(config)
Request Config
These are the available config options for making requests. Only the url is required. Requests will default to GET if method is not specified.
{
// `url` is the server URL that will be used for the request
url: '/user',
// `method` is the request method to be used when making the request
method: 'get', // default
// `baseURL` will be prepended to `url` unless `url` is absolute.
// It can be convenient to set `baseURL` for an instance of axios to pass relative URLs
// to methods of that instance.
baseURL: 'https://some-domain.com/api/',
// 发送前对配置数据进行处理
transformRequest: function (config, url) {
// Do whatever you want to transform the data
return config;
},
// `headers` are custom headers to be sent
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` are the URL parameters to be sent with the request
// Must be a plain object or a URLSearchParams object
// NOTE: params that are null or undefined are not rendered in the URL.
params: {
ID: 12345
},
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', 'DELETE , and 'PATCH'
// When no `transformRequest` is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
},
// syntax alternative to send data into the body
// method post
// only the value is sent, not the key
data: 'Country=Brasil&City=Belo Horizonte',
// `timeout` specifies the number of milliseconds before the request times out.
// If the request takes longer than `timeout`, the request will be aborted.
timeout: 1000, // default is `0` (no timeout)
// should be made using credentials
// "include" | "omit" | "same-origin"
credentials: 'include', // default
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
responseType: 'json', // default
// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `maxRedirects` defines the maximum number of redirects to follow in node.js.
// If set to 0, no redirects will be followed.
maxRedirects: 5, // default
// data 数据是否默认转json,默认true,只有在data是对象时才会执行
isDataToConvertJson:boolean,
/**
* A cryptographic hash of the resource to be fetched by request. Sets request's integrity.
*/
integrity?: string,
/**
* A boolean to set request's keepalive.
*/
keepalive?: boolean,
/**
* A string to set request's method.
*/
method?: string,
/**
* A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode.
*/
mode?: RequestMode,
/**
* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect.
*/
redirect?: RequestRedirect,
/**
* A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer.
*/
referrer?: string,
/**
* A referrer policy to set request's referrerPolicy.
*/
referrerPolicy?: ReferrerPolicy,
/**
* An AbortSignal to set request's signal.
*/
signal?: AbortSignal | null,
}
拦截器
在请求或响应被 then 处理前拦截它们。
use
legionFetch.instance.register({
request: (config) => {
config.credentials = 'omit';
return config;
},
response: (res) => {
return res;
},
responseReject: (res) => {
return res;
},
});
默认注入的拦截器
request
const defaultRequest = [
{
request: (configs /** 传入进来的配置参数*/) => {
//注册拦截请求, 默认所有请求返回json格式
/** 设置默认request */
let options = {
headers: {
'content-type': 'application/json',
},
credentials: 'include',
};
/** 使用传入进来的配置覆盖默认配置*/
let config = mergeConfig(options, { ...tranfromOptions(configs.options), url: configs.url });
return config;
},
},
{
request: (configs: IRequestConfigs) => {
//注册请求, 请求默认携带验证参数
let options = {
/** 超时时间*/
timeout: 50000,
};
let config = mergeConfig(options, configs);
return config;
},
},
];
response
defaultResponse = [
{
response: (response, config: IRequestConfigs) => {
return response;
},
},
{
//response已经被反序列化, 返回了对应对象
response: (response, config: IRequestConfigs) => {
if (config && config.responseType === 'json') {
const res = response.json();
return res;
}
return response;
},
},
];
Reject
[
{
//@ts-ignore
responseReject: (response, config) => {
let error: any = {};
if (response instanceof FetchError) {
error = {
name: response.name,
type: response.type,
code: response.code,
message: response.message,
};
} else if (response instanceof Error) {
error = {
name: response.name,
type: response.name,
stack: response.stack,
message: response.message,
};
}
return Promise.resolve(
new ResponseResult({
success: false,
message: JSON.stringify(error),
code: error.type === 'request-timeout' ? DataRequestStateEnum.Timeout.toString() : '500',
data: null,
}),
);
},
},
];
lrequestContainer
此方法初步方案借鉴社区方案实现,后续会迭代完善
所做事情
- 保证在相同的 HTTP 请求在同一个时间段内只有一个请求发生
- 所有的请求都经过这个容器,这样有助于我们去管理所有发生的请求
举例场景
-
单应用中,我们做组件开发,每一个组件应该独立完成自己的工作. 那么组件之间在绝大多数情况下是没有任何通信的. 那么如果一个页面存在任意 2 个或多个组件在几 乎同一个时间内发出相同的 http 请求, 这样无疑会浪费且会增加服务器负载. 为了处理这个问题, lrequestContainer(请求容器)保证同一个时刻有且仅有一个 http 请求, 无论组件发送多少请求.
-
对于单页面应用, 因为所有请求都经过容器,在我们发送一个耗时请求时,这个时候我们想离开当前页面. 但是任何发生在先前页面的请求会难以管理/取消 . lrequestContainer(请求容器)提供了一个非常好的方式让开发者能 轻易管理到这些请求.
use
import { lrequestContainer } from 'legions-fetch';
//type RequestParamType = {url: 'www.example.com/api', data: {token:123}, method: 'get'};
//promiseFn:() =>Promise<any>, e.g. promiseFn = ()=>$.ajax(...);
const promiseState = requestContainer.put(JSON.stringify(requestParam), promiseFn);
并发相同请求
import { lrequestContainer } from 'legions-fetch';
function httpRequest(duration: number): Promise<void> {
return new Promise(() => setTimeout(resolve, number));
}
const requestContainer = RequestContainer.getInstance();
//假设第一个请求请求2秒
const requestParam1 = { url: 'www.example.com/api', data: { token: 123 }, method: 'get' };
const promiseFn1 = () => httpRequest(2000);
const promiseState1 = requestContainer.put(JSON.stringify(requestParam1), promiseFn1);
//假设第二个请求4秒, 其实并没有关系, 因为第一个请求会决定这后面请求只会是2秒完成
const requestParam2 = { url: 'www.example.com/api', data: { token: 123 }, method: 'get' };
const promiseFn2 = () => httpRequest(4000);
const promiseState2 = requestContainer.put(JSON.stringify(requestParam2), promiseFn2);
/**
* 由于他们使用相同参数请求, 那么我们可以认为他们是相同的请求. 所以如果<请求1>发出,
* 请求2会被拦截, 并指向请求1, 也就是说他们两会共享同一个promise.
* 那么这样就能确保整一个请求周期有且只有一个真正的对外请求
*/
版权
MIT