umi-request API

可以通过向 umi-request 传参来发起请求 umi-request(url[, options])

  1. import request from 'umi-request';
  2. request('/api/v1/xxx', {
  3. method: 'get',
  4. params: { id: 1 },
  5. })
  6. .then(function(response) {
  7. console.log(response);
  8. })
  9. .catch(function(error) {
  10. console.log(error);
  11. });
  12. request('/api/v1/user', {
  13. method: 'post',
  14. data: {
  15. name: 'Mike',
  16. },
  17. })
  18. .then(function(response) {
  19. console.log(response);
  20. })
  21. .catch(function(error) {
  22. console.log(error);
  23. });

请求方法的别名

为了方便起见,为所有支持的请求方法提供了别名, method 属性不必在配置中指定
request.get(url[, options]) request.post(url[, options])

创建实例

有些通用的配置我们不想每个请求里都去添加,那么可以通过 extend 新建一个 umi-request 实例
extend([options])

  1. import { extend } from 'umi-request';
  2. const request = extend({
  3. prefix: '/api/v1',
  4. timeout: 1000,
  5. headers: {
  6. 'Content-Type': 'multipart/form-data',
  7. },
  8. });
  9. request
  10. .get('/user')
  11. .then(function(response) {
  12. console.log(response);
  13. })
  14. .catch(function(error) {
  15. console.log(error);
  16. });

请求配置

request options 参数

参数 说明 类型 可选值 默认值
method 请求方式 string get , post , put get
params url 请求参数 object 或 URLSearchParams 对象 — —
data 提交的数据 any — —
headers fetch 原有参数 object — {}
timeout 超时时长, 默毫秒, 写慎用 number —
timeoutMessage 超时可自定义提示文案, 需先定义 timeout string — —
prefix 前缀, 一般用于覆盖统一设置的 prefix string — —
suffix 后缀, 比如某些场景 api 需要统一加 .json string — —
credentials fetch 请求包含 cookies 信息 string — credentials: ‘same-origin’
useCache 是否使用缓存(仅支持浏览器客户端) boolean — false
validateCache 缓存策略函数 (url, options) => boolean — 默认 get 请求做缓存
ttl 缓存时长, 0 为不过期 number — 60000
maxCache 最大缓存数 number — 无限
requestType post 请求时数据类型 string json , form json
parseResponse 是否对 response 做简化 boolean — true
charset 字符集 string utf8 , gbk utf8
responseType 如何解析返回的数据 string json , text , blob , formData json , text
throwErrIfParseFail 当 responseType 为 ‘json’, 对请求结果做 JSON.parse 出错时是否抛出异常 boolean — false
getResponse 是否获取源 response, 返回结果将包裹一层 boolean — fasle
errorHandler 异常处理, 或者覆盖统一的异常处理 function(error) —

fetch 原其他参数有效, 详见fetch 文档

extend options 初始化默认参数, 支持以上所有

