wodax-umi-request
Version:
A request tool based on fetch. Cloned from umi-request
736 lines (572 loc) • 21 kB
Markdown
English | [简体中文](./README_zh-CN.md)
**Base on umi-request**
The network request library, based on fetch encapsulation, combines the features of fetch and axios to provide developers with a unified api call method, simplifying usage, and providing common functions such as caching, timeout, character encoding processing, and error handling.
[](https://npmjs.org/package/wodax-umi-request)
[](https://npmjs.org/package/wodax-umi-request)
--------------------
- url parameter is automatically serialized
- post data submission method is simplified
- response return processing simplification
- api timeout support
- api request cache support
- support for processing gbk
- request and response interceptor support like axios
- unified error handling
- middleware support
- cancel request support like axios
- make http request from node.js
| Features | wodax-umi-request | fetch | axios |
| :---------- | :-------------- | :-------------- | :-------------- |
| implementation | Browser native support | Browser native support | XMLHttpRequest |
| size | 9k | 4k (polyfill) | 14k |
| query simplification | ✅ | ❎ | ✅ |
| post simplification | ✅ | ❎ | ❎ |
| timeout | ✅ | ❎ | ✅ |
| cache | ✅ | ❎ | ❎ |
| error Check | ✅ | ❎ | ❎ |
| error Handling | ✅ | ❎ | ✅ |
| interceptor | ✅ | ❎ | ✅ |
| prefix | ✅ | ❎ | ❎ |
| suffix | ✅ | ❎ | ❎ |
| processing gbk | ✅ | ❎ | ❎ |
| middleware | ✅ | ❎ | ❎ |
| cancel request | ✅ | ❎ | ✅ |
For more discussion, refer to [Traditional Ajax is dead, Fetch eternal life](https://github.com/camsong/blog/issues/2) If you have good suggestions and needs, please mention [issue](https://github.com/umijs/umi/issues)
- [x] Test case coverage 85%+
- [x] write a document
- [x] CI integration
- [x] release configuration
- [x] typescript
```
npm install --save wodax-umi-request
```
Performing a ```GET``` request
``` javascript
import request from 'wodax-umi-request';
request.get('/api/v1/xxx?id=1')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// use options.params
request.get('/api/v1/xxx', {
params: {
id: 1
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
```
Performing a ```POST``` request
``` javascript
request.post('/api/v1/user', {
data: {
name: 'Mike'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
```
Requests can be made by passing relevant options to ```wodax-umi-request```
**wodax-umi-request(url[, options])**
```javascript
import request from 'wodax-umi-request';
request('/api/v1/xxx', {
method: 'get',
params: { id: 1 }
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
request('/api/v1/user', {
method: 'post',
data: {
name: 'Mike'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
```
For convenience wodax-umi-request have been provided for all supported methods.
**request.get(url[, options])**
**request.post(url[, options])**
**request.delete(url[, options])**
**request.put(url[, options])**
**request.patch(url[, options])**
**request.head(url[, options])**
**request.options(url[, options])**
You can use ```extend({[options]})``` to create a new instance of wodax-umi-request.
**extend([options])**
``` javascript
import { extend } from 'wodax-umi-request';
const request = extend({
prefix: '/api/v1',
timeout: 1000,
headers: {
'Content-Type': 'multipart/form-data'
}
});
request.get('/user')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
```
Create an instance of wodax-umi-request in NodeJS enviroment
```javascript
const umi = require('wodax-umi-request');
const extendRequest = umi.extend({ timeout: 10000 })
extendRequest('/api/user')
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
});
```
The available instance methods are list below. The specified options will be merge with the instance options.
**request.get(url[, options])**
**request.post(url[, options])**
**request.delete(url[, options])**
**request.put(url[, options])**
**request.patch(url[, options])**
**request.head(url[, options])**
**request.options(url[, options])**
More wodax-umi-request cases can see [antd-pro](https://github.com/umijs/ant-design-pro/blob/master/src/utils/request.js)
| Parameter | Description | Type | Optional Value | Default |
| :--- | :--- | :--- | :--- | :--- |
| method | request method | string | get , post , put ... | get |
| params | url request parameters | object or URLSearchParams | -- | -- |
| data | Submitted data | any | -- | -- |
| headers | fetch original parameters | object | -- | {} |
| timeout | timeout, default millisecond, write with caution | number | -- | -- |
| prefix | prefix, generally used to override the uniform settings prefix | string | -- | -- |
| suffix | suffix, such as some scenes api need to be unified .json | string | -- |
| credentials | fetch request with cookies | string | -- | credentials: 'same-origin' |
| useCache | Whether to use caching (only support browser environment) | boolean | -- | false |
| ttl | Cache duration, 0 is not expired | number | -- | 60000 |
| maxCache | Maximum number of caches | number | -- | 0(Infinity) |
| requestType | post request data type | string | json , form | json |
| parseResponse | response processing simplification | boolean | -- | true |
| charset | character set | string | utf8 , gbk | utf8 |
| responseType | How to parse the returned data | string | json , text , blob , formData ... | json , text |
| throwErrIfParseFail | throw error when JSON parse fail and responseType is 'json' | boolean | -- | false |
| getResponse | Whether to get the source response, the result will wrap a layer | boolean | -- | fasle |
| errorHandler | exception handling, or override unified exception handling | function(error) | -- |
| cancelToken | Token to cancel request | CancelToken.token | -- | -- |
The other parameters of fetch are valid. See [fetch documentation](https://github.github.io/fetch/)
## extend options Initialize default parameters, support all of the above
| Parameter | Description | Type | Optional Value | Default |
| :--- | :--- | :--- | :--- | :--- |
| method | request method | string | get , post , put ... | get |
| params | url request parameters | object | -- | -- |
| data | Submitted data | any | -- | -- |
| ... |
```javascript
{
// 'method' is the request method to be used when making the request
method: 'get', // default
// 'params' are the URL parameters to be sent with request
// Must be a plain object or a URLSearchParams object
params: { id: 1 },
// 'paramSerializer' is a function in charge of serializing 'params'. ( be aware of 'params' was merged by extends's 'params' and request's 'params' and URLSearchParams will be transform to plain object. )
paramsSerializer: function (params) {
return Qs.stringify(params, { arrayFormat: 'brackets' })
},
// 'data' 作为请求主体被发送的数据
// 适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
// 必须是以下类型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream
// 'data' is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
// Must be of one of the following types:
// 1. string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// 2. Browser only: FormData, File, Blob
// 3. Node only: Stream
data: { name: 'Mike' },
// 'headers' are custom headers to be sent
headers: { 'Content-Type': 'multipart/form-data' },
// 'timeout' specifies the number of milliseconds before the request times out.
// If the request takes longer than 'timeout', request will be aborted and throw RequestError.
timeout: 1000,
// ’prefix‘ used to set URL's prefix
// ( e.g. request('/user/save', { prefix: '/api/v1' }) => request('/api/v1/user/save') )
prefix: '',
// ’suffix‘ used to set URL's suffix
// ( e.g. request('/api/v1/user/save', { suffix: '.json'}) => request('/api/v1/user/save.json') )
suffix: '',
// 'credentials' indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests.
// omit: Never send or receive cookies.
// same-origin: Send user credentials (cookies, basic http auth, etc..) if the URL is on the same origin as the calling script. This is the default value.
// include: Always send user credentials (cookies, basic http auth, etc..), even for cross-origin calls.
credentials: 'same-origin', // default
// ’useCache‘ The GET request would be cache in ttl milliseconds when 'useCache' is true.
// The cache key would be 'url + params'.
useCache: false, // default
// 'ttl' cache duration(milliseconds),0 is infinity
ttl: 60000,
// 'maxCache' are the max number of requests to be cached, 0 means infinity.
maxCache: 0,
// 'requestType' wodax-umi-request will add headers and body according to the 'requestType' when the type of data is object or array.
// 1. requestType === 'json' :(default )
// options.headers = {
// Accept: 'application/json',
// 'Content-Type': 'application/json;charset=UTF-8',
// ...options.headers,
// }
// options.body = JSON.stringify(data)
//
// 2. requestType === 'form':
// options.headers = {
// Accept: 'application/json',
// 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
// ...options.headers,
// };
// options.body = query-string.stringify(data);
//
// 3. other requestType
// options.headers = {
// Accept: 'application/json',
// ...options.headers,
// };
// options.body = data;
requestType: 'json', // default
// 'parseResponse' whether processing response
parseResponse: true, // default
// 'charset' This parameter can be used when the server returns gbk to avoid garbled characters.(parseResponse should set to true)
charset: 'gbk',
// 'responseType': how to processing response.(parseResponse should be true)
// The default value is 'json', would processing response by Response.text().then( d => JSON.parse(d) )
// Other responseType (text, blob, arrayBuffer, formData), would processing response by Response[responseType]()
responseType: 'json', // default
// 'throwErrIfParseFail': whether throw error or not when JSON parse data fail and responseType is 'json'.
throwErrIfParseFail: false, // default
// 'getResponse': if you need the origin Response, set true and will return { data, response }.
getResponse: false,// default
// 'errorHandler' error handle entry.
errorHandler: function(error) { /* 异常处理 */ },
// 'cancelToken' the token of cancel request.
cancelToken: null,
}
```
The response for a request contains the following information.
``` javascript
{
// 'data' is the response that was provided by the server
data: {},
// 'status' is the HTTP status code from the server response
status: 200,
// 'statusText' is the HTTP status message from the server response
statusText: 'OK',
// 'headers' the headers that the server responded with
// All header names are lower cased
headers: {},
}
```
When options.getResponse === false, the response schema would be 'data'
```javascript
request.get('/api/v1/xxx', { getResponse: false })
.then(function(data) {
console.log(data);
})
```
When options.getResponse === true ,the response schema would be { data, response }
``` javascript
request.get('/api/v1/xxx', { getResponse: true })
.then(function({ data, response }) {
console.log(data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
})
```
You can get Response from ```error``` object in errorHandler or request.catch.
```javascript
import request, { extend } from 'wodax-umi-request';
const errorHandler = function (error) {
const codeMap = {
'021': 'An error has occurred',
'022': 'It’s a big mistake,',
// ....
};
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.status);
console.log(error.response.headers);
console.log(error.data);
console.log(error.request);
console.log(codeMap[error.data.status])
} else {
// The request was made but no response was received or error occurs when setting up the request.
console.log(error.message);
}
throw error; // If throw. The error will continue to be thrown.
// return {some: 'data'}; If return, return the value as a return. If you don't write it is equivalent to return undefined, you can judge whether the response has a value when processing the result.
// return {some: 'data'};
}
// 1. Unified processing
const extendRequest = extend({ errorHandler });
// 2. Separate special treatment
// If unified processing is configured, but an api needs special handling. When requested, the errorHandler is passed as a parameter.
request('/api/v1/xxx', { errorHandler });
// 3. not configure errorHandler, the response will be directly treated as promise, and it will be caught.
request('/api/v1/xxx')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
return errorHandler(error);
})
```
Expressive HTTP middleware framework for node.js. For development to enhance before and after request. Support create instance, global, core middlewares.
**Instance Middleware (default)** request.use(fn) Different instances's instance middleware are independence.
**Global Middleware** request.use(fn, { global: true }) Different instances share global middlewares.
**Core Middleware** request.use(fn, { core: true }) Used to expand request core.
request.use(fn[, options])
### params
fn params
* ctx(Object):context, content request and response
* next(Function):function to call the next middleware
options params
* global(boolean): whether global, higher priority than core
* core(boolean): whether core
### example
1. same type of middlewares
``` javascript
import request, { extend } from 'wodax-umi-request';
request.use(async (ctx, next) => {
console.log('a1');
await next();
console.log('a2');
})
request.use(async (ctx, next) => {
console.log('b1');
await next();
console.log('b2');
})
const data = await request('/api/v1/a');
```
order of middlewares be called:
```
a1 -> b1 -> response -> b2 -> a2
```
2. Defferent type of middlewares
``` javascript
request.use( async (ctx, next) => {
console.log('instanceA1');
await next();
console.log('instanceA2');
})
request.use( async (ctx, next) => {
console.log('instanceB1');
await next();
console.log('instanceB2');
})
request.use( async (ctx, next) => {
console.log('globalA1');
await next();
console.log('globalA2');
}, { global: true })
request.use( async (ctx, next) => {
console.log('coreA1');
await next();
console.log('coreA2');
}, { core: true })
```
order of middlewares be called:
```
instanceA1 -> instanceB1 -> globalA1 -> coreA1 -> coreA2 -> globalA2 -> instanceB2 -> instanceA2
```
3. Enhance request
``` javascript
request.use(async (ctx, next) => {
const { req } = ctx;
const { url, options } = req;
if ( url.indexOf('/api') !== 0 ) {
ctx.req.url = `/api/v1/${url}`;
}
ctx.req.options = {
...options,
foo: 'foo'
};
await next();
const { res } = ctx;
const { success = false } = res;
if (!success) {
// ...
}
})
```
4. Use core middleware to expand request core.
``` javascript
request.use(async (ctx, next) => {
const { req } = ctx;
const { url, options } = req;
const { __umiRequestCoreType__ = 'normal' } = options;
// __umiRequestCoreType__ use to identificat request core
// when value is 'normal' , use wodax-umi-request 's fetch request core
if ( __umiRequestCoreType__ === 'normal') {
await next();
return;
}
// when value is not normal, use your request func.
const response = getResponseByOtherWay();
ctx.res = response;
await next();
return;
}, { core: true });
request('/api/v1/rpc', {
__umiRequestCoreType__: 'rpc',
parseResponse: false,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
```
You can intercept requests or responses before they are handled by then or catch.
``` javascript
// request interceptor, change url or options.
request.interceptors.request.use((url, options) => {
return (
{
url: `${url}&interceptors=yes`,
options: { ...options, interceptors: true },
}
);
});
// response interceptor, chagne response
request.interceptors.response.use((response, options) => {
response.headers.append('interceptors', 'yes yo');
return response;
});
// handling error in response interceptor
request.interceptors.response.use((response) => {
const codeMaps = {
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
message.error(codeMaps[response.status]);
return response;
});
// clone response in response interceptor
request.interceptors.response.use(async (response) => {
const data = await response.clone().json();
if(data && data.NOT_LOGIN) {
location.href = '登录url';
}
return response;
})
```
1. You can cancel a request using a cancel token.
```javascript
import Request from 'wodax-umi-request';
const CancelToken = Request.CancelToken;
const { token, cancel } = CancelToken.source();
Request.get('/api/cancel', {
cancelToken: token
}).catch(function(thrown) {
if (Request.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
Request.post('/api/cancel', {
name: 'hello world'
}, {
cancelToken: token
})
// cancel request (the message parameter is optional)
cancel('Operation canceled by the user.');
```
2. You can also create a cancel token by passing an executor function to the CancelToken constructor:
```javascript
import Request from 'wodax-umi-request';
const CancelToken = Request.CancelToken;
let cancel;
Request.get('/api/cancel', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
// cancel request
cancel();
```
use **Headers.get()** to get information from Response Headers. ( more detail see [MDN doc](https://developer.mozilla.org/zh-CN/docs/Web/API/Headers/get))
``` javascript
request('/api/v1/some/api', { getResponse: true })
.then(({ data, response}) => {
response.headers.get('Content-Type');
})
```
If want to get a custem header, you need to set [Access-Control-Expose-Headers](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) on server.
Use **Headers.get()** (more detail see [MDN 文档](https://developer.mozilla.org/zh-CN/docs/Web/API/Headers/get))
``` javascript
request('/api/v1/some/api', { getResponse: true })
.then(({ data, response}) => {
response.headers.get('Content-Type');
})
```
Use FormData() contructor,the browser will add rerequest header ```"Content-Type: multipart/form-data"``` automatically, developer don't need to add request header **Content-Type**
``` javascript
const formData = new FormData();
formData.append('file', file);
request('/api/v1/some/api', { method:'post', data: formData });
```
The Access-Control-Expose-Headers response header indicates which headers can be exposed as part of the response by listing their names.[Access-Control-Expose-Headers](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Access-Control-Expose-Headers)
- npm install
- npm run dev
- npm link
- Then go to the project you are testing to execute npm link wodax-umi-request
- Introduced and used
Please open an issue [here](https://github.com/umijs/umi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
MIT