理解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-ignore
if (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
//
//非标准格式或异常状态码时处理,符合标准格式转为BizError
if (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 nothing
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:
// @ts-ignore
history.push({
pathname: errorPage,
query: {errorCode, errorMessage},
});
// redirect to error page
break;
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还是code
if (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-Disposition
const 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/await
fileName: 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博客