参数 说明 类型 可选值 默认值
method 请求方式 string get , post , put get
params url 请求参数 object — —
data 提交的数据 any — —
  1. {
  2. // 'method' 是创建请求时使用的方法
  3. method: 'get', // default
  4. // 'params' 是即将于请求一起发送的 URL 参数,参数会自动 encode 后添加到 URL 中
  5. // 类型需为 Object 对象或者 URLSearchParams 对象
  6. params: { id: 1 },
  7. // 'paramsSerializer' 开发者可通过该函数对 params 做序列化(注意:此时传入的 params 为合并了 extends 中 params 参数的对象,如果传入的是 URLSearchParams 对象会转化为 Object 对象
  8. paramsSerializer: function (params) {
  9. return Qs.stringify(params, { arrayFormat: 'brackets' })
  10. },
  11. // 'data' 作为请求主体被发送的数据
  12. // 适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
  13. // 必须是以下类型之一:
  14. // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  15. // - 浏览器专属:FormData, File, Blob
  16. // - Node 专属: Stream
  17. data: { name: 'Mike' },
  18. // 'headers' 请求头
  19. headers: { 'Content-Type': 'multipart/form-data' },
  20. // 'timeout' 指定请求超时的毫秒数(0 表示无超时时间)
  21. // 如果请求超过了 'timeout' 时间,请求将被中断并抛出请求异常
  22. timeout: 1000,
  23. // ’prefix‘ 前缀,统一设置 url 前缀
  24. // ( e.g. request('/user/save', { prefix: '/api/v1' }) => request('/api/v1/user/save') )
  25. prefix: '',
  26. // ’suffix‘ 后缀,统一设置 url 后缀
  27. // ( e.g. request('/api/v1/user/save', { suffix: '.json'}) => request('/api/v1/user/save.json') )
  28. suffix: '',
  29. // 'credentials' 发送带凭据的请求
  30. // 为了让浏览器发送包含凭据的请求(即使是跨域源),需要设置 credentials: 'include'
  31. // 如果只想在请求URL与调用脚本位于同一起源处时发送凭据,请添加credentials: 'same-origin'
  32. // 要改为确保浏览器不在请求中包含凭据,请使用credentials: 'omit'
  33. credentials: 'same-origin', // default
  34. // ’useCache‘ 是否使用缓存,当值为 true 时,GET 请求在 ttl 毫秒内将被缓存,缓存策略唯一 key 为 url + params + method 组合
  35. useCache: false, // default
  36. // ’ttl‘ 缓存时长(毫秒), 0 为不过期
  37. ttl: 60000,
  38. // 'maxCache' 最大缓存数, 0 为无限制
  39. maxCache: 0,
  40. // 根据协议规范, GET 请求用于获取、查询服务端数据,在数据更新频率不频繁的情况下做必要的缓存能减少服务端的压力,因为缓存策略是默认对 GET 请求做缓存,但对于一些特殊场景需要缓存其他类型请求的响应数据时,我们提供 validateCache 供用户自定义何时需要进行缓存, key 依旧为 url + params + method
  41. validateCache: (url, options) => { return options.method.toLowerCase() === 'get' },
  42. // 'requestType' 当 data 为对象或者数组时, umi-request 会根据 requestType 动态添加 headers 和设置 body(可传入 headers 覆盖 Accept 和 Content-Type 头部属性):
  43. // 1. requestType === 'json' 时, (默认为 json )
  44. // options.headers = {
  45. // Accept: 'application/json',
  46. // 'Content-Type': 'application/json;charset=UTF-8',
  47. // ...options.headers,
  48. // }
  49. // options.body = JSON.stringify(data)
  50. // 2. requestType === 'form' 时,
  51. // options.headers = {
  52. // Accept: 'application/json',
  53. // 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
  54. // ...options.headers,
  55. // };
  56. // options.body = query-string.stringify(data);
  57. // 3. 其他 requestType
  58. // options.headers = {
  59. // Accept: 'application/json',
  60. // ...options.headers,
  61. // };
  62. // options.body = data;
  63. requestType: 'json', // default
  64. // ’parseResponse‘ 是否对请求返回的 Response 对象做格式、状态码解析
  65. parseResponse: true, // default
  66. // ’charset‘ 当服务端返回的数据编码类型为 gbk 时可使用该参数,umi-request 会按 gbk 编码做解析,避免得到乱码, 默认为 utf8
  67. // 当 parseResponse 值为 false 时该参数无效
  68. charset: 'gbk',
  69. // 'responseType': 如何解析返回的数据,当 parseResponse 值为 false 时该参数无效
  70. // 默认为 'json', 对返回结果进行 Response.text().then( d => JSON.parse(d) ) 解析
  71. // 其他(text, blob, arrayBuffer, formData), 做 Response[responseType]() 解析
  72. responseType: 'json', // default
  73. // 'throwErrIfParseFail': 当 responseType 为 json 但 JSON.parse(data) fail 时,是否抛出异常。默认不抛出异常而返回 Response.text() 后的结果,如需要抛出异常,可设置 throwErrIfParseFail 为 true
  74. throwErrIfParseFail: false, // default
  75. // 'getResponse': 是否获取源 Response, 返回结果将包含一层: { data, response }
  76. getResponse: false,// default
  77. // 'errorHandler' 统一的异常处理,供开发者对请求发生的异常做统一处理,详细使用请参考下方的错误处理文档
  78. errorHandler: function(error) { /* 异常处理 */ },
  79. // 'cancelToken' 取消请求的 Token,详细使用请参考下方取消请求文档
  80. cancelToken: null,
  81. }

更新拓展实例默认参数

实例化一个请求实例后,有时还需动态更新默认参数,umi-request 提供 extendOptions 方法进行更新:

  1. const request = extend({ timeout: 1000, params: { a: '1' } });
  2. // 默认参数是 { timeout: 1000, params: { a: '1' }}
  3. request.extendOptions({ timeout: 3000, params: { b: '2' } });
  4. // 此时默认参数是 { timeout: 3000, params: { a: '1', b: '2' }}

响应结构

某个请求的响应返回的响应对象 Response 如下:

  1. {
  2. // `data` 由服务器提供的响应, 需要进行解析才能获取
  3. data: {},
  4. // `status` 来自服务器响应的 HTTP 状态码
  5. status: 200,
  6. // `statusText` 来自服务器响应的 HTTP 状态信息
  7. statusText: 'OK',
  8. // `headers` 服务器响应的头
  9. headers: {},
  10. }

