news 2026/9/14 2:52:32

UmiJS Max 内置 request 插件详解:基于 axios 的统一请求、错误处理与拦截器方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UmiJS Max 内置 request 插件详解:基于 axios 的统一请求、错误处理与拦截器方案

UmiJS Max 内置 request 插件详解:基于 axios 的统一请求、错误处理与拦截器方案

【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi

本文基于 UmiJS 仓库中@umijs/max的 Request 插件官方文档(docs/docs/docs/max/request.en-US.md)撰写,系统讲解其构建时配置(dataField)、运行时配置(errorConfigrequestInterceptorsresponseInterceptors)、request/useRequestAPI、请求取消机制,以及从 umi@3 的 umi-request 迁移到 axios 方案的全部要点;并结合仓库源码 packages/plugins/src/request.ts 还原每个配置项的底层实现,帮助你在实际项目中快速落地一套统一、可定制的网络请求方案。

方案概览:axios + ahooks useRequest

@umijs/max内置了 request 插件,提供基于 axios 的统一网络请求与错误处理方案,同时内置了 ahooks 的useRequest用于在组件内消费数据:

import { request, useRequest } from 'umi'; request; useRequest;

从源码结构看,该插件在 packages/plugins/src/request.ts 中以key: 'request'注册,属于"按配置启用"(EnableBy.config)的插件:当项目在构建配置中声明了request字段,或在src/app.ts中导出了运行时request配置时,框架会自动生成临时的request.tstypes.d.ts等运行时文件(见该文件api.onGenerateFiles段落),从而把 axios 实例与 ahooks 的useRequest封装成一套统一 API。

仓库内可直接参考的示例包括:

  • 基础用法示例:examples/with-request/app.ts、examples/with-request/pages/index.tsx;
  • 完整@umijs/max运行时配置示例:examples/max/app.ts;
  • Ant Design Pro 模板中的错误处理配置:examples/antd-pro-create/src/requestErrorConfig.ts。

配置

构建时配置:dataField

在构建配置中可以为useRequest配置dataField,默认值为data

export default { request: { dataField: 'data' }, };

该配置的主要目的是方便useRequest直接消费数据。如果你希望在消费数据时拿到后端的原始数据,需要把dataField配置为''(空字符串)。

例如,如果后端返回的数据结构如下:

{ success: true, data: 123, code: 1, }

配置默认值时,useRequest拿到的data就是内层的123,而不是{ success, data, code }

源码层面可以印证这一机制:插件在生成临时文件时读取api.config.request?.dataField(未配置时回退为'data'),并据此生成useRequestformatResult逻辑——dataField为空时生成result => result(原样透传),否则生成result => result?.${dataField}(自动解包对应字段)。相关实现见 packages/plugins/src/request.ts。此外,dataField还会影响生成的ResultWithData类型与泛型,使useRequest返回值的data类型与配置保持一致。

运行时配置

src/app.ts中,通过导出request配置项即可定制项目的请求行为:

import type { RequestConfig } from 'umi'; export const request: RequestConfig = { timeout: 1000, // other axios options you want errorConfig: { errorHandler(){ }, errorThrower(){ } }, requestInterceptors: [], responseInterceptors: [] };

errorConfigrequestInterceptorsresponseInterceptors三项外,其余配置会直接透传给 axios 的请求配置(如timeoutheadersparamsSerializer等)。这里配置的所有规则对全部requestuseRequest调用生效——因为运行时配置最终被传入axios.create(config)创建的全局 axios 实例,见 packages/plugins/src/request.ts。

errorConfig:errorThrower 与 errorHandler

如果想为请求设置统一的错误处理方案,就配置errorConfig

  • errorThrower:接收后端返回的数据,负责抛出一个你自定义的错误。你可以在这里按业务需要处理后端数据;
  • errorHandlerrequest会捕获errorThrower抛出的错误并执行该回调。它接收两个参数,第一个是被捕获的错误,第二个是该次请求的opts

两者需要配合使用。文档同时提示:如果你觉得这种错误处理方式过于复杂,也可以直接在拦截器中实现自己的错误处理。

触发时机:errorThrower底层是通过响应拦截器实现的,当响应的data.successfalse时被调用。

源码印证见 packages/plugins/src/request.ts:插件在创建 axios 实例后会额外注册一个响应拦截器,当data?.success === false且配置了errorConfig.errorThrower时,调用errorThrower(data)抛出错误;抛出的错误随后走 axios 的 reject 链路,在request方法的catch分支中被errorHandler捕获处理(见 packages/plugins/src/request.ts)。这一实现也解释了为什么文档强调"若使用了 errorHandler,单次请求注册的响应拦截器将不生效,因为 errorHandler 会抛出 error"。

requestInterceptors

为请求方法添加"请求阶段"拦截器,传入一个数组,每个元素是一个拦截器,按顺序注册到 axios 实例上。拦截器写法与 axios 请求拦截器一致:接收请求配置并返回它。

推荐使用RequestConfig类型,以规范化的方式编写拦截器:

const request: RequestConfig = { requestInterceptors: [ // 直接写一个函数作为拦截器 (url, options) => { // do something return { url, options } }, // 元组:第一个元素是请求拦截器,第二个是错误处理 [(url, options) => {return { url, options }}, (error) => {return Promise.reject(error)}], // 数组:省略错误处理 [(url, options) => {return { url, options }}] ] }

值得注意的是,为了兼容 umi-request 的使用习惯,插件同时允许 umi-request 风格的拦截器语法(即(url, options) => ({ url, options })双参数形式),尽管它可能通不过 TypeScript 的类型检查。源码中的兼容处理见 packages/plugins/src/request.ts:类型上定义了IRequestInterceptorAxios(单参数 config)与IRequestInterceptorUmiRequest(url + options 双参数)两种形态;运行时通过检查拦截器函数的length是否为 2 来区分两种写法并分别调用,见 packages/plugins/src/request.ts。

仓库中的 examples/with-request/app.ts 展示了多个请求拦截器注册的实例(含异步拦截器示例),可用于验证执行顺序行为。

responseInterceptors

为请求方法添加"响应阶段"拦截器,同样是传入数组、按顺序注册到 axios 实例。写法与 axios 响应拦截器一致:接收 axios response 并返回它。

const request: RequestConfig = { responseInterceptors: [ // 直接写一个函数作为拦截器 (response) => { // 无需异步读取响应体,可以直接从 data 读取 const { data = {} as any, config } = response; // do something return response }, // 元组:第一个元素是拦截器,第二个是错误处理 [(response) => {return response}, (error) => {return Promise.reject(error)}], // 数组:省略错误处理 [(response) => {return response}] ] }

注意:拦截器按你数组的顺序注册,但执行顺序遵循 axios 约定——后添加的请求拦截器先执行,后添加的响应拦截器后执行。这也是 examples/with-request/app.ts 中三个拦截器以3 → 2 → 1顺序打印日志的原因。

API

useRequest

插件内置了 ahooks 的useRequest,让你在组件内简单消费数据:

import { useRequest } from 'umi'; export default function Page() { const { data, error, loading } = useRequest(() => { return services.getUserList('/api/test'); }); if (loading) { return <div>loading...</div>; } if (error) { return <div>{error.message}</div>; } return <div>{data.name}</div>; };

在上述代码中,data并不是后端返回的原始数据,而是其内层的data(因为构建时配置默认值为'data'formatResult会自动解包,见 packages/plugins/src/request.ts 中useRequestformatResult注入逻辑)。

需要说明:ahooks 已更新到 3.0,但为降低umi@3项目升级的难度,插件继续基于 ahooks 2.0 的@ahooksjs/use-request实现。

request

request接受除透传 axios 全部配置之外,还额外提供了四个属性:skipErrorHandlergetResponserequestInterceptorsresponseInterceptors

request('/api/user', { params: { name : 1 }, timeout: 2000, // other axios options skipErrorHandler: true, getResponse: false, requestInterceptors: [], responseInterceptors: [], }

各属性行为:

  • skipErrorHandler: true:让该次请求跳过统一错误处理;
  • getResponse:默认request返回后端数据(即res.data);传入{ getResponse: true }则拿到 axios 完整响应结构。源码中对应resolve(getResponse ? res : res.data),见 packages/plugins/src/request.ts;
  • 单次requestInterceptors/responseInterceptors:写法与运行时配置相同,但这里注册的拦截器是"一次性"的——源码会在请求完成(无论成功或失败)后调用interceptors.request.eject/interceptors.response.eject将其移除,见 packages/plugins/src/request.ts。同时,单次注册的拦截器注册时机晚于运行时配置的拦截器;
  • 注意:当项目配置了errorHandler时,此处注册的响应拦截器将不生效,因为errorHandler会抛出 error。

IRequest的类型签名(含getResponse: true/false的重载区分返回AxiosResponse还是T)见 packages/plugins/src/request.ts。

RequestConfig

RequestConfig是帮助你规范化编写运行时配置的类型接口,导入时注意加type

import type { RequestConfig } from 'umi'; export const request:RequestConfig = {};

从源码看,其定义为AxiosRequestConfig的扩展,额外增加了errorConfigerrorHandler+errorThrower)、requestInterceptorsresponseInterceptors四个字段,见 packages/plugins/src/request.ts。

取消请求:AbortController

使用标准 fetch API 的AbortController机制取消请求:

import { request } from '@umijs/max'; import { Button } from 'antd'; const controller = new AbortController(); const HomePage: React.FC = () => { const fetchData = async () => { const res = await request('/api/getData', { method: 'GET', signal: controller.signal }) } const cancelData = () => { controller.abort(); } return ( <> <Button onClick={fetchData}>send request</Button> <Button onClick={cancelData}>cancel request</Button> </> ); }; export default HomePage;

由于signal属于 axios 原生支持的请求配置项,而运行时配置中除插件专属字段外全部透传给 axios,因此该能力可直接使用,无需额外插件支持。

从 umi@3 迁移到 umi@4

umi@3umi@4的升级中,官方停用了 umi-request,选择 axios 作为默认请求方案,相应地发生了几项功能变化。

运行时配置变化

umi@4的运行时配置与umi@3相比有显著变化:

export const request: RequestConfig = { errorConfig: { ++ errorHandler: () => {}, ++ errorThrower: () => {} -- errorPage: '', -- adaptor: ()=>{}, }; -- middlewares: [], ++ requestInterceptors: [], ++ responseInterceptors: [], ... // umi-request 与 axios 之间的差异。 };

要点归纳:

  • umi-request 的配置项变为 axios 的配置项;
  • 移除了middlewares中间件,可以用 axios 的 interceptors 实现相同功能;
  • errorConfig移除了原有全部设置,新增errorHandlererrorThrower作为统一错误处理设置。

中间件替换规则:umi@3中间件中next()之前的内容放入requestInterceptorsnext()之后的内容放入responseInterceptors

// Middleware(umi@3) async function middleware(ctx, next) { const { url, options } = req; if (url.indexOf('/api') !== 0) { ctx.req.url = `/api/v1/${url}`; } await next(); if (!ctx.res.success) { // do something } } // Interceptor(umi@4) { requestInterceptors:[ (config) => { if (config.url.indexOf('/api') !== 0) { config.url = `/api/v1/${url}`; } return config; } ], responseInterceptors: [ (response) => { if(!response.data.success){ // do something } } ] }

请求方法参数变化

umi-request 与 axios 在配置项上存在差异,迁移时两者文档需对照参考;由于插件对 umi-request 风格做了兼容(如前文双参数拦截器写法),大部分场景可直接平移。

GET 请求参数序列化差异

umi@3 默认用同一个 Key 序列化数组,而 umi@4 基于 axios,默认以方括号[]形式序列化:

// Umi@3 import { useRequest } from 'umi'; // a: [1,2,3] => a=1&a=2&a=3 // Umi@4 import { useRequest } from '@umijs/max'; // a: [1,2,3] => a[]=1&a[]=2&a[]=3

如果希望保持 umi@3 的序列化形式,可以借助paramsSerializer(该字段属于 axios 配置,可直接写在运行时配置中):

// src/app.[ts|tsx] import queryString from 'query-string'; export const request: RequestConfig = { paramsSerializer(params) { return queryString.stringify(params); }, ... }

完整运行时配置示例

下面给出一个完整的运行时配置示例,帮助你在项目中定制请求方案。该示例的错误处理方案脱胎于umi@3的内置错误处理;umi@4中官方将其移除以给用户更多自定义空间,如果你仍想沿用,可直接将该配置粘贴到项目中。你也可以完全通过响应拦截器编写自己的错误处理,并不局限于 errorConfig

import { RequestConfig } from './request'; // 错误处理方案:错误类型 enum ErrorShowType { SILENT = 0, WARN_MESSAGE = 1, ERROR_MESSAGE = 2, NOTIFICATION = 3, REDIRECT = 9, } // 与后端约定的响应数据结构 interface ResponseStructure { success: boolean; data: any; errorCode?: number; errorMessage?: string; showType?: ErrorShowType; } // 运行时配置 export const request: RequestConfig = { // 统一请求设置 timeout: 1000, headers: {'X-Requested-With': 'XMLHttpRequest'}, // 错误处理:umi@3 的错误处理方案。 errorConfig: { // 抛错 errorThrower: (res: ResponseStructure) => { const { success, data, errorCode, errorMessage, showType } = res; if (!success) { const error: any = new Error(errorMessage); error.name = 'BizError'; error.info = { errorCode, errorMessage, showType, data }; throw error; // 抛出自定义错误 } }, // 捕获错误并处理 errorHandler: (error: any, opts: any) => { if (opts?.skipErrorHandler) throw error; // 由我们的 errorThrower 抛出的错误。 if (error.name === 'BizError') { const errorInfo: ResponseStructure | undefined = error.info; if (errorInfo) { const { errorMessage, errorCode } = errorInfo; switch (errorInfo.showType) { case ErrorShowType.SILENT: // 什么都不做 break; case ErrorShowType.WARN_MESSAGE: message.warn(errorMessage); break; case ErrorShowType.ERROR_MESSAGE: message.error(errorMessage); break; case ErrorShowType.NOTIFICATION: notification.open({ description: errorMessage, message: errorCode, }); break; case ErrorShowType.REDIRECT: // TODO: 重定向 break; default: message.error(errorMessage); } } } else if (error.response) { // Axios 错误 // 请求已发出,服务器响应了非 2xx 的状态码 message.error(`Response status:${error.response.status}`); } else if (error.request) { // 请求已发出但未收到响应 message.error('None response! Please retry.'); } else { // 请求设置阶段触发的错误 message.error('Request error, please retry.'); } }, }, // 请求拦截器 requestInterceptors: [ (config) => { // 拦截请求配置做个性化处理。 const url = config.url.concat('?token = 123'); return { ...config, url}; } ], // 响应拦截器 responseInterceptors: [ (response) => { // 拦截响应数据做个性化处理 const { data } = response; if(!data.success){ message.error('Request failed!'); } return response; } ] };

这套方案覆盖了完整的错误分类:业务错误(errorThrower抛出BizError,按showType分五种展示级别)、HTTP 非 2xx 错误(error.response)、无响应错误(error.request)与请求设置错误。仓库中 examples/max/app.ts 与 examples/antd-pro-create/src/requestErrorConfig.ts 提供了该方案的真实落地版本,可直接对照参考。

小结:配置项速查

配置位置配置项作用源码依据
构建配置request.dataFielduseRequest数据解包字段,默认data''表示原样返回request.ts
src/app.tsRequestConfig其余字段透传给axios.create()全局生效request.ts
src/app.tserrorConfig.errorThrowerdata.success === false时抛错request.ts
src/app.tserrorConfig.errorHandler统一捕获并处理错误,可用skipErrorHandler跳过request.ts
src/app.ts/ 单次请求requestInterceptors/responseInterceptors全局 / 一次性拦截器,兼容 axios 与 umi-request 两种写法request.ts
单次请求getResponsetrue时返回完整AxiosResponse,否则返回datarequest.ts

掌握以上内容后,你可以独立完成:基于 axios 的统一请求初始化、按团队约定解包后端数据、按错误分级实现统一错误处理、用拦截器注入鉴权 token 等横切逻辑,并在 umi@3 存量项目中平滑迁移到 umi@4 的请求方案。

【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 2:52:17

AI Agent开发新范式:MCP协议接入、PyTorch教程与TVM编译实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 2:52:09

PHP社区交友系统实战:WebSocket实时消息与WebRTC音视频通话

简介&#xff1a;这份开源PHP社区交友系统面向想快速搭建私域社交平台的个人开发者与初创团队&#xff0c;涵盖网站端和APP端&#xff0c;支持实时消息、视频通话、语音通话等功能&#xff0c;是一套低门槛的完整交友解决方案。包体共2000个文件&#xff0c;以JS、CSS、HTML等前…

作者头像 李华
网站建设 2026/9/14 2:50:51

Hermes Agent 跑项目重构任务:Key 用 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 2:50:10

Cocos Creator打造微信飞机大战:从构建到性能优化全解析

简介&#xff1a;基于CocosCreator开发的微信经典飞机大战完整工程包&#xff0c;面向希望入门微信小游戏或学习Cocos Creator的小白与进阶学习者&#xff0c;可作毕设、课程设计或初期项目立项参考。项目采用数据驱动设计&#xff0c;敌机生成频率、移速、子弹频率等参数集中可…

作者头像 李华
网站建设 2026/9/14 2:49:46

Python+OpenCV人脸识别签到系统:客户端服务端架构与工程实践

简介&#xff1a;一套基于Python与OpenCV的人脸识别签到管理系统完整源码&#xff0c;面向毕业设计、期末大作业及课设实践&#xff0c;适合需要快速搭建人脸考勤项目并学习客户端与服务端双重架构的开发者。系统功能覆盖人脸注册、实时检测、身份识别、签到记录管理&#xff0…

作者头像 李华
网站建设 2026/9/14 2:49:39

VTK医学影像三维重建实战:从DICOM到STL临床级流程

简介&#xff1a;本资源是一个基于VTK的医学影像三维重建完整实践项目&#xff0c;面向医学图像处理初学者、计算机视觉开发者及生物医学工程相关专业学生&#xff0c;解决从DICOM数据读取、预处理、分割到三维可视化的一整套技术落地问题。压缩包共318个文件&#xff0c;含10个…

作者头像 李华