likes
comments
collection
share

umi踩坑记录 —— useRequest 以及 umi 请求报错捕获流程

作者站长头像
站长
· 阅读数 6

引言

我们的项目中使用了 useRequest 这个 hooks。今天下午我在写项目的过程中发现,假如我的登录密码是错误的,当后端成功返回异常信息后,页面就报错了,提示我异常没有被成功捕获(Unhandled Rejection)......因为这个问题折腾了好几个小时

原因

先说一下原因吧,因为目前 Umi 4 中内置的 ahooks 版本为 2.x 版本,2.x 版本中的 useRequest 并没有返回 runAsync,即异步请求函数。但是在某些场景中,我希望通过异步请求来实现某些功能,又不想在项目中使用两种请求方案,即 useRequest 和直接使用 services 封装的请求函数

于是,我安装了 ahooks 3.x。在需要使用异步请求的地方,引用了 ahooks 3.x 中的 useRequest 函数,登录请求便在其中~

解决方案

解决方案为:为登录请求相关的 useRequest 及所有异步请求的函数都加了 try ... catch

总结

runAsync 返回的异步函数中,想要捕获错误使用自带的 onError 函数和 errorHandle 函数是处理不了的,如果该异步函数抛出了异常,上述的函数再处理完成后还会抛给外部一个异常,所以最后的解决方法必须要在最外层加一个 try ... catch

既然谈到这里了,也都走了一遍坑,我就总结一下 umi 全局请求是怎么捕获错误的

umi 全局请求的错误是怎么捕获的

在 umi 项目中,我们优先使用 umi 插件自带的 request,首先需要在 .umirc.ts 文件中增加配置

export default defineConfig({
  request: {},
});

在定义请求时,引用 umi 中的请求,我使用的是 umi/max

import { BASE_URL } from './config.ts';
import { request } from '@umijs/max';

export const requestUserLogin = async (data: any) => {
    return await request(`${BASE_URL}/api/user/login`, {
    method: 'post',
    data,
  });
}

我们通常需要对后端传回的数据进行全局处理,比如后端虽然成功响应,但是在返回的 data 中 code 不为 200 或者 success,我们就需要在 app.tsx 中进行配置

// app.tsx
import type { RequestConfig } from '@umijs/max';

export const request: RequestConfig {
    timeout: 1000 * 6,
    errorConfig: {
        errorThrower: (res) => {
            // 后端返回数据后,都会执行此函数,如果不符合条件,我们可以手动抛出一些错误,从而会被 errorHandle 捕获
            
            // 此处的 res 为响应拦截器中的 res.data 部分,为后端自己手动封装的 body 部分
            const { code, message } = res;
            if (code !== 200) {
                const error: any = new Error(message);
                error.name = 'BizError';
                error.info = { message, code };
                throw error; // 抛出自制的错误
            },
        },
        // 此函数只能对 useRequest 中 run 返回的同步函数内部的异步函数进行错误捕获;如果为 runAsync 或者我们自定义的异步请求函数,并且该错误是由 errorThrower、 responseInterceptors 或者 requestInterceptors 抛出(即不是请求的 status 不为 200),在 errorHandle 执行完之后还会抛出一个错误,所以异步请求一定要记得加 try ... catch
        errorHandle: (error) => {
            // 分情况处理错误
            // 错误可能为我们手动抛出的错误,可能为状态码(status)不为 200 的错误,或者后端服务没有响应的错误,或者是发送请求时出现的其它错误等等
            if (error.name === 'BizError') {
                // switch ...
            } else if (error.response?.status) {
                // 请求已发送,并且收到了响应,但是状态码超出了 2xx 的范围
                // 分情况处理
                // switch (error.response.status) {}
            } else if (error.request) {
                // 请求已发送,但是没有收到响应
            } else {
                // 发送请求前出现了问题
            }
        }
    },
    // 请求拦截
    requestInterceptors: () => {},
    // 响应拦截
    responseInterceptors: (res) => {
        // 此处抛出的错误会直接进入errorHandle,但我们一般选择在errorThrower中抛出
        
        // 此处的 res 包含整个响应内容,包括headers、status、data、config等等
        return res;
    }
}