当 options.getResponse === false 时, 响应结构为解析后的 data

  1. request.get('/api/v1/xxx', { getResponse: false }).then(function(data) {
  2. console.log(data);
  3. });

当 options.getResponse === true 时,响应结构为包含 data 和 Response 的对象

  1. request.get('/api/v1/xxx', { getResponse: true }).then(function({ data, response }) {
  2. console.log(data);
  3. console.log(response.status);
  4. console.log(response.statusText);
  5. console.log(response.headers);
  6. });

在使用 catch 或者 errorHandler, 响应对象可以通过 error 对象获取使用,参考错误处理这一节文档。

错误处理

  1. import request, { extend } from 'umi-request';
  2. const errorHandler = function(error) {
  3. const codeMap = {
  4. '021': '发生错误啦',
  5. '022': '发生大大大大错误啦',
  6. // ....
  7. };
  8. if (error.response) {
  9. // 请求已发送但服务端返回状态码非 2xx 的响应
  10. console.log(error.response.status);
  11. console.log(error.response.headers);
  12. console.log(error.data);
  13. console.log(error.request);
  14. console.log(codeMap[error.data.status]);
  15. } else {
  16. // 请求初始化时出错或者没有响应返回的异常
  17. console.log(error.message);
  18. }
  19. throw error; // 如果throw. 错误将继续抛出.
  20. // 如果return, 则将值作为返回. 'return;' 相当于return undefined, 在处理结果时判断response是否有值即可.
  21. // return {some: 'data'};
  22. };
  23. // 1. 作为统一错误处理
  24. const extendRequest = extend({ errorHandler });
  25. // 2. 单独特殊处理, 如果配置了统一处理, 但某个api需要特殊处理. 则在请求时, 将errorHandler作为参数传入.
  26. request('/api/v1/xxx', { errorHandler });
  27. // 3. 通过 Promise.catch 做错误处理
  28. request('/api/v1/xxx')
  29. .then(function(response) {
  30. console.log(response);
  31. })
  32. .catch(function(error) {
  33. return errorHandler(error);
  34. });

中间件

类 koa 的洋葱机制,让开发者优雅地做请求前后的增强处理,支持创建实例、全局、内核中间件。

  • 实例中间件(默认) :request.use(fn) 不同实例创建的中间件相互独立不影响;
  • 全局中间件 : request.use(fn, { global: true }) 全局中间件,不同实例共享全局中间件;
  • 内核中间件 :request.use(fn, { core: true }) 内核中间件, 方便开发者拓展请求内核;

    参数

    fn 入参

  • ctx(Object):上下文对象,包括 req 和 res 对象

  • next(Function):调用下一个中间件的函数

