> 文章列表 > antd Pro 统一错误处理及解决接口返回错误时页面出现的BizError

antd Pro 统一错误处理及解决接口返回错误时页面出现的BizError

antd Pro 统一错误处理及解决接口返回错误时页面出现的BizError

理解request,import { request } from ‘umi’;

//app.ts配置
export const request: RequestConfig = {//错误出理器errorHandler: errorHandler,//请求拦截器,根据需要决定是否需要requestInterceptors: [authHeaderInterceptor],errorConfig: {//响应体格式转换器adaptor: errorConfigAdaptor,}
};//错误出理器:这段代码是plug-request 里面默认的处理,只是修改了一部分@custom
export const errorHandler = (error: RequestError) => {//@custom//这个是下面配置的响应体转换器const errorAdaptor =error?.request?.options?.errorConfig?.adaptor || ((resData: any) => resData);const DEFAULT_ERROR_PAGE = '/exception';// @ts-ignoreif (error?.request?.options?.skipErrorHandler) {throw error;}let errorInfo: ErrorInfoStructure | undefined;if (error.name === 'ResponseError' && error.data && error.request) {const ctx: Context = {req: error.request,res: error.response,};errorInfo = errorAdaptor(error.data, ctx);//@custom ////非标准格式或异常状态码时处理,符合标准格式转为BizErrorif (errorInfo?.success === undefined) {errorInfo = {success: false,errorMessage: `${ctx?.res?.statusText} (#${ctx?.res?.status})`,};}error.message = errorInfo?.errorMessage || error.message;error.data = error.data;error.info = errorInfo;}errorInfo = error.info;if (errorInfo) {const errorMessage = errorInfo?.errorMessage;const errorCode = errorInfo?.errorCode;const errorPage =error?.request?.options?.errorConfig?.errorPage || DEFAULT_ERROR_PAGE;switch (errorInfo?.showType) {case ErrorShowType.SILENT:// do nothingbreak;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:// @ts-ignorehistory.push({pathname: errorPage,query: {errorCode, errorMessage},});// redirect to error pagebreak;default:message.error(errorMessage);break;}} else {message.error(error.message || 'Request error, please retry.');}throw error;
}//响应体格式转换器 这里处理后,就符合规格定时了,但是非标转异常的状态码不会处理
export const errorConfigAdaptor = (resData: any | { code: number, message: string, name?: string, data: any, status: number, [key: string]: any }, ctx) => {if (resData?.code && resData?.status) {return {success: resData.status >= 200 && resData.status < 300,//yii框架抛出异常时有name,具体可以根据服务端响应处理errorMessage: resData?.message != '' && resData.message || resData?.name != '' && resData.name || '',errorCode: resData?.code || '',data: resData?.data || resData,...resData};}return resData;
};

示例错误处理

如果遇到后台返回数据不规范,不返回任何错误信息的情况,可以对数据进行适配

const errorConfig = {errorPage: '',adaptor: (resData: any) => {console.log('resData', resData)// 如 resData = {flag: true}return {...resData,success: resData.flag || resData.success,// data: resData.obj,errorMessage: resData.msg || '未知错误',};},
};

注意点: 对于服务器返回的数据于这个格式不一致的,可以在Ant Design Pro
5/src/app.tsx通过配置进行适配。但此适配只作用于错误处理,并影响接口返回的数据。所以在使用useRequest时,这个适配并没有用。

方法一:调用使用trycatch

缺点:需要更改所有的方法,工程量比较大

const handleAdd = async (fields: API.RuleListItem) => {const hide = message.loading('正在添加');try {await addRule({ ...fields });hide();message.success('Added successfully');return true;} catch (error) {hide();message.error('Adding failed, please try again!');return false;}
};

方法二:定义接口时直接跳过错误处理 skipErrorHandler: true,

缺点:需要更改所有的报错方法,工程量次之

export async function xxx(params: {}, options?: { [key: string]: any }) {const res = await request('/api/xxx', {method: 'GET',params,headers: {'Content-Type': 'application/json',},...(options || {}),skipErrorHandler: true,});if (res?.flag) {return {success: res.flag,data: res.obj,};}return res;
}

推荐方法三:errorHandler错误处理函数,当302时跳转,可注释errorConfig

// 错误处理
const errorHandler = (error: any) => {const { response } = error;console.log('errorHandle', response);// 我的接口使用code,根据业务决定使用status还是codeif (response && !response.success) {const errorText =codeMessage[response.status] || response.statusText || response.message || '未知错误';const { status, url } = response;if (+response.code === 404) {// console.log('404', 404);} else if (+response.code === 500) {// console.log('500', 500);} else if (+response.code === 302) {// console.log('302', 302);history.push('/user/login');} else {// 我已经在responseIntercept抛出错误,此处注释// notification.error({//   message: `请求错误 ${status}`,//   description: errorText,// });}// notification.error({//   message: `请求错误 ${status}: ${url}`,//   description: errorText,// });}// Unhandled Rejection (BizError): 此处异常不要使用throw error,会直接页面抛出return response;// throw error;
};

响应拦截器

如果后台接口不规范,无法进入errorHandler,优先使用响应拦截器

// 响应拦截器
const responseInterceptor = async (response: Response, options: RequestInit) => {const disposition = response.headers.get('Content-Disposition'); // 获取Content-Dispositionconst data = !disposition && (await response.clone().json());// console.log('返回了', data, disposition, response, options?.skipErrorHandler);// if (!data.flag || (data.flag && data.code)) {// skipErrorHandler 是否跳过统一错误处理if ((data?.flag === false || data?.success === false) && !options?.skipErrorHandler) {notification.destroy();notification.error({message: `${data.msg || data.message || '未知错误'}`,});if (+data.code === 302 || +data.code === 301 ) {history.push('/user/login');}}return disposition // 当Content-Disposition中有值的时候进行处理,其他请求的响应则放过? {blob: await response.blob(), // 将二进制的数据转为blob对象,这一步是异步的因此使用async/awaitfileName: decodeURI(disposition.split(';')[1].split('filename=')[1]), // 处理Content-Disposition,获取header中的文件名}: response;
};

注意响应拦截器优先于errorHandler执行,用响应拦截器可以不再使用errorHandler

附参考文档

附1: Ant Design Pro 5 网络请求和错误处理 - 前端知识 - UJCMS

注意点: antd Design Pro 5提供了自定义错误处理方法,和自定义数据格式的接口。默认的演示代码中,覆盖了@umijs/plugin-request的错误处理方法。如未注意到这点,则会迷惑于Ant Design Pro 5的错误处理行为与@umijs/plugin-request文档描述的并不一致,前面所述的错误显示类型完全无效。

附2: ant design pro v5 之统一错误处理_errorthrower没有触发_叫我虫虫吧的博客-CSDN博客