851 lines
27 KiB
Markdown
851 lines
27 KiB
Markdown
|
English | [简体中文](./README_zh-CN.md)
|
|||
|
|
|||
|
# 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.
|
|||
|
|
|||
|
[![NPM version](https://img.shields.io/npm/v/umi-request.svg?style=flat)](https://npmjs.org/package/umi-request)
|
|||
|
[![Build Status](https://img.shields.io/travis/umijs/umi-request.svg?style=flat)](https://travis-ci.org/umijs/umi-request)
|
|||
|
[![NPM downloads](http://img.shields.io/npm/dm/umi-request.svg?style=flat)](https://npmjs.org/package/umi-request)
|
|||
|
|
|||
|
---
|
|||
|
|
|||
|
## Supported features
|
|||
|
|
|||
|
- 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
|
|||
|
|
|||
|
## umi-request vs fetch vs axios
|
|||
|
|
|||
|
| Features | 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)
|
|||
|
|
|||
|
## TODO Welcome pr
|
|||
|
|
|||
|
- [x] Test case coverage 85%+
|
|||
|
- [x] write a document
|
|||
|
- [x] CI integration
|
|||
|
- [x] release configuration
|
|||
|
- [x] typescript
|
|||
|
|
|||
|
## Installation
|
|||
|
|
|||
|
```
|
|||
|
npm install --save umi-request
|
|||
|
```
|
|||
|
|
|||
|
## Example
|
|||
|
|
|||
|
Performing a `GET` request
|
|||
|
|
|||
|
```javascript
|
|||
|
import request from '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);
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## umi-request API
|
|||
|
|
|||
|
Requests can be made by passing relevant options to `umi-request`
|
|||
|
|
|||
|
**umi-request(url[, options])**
|
|||
|
|
|||
|
```javascript
|
|||
|
import request from '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);
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## Request method aliases
|
|||
|
|
|||
|
For convenience 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])**
|
|||
|
|
|||
|
## Creating an instance
|
|||
|
|
|||
|
You can use `extend({[options]})` to create a new instance of umi-request.
|
|||
|
|
|||
|
**extend([options])**
|
|||
|
|
|||
|
```javascript
|
|||
|
import { extend } from '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 umi-request in NodeJS enviroment
|
|||
|
|
|||
|
```javascript
|
|||
|
const umi = require('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 umi-request cases can see [antd-pro](https://github.com/umijs/ant-design-pro/blob/master/src/utils/request.js)
|
|||
|
|
|||
|
## request options
|
|||
|
|
|||
|
| 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 | -- |
|
|||
|
| timeoutMessage | customize timeout error message, please config `timeout` first | string | -- | -- |
|
|||
|
| 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 |
|
|||
|
| validateCache | cache strategy function | (url, options) => boolean | -- | only get request to cache |
|
|||
|
| 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 + method'.
|
|||
|
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,
|
|||
|
|
|||
|
// According to http protocal, request of GET used to get data from server, it's necessary to cache response data when server data update not frequently. We provide 'validateCache'
|
|||
|
// for some cases that need to cache data with other method reqeust.
|
|||
|
validateCache: (url, options) => { return options.method.toLowerCase() === 'get' },
|
|||
|
|
|||
|
|
|||
|
// 'requestType' 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,
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### Extend Options
|
|||
|
|
|||
|
Sometimes we need to update options after **extend** a request instance, umi-request provide **extendOptions** for users to update options:
|
|||
|
|
|||
|
```javascript
|
|||
|
const request = extend({ timeout: 1000, params: { a: '1' } });
|
|||
|
// default options is: { timeout: 1000, params: { a: '1' }}
|
|||
|
|
|||
|
request.extendOptions({ timeout: 3000, params: { b: '2' } });
|
|||
|
// after extendOptions: { timeout: 3000, params: { a: '1', b: '2' }}
|
|||
|
```
|
|||
|
|
|||
|
## Response Schema
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
## Error handling
|
|||
|
|
|||
|
```javascript
|
|||
|
import request, { extend } from '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);
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## Middleware
|
|||
|
|
|||
|
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 '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:
|
|||
|
|
|||
|
```shell
|
|||
|
a1 -> b1 -> response -> b2 -> a2
|
|||
|
```
|
|||
|
|
|||
|
2. Different 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:
|
|||
|
|
|||
|
```shell
|
|||
|
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 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);
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## Interceptor
|
|||
|
|
|||
|
You can intercept requests or responses before they are handled by then or catch.
|
|||
|
|
|||
|
1. global Interceptor
|
|||
|
|
|||
|
```javascript
|
|||
|
// request interceptor, change url or options.
|
|||
|
request.interceptors.request.use((url, options) => {
|
|||
|
return {
|
|||
|
url: `${url}&interceptors=yes`,
|
|||
|
options: { ...options, interceptors: true },
|
|||
|
};
|
|||
|
});
|
|||
|
|
|||
|
// Same as the last one
|
|||
|
request.interceptors.request.use(
|
|||
|
(url, options) => {
|
|||
|
return {
|
|||
|
url: `${url}&interceptors=yes`,
|
|||
|
options: { ...options, interceptors: true },
|
|||
|
};
|
|||
|
},
|
|||
|
{ global: 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. instance Interceptor
|
|||
|
|
|||
|
```javascript
|
|||
|
// Global interceptors are used `request` instance method directly
|
|||
|
request.interceptors.request.use(
|
|||
|
(url, options) => {
|
|||
|
return {
|
|||
|
url: `${url}&interceptors=yes`,
|
|||
|
options: { ...options, interceptors: true },
|
|||
|
};
|
|||
|
},
|
|||
|
{ global: false }
|
|||
|
); // second paramet defaults { global: true }
|
|||
|
|
|||
|
function createClient(baseUrl) {
|
|||
|
const request = extend({
|
|||
|
prefix: baseUrl,
|
|||
|
});
|
|||
|
return request;
|
|||
|
}
|
|||
|
|
|||
|
const clientA = createClient('/api');
|
|||
|
const clientB = createClient('/api');
|
|||
|
// Independent instance Interceptor
|
|||
|
clientA.interceptors.request.use(
|
|||
|
(url, options) => {
|
|||
|
return {
|
|||
|
url: `${url}&interceptors=clientA`,
|
|||
|
options,
|
|||
|
};
|
|||
|
},
|
|||
|
{ global: false }
|
|||
|
);
|
|||
|
|
|||
|
clientB.interceptors.request.use(
|
|||
|
(url, options) => {
|
|||
|
return {
|
|||
|
url: `${url}&interceptors=clientB`,
|
|||
|
options,
|
|||
|
};
|
|||
|
},
|
|||
|
{ global: false }
|
|||
|
);
|
|||
|
```
|
|||
|
|
|||
|
## Cancel request
|
|||
|
|
|||
|
### Use AbortController
|
|||
|
|
|||
|
Base on [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) that allows you to abort one or more Web requests as and when desired.
|
|||
|
|
|||
|
```javascript
|
|||
|
// polyfill abort controller if needed
|
|||
|
import 'yet-another-abortcontroller-polyfill'
|
|||
|
import Request from 'umi-request';
|
|||
|
|
|||
|
const controller = new AbortController(); // create a controller
|
|||
|
const { signal } = controller; // grab a reference to its associated AbortSignal object using the AbortController.signal property
|
|||
|
|
|||
|
signal.addEventListener('abort', () => {
|
|||
|
console.log('aborted!');
|
|||
|
});
|
|||
|
|
|||
|
Request('/api/response_after_1_sec', {
|
|||
|
signal, // pass in the AbortSignal as an option inside the request's options object (see {signal}, below). This associates the signal and controller with the fetch request and allows us to abort it by calling AbortController.abort(),
|
|||
|
});
|
|||
|
|
|||
|
// 取消请求
|
|||
|
setTimeout(() => {
|
|||
|
controller.abort(); // Aborts a DOM request before it has completed. This is able to abort fetch requests, consumption of any response Body, and streams.
|
|||
|
}, 100);
|
|||
|
```
|
|||
|
|
|||
|
### Use Cancel Token
|
|||
|
|
|||
|
> Cancel Token still work, but we don’t recommend using them in the new code.
|
|||
|
|
|||
|
1. You can cancel a request using a cancel token.
|
|||
|
|
|||
|
```javascript
|
|||
|
import Request from '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 'umi-request';
|
|||
|
|
|||
|
const CancelToken = Request.CancelToken;
|
|||
|
let cancel;
|
|||
|
|
|||
|
Request.get('/api/cancel', {
|
|||
|
cancelToken: new CancelToken(function executor(c) {
|
|||
|
cancel = c;
|
|||
|
}),
|
|||
|
});
|
|||
|
|
|||
|
// cancel request
|
|||
|
cancel();
|
|||
|
```
|
|||
|
|
|||
|
## Cases
|
|||
|
|
|||
|
### How to get Response Headers
|
|||
|
|
|||
|
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');
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
### File upload
|
|||
|
|
|||
|
Use FormData() contructor,the browser will add request 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)
|
|||
|
|
|||
|
## Development and debugging
|
|||
|
|
|||
|
- npm install
|
|||
|
- npm run dev
|
|||
|
- npm link
|
|||
|
- Then go to the project you are testing to execute npm link umi-request
|
|||
|
- Introduced and used
|
|||
|
|
|||
|
## Questions & Suggestions
|
|||
|
|
|||
|
Please open an issue [here](https://github.com/umijs/umi/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
|
|||
|
|
|||
|
## Code Contributors
|
|||
|
|
|||
|
- @clock157
|
|||
|
- @yesmeck
|
|||
|
- @yutingzhao1991
|
|||
|
|
|||
|
## LICENSE
|
|||
|
|
|||
|
MIT
|