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-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博客

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值