options 参数

  • global(boolean): 是否为全局中间件,优先级比 core 高
  • core(boolean): 是否为内核中间件

    例子

  1. 同类型中间件执行顺序 ```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’);

  1. 执行顺序如下:
  2. ```shell
  3. a1 -> b1 -> response -> b2 -> a2
  1. 不同类型中间件执行顺序

    1. request.use(async (ctx, next) => {
    2. console.log('instanceA1');
    3. await next();
    4. console.log('instanceA2');
    5. });
    6. request.use(async (ctx, next) => {
    7. console.log('instanceB1');
    8. await next();
    9. console.log('instanceB2');
    10. });
    11. request.use(
    12. async (ctx, next) => {
    13. console.log('globalA1');
    14. await next();
    15. console.log('globalA2');
    16. },
    17. { global: true }
    18. );
    19. request.use(
    20. async (ctx, next) => {
    21. console.log('coreA1');
    22. await next();
    23. console.log('coreA2');
    24. },
    25. { core: true }
    26. );

    执行顺序如下:

    1. instanceA1 -> instanceB1 -> globalA1 -> coreA1 -> coreA2 -> globalA2 -> instanceB2 -> instanceA2
  2. 使用中间件对请求前后做处理

    1. request.use(async (ctx, next) => {
    2. const { req } = ctx;
    3. const { url, options } = req;
    4. // 判断是否需要添加前缀,如果是统一添加可通过 prefix、suffix 参数配置
    5. if (url.indexOf('/api') !== 0) {
    6. ctx.req.url = `/api/v1/${url}`;
    7. }
    8. ctx.req.options = {
    9. ...options,
    10. foo: 'foo',
    11. };
    12. await next();
    13. const { res } = ctx;
    14. const { success = false } = res; // 假设返回结果为 : { success: false, errorCode: 'B001' }
    15. if (!success) {
    16. // 对异常情况做对应处理
    17. }
    18. });
  3. 使用内核中间件拓展请求能力 ```javascript request.use( async (ctx, next) => { const { req } = ctx; const { url, options } = req; const { umiRequestCoreType = ‘normal’ } = options;

    // umiRequestCoreType 用于区分请求内核类型 // 值为 ‘normal’ 使用 umi-request 内置的请求内核 if (umiRequestCoreType === ‘normal’) { await next(); return; }

    // 非 normal 使用自定义请求内核获取响应数据 const response = getResponseByOtherWay();

    // 将响应数据写入 ctx 中 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); });

  1. <a name="f7ae864d"></a>
  2. ## 拦截器
  3. 在请求或响应被 `then` 或 `catch` 处理前拦截它们。
  4. 1. 全局拦截器
  5. ```javascript
  6. // request拦截器, 改变url 或 options.
  7. request.interceptors.request.use((url, options) => {
  8. return {
  9. url: `${url}&interceptors=yes`,
  10. options: { ...options, interceptors: true },
  11. };
  12. });
  13. // 和上一个相同
  14. request.interceptors.request.use(
  15. (url, options) => {
  16. return {
  17. url: `${url}&interceptors=yes`,
  18. options: { ...options, interceptors: true },
  19. };
  20. },
  21. { global: true }
  22. );
  23. // response拦截器, 处理response
  24. request.interceptors.response.use((response, options) => {
  25. const contentType = response.headers.get('Content-Type');
  26. return response;
  27. });
  28. // 提前对响应做异常处理
  29. request.interceptors.response.use(response => {
  30. const codeMaps = {
  31. 502: '网关错误。',
  32. 503: '服务不可用,服务器暂时过载或维护。',
  33. 504: '网关超时。',
  34. };
  35. message.error(codeMaps[response.status]);
  36. return response;
  37. });
  38. // 克隆响应对象做解析处理
  39. request.interceptors.response.use(async response => {
  40. const data = await response.clone().json();
  41. if (data && data.NOT_LOGIN) {
  42. location.href = '登录url';
  43. }
  44. return response;
  45. });
  1. 实例内部拦截器 ``javascript // 全局拦截器直接使用 request 实例中的方法 request.interceptors.request.use( (url, options) => { return { url:${url}&interceptors=yes`, options: { …options, interceptors: true }, }; }, { global: false } ); // 第二个参数不传默认为 { global: true }

function createClient(baseUrl) { const request = extend({ prefix: baseUrl, }); return request; }

const clientA = createClient(‘/api’); const clientB = createClient(‘/api’); // 局部拦截器使用 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 } );

  1. <a name="b16806dc"></a>
  2. ## 中止请求
  3. <a name="b2b1f360"></a>
  4. ### 通过 AbortController 来中止请求
  5. 基于 [AbortController](https://developer.mozilla.org/zh-CN/docs/Web/API/FetchController) 方案来中止一个或多个DOM请求
  6. ```javascript
  7. // 按需决定是否使用 polyfill
  8. import 'yet-another-abortcontroller-polyfill'
  9. import Request from 'umi-request';
  10. const controller = new AbortController(); // 创建一个控制器
  11. const { signal } = controller; // 返回一个 AbortSignal 对象实例,它可以用来 with/abort 一个 DOM 请求。
  12. signal.addEventListener('abort', () => {
  13. console.log('aborted!');
  14. });
  15. Request('/api/response_after_1_sec', {
  16. signal, // 这将信号和控制器与获取请求相关联然后允许我们通过调用 AbortController.abort() 中止请求
  17. });
  18. // 取消请求
  19. setTimeout(() => {
  20. controller.abort(); // 中止一个尚未完成的DOM请求。这能够中止 fetch 请求,任何响应Body的消费者和流。
  21. }, 100);

案例

如何获取响应头信息

通过 Headers.get() 获取响应头信息。(可参考 MDN 文档)

  1. request('/api/v1/some/api', { getResponse: true }).then(({ data, response }) => {
  2. response.headers.get('Content-Type');
  3. });

文件上传

使用 FormData() 构造函数时,需要制定requestType: "form",然后浏览器会自动识别并添加请求头 "Content-Type: multipart/form-data",且参数依旧是表单提交时那种键值对,因此不需要开发者手动设置请求头 Content-Type,否则可能接口会报 500 的错误。

  1. const formData = new FormData();
  2. formData.append('file', file);
  3. request('/api/v1/some/api', { method: 'post', requestType: "form", data: formData });

如果希望获取自定义头部信息,需要在服务器设置 Access-Control-Expose-Headers,然后可按照上述方式获取自定义头部信息。