【问题标题】:Axios handling errorsAxios 处理错误
【发布时间】:2018-10-02 17:08:01
【问题描述】:

我正在尝试使用 Axios 更好地理解 javascript 承诺。我假装是处理 Request.js 中的所有错误,只从任何地方调用请求函数,而不必使用catch()

在此示例中,对请求的响应将为 400,并带有 JSON 格式的错误消息。

这是我得到的错误:

Uncaught (in promise) Error: Request failed with status code 400

我找到的唯一解决方案是在 Somewhere.js 中添加.catch(() => {}),但我试图避免这样做。有可能吗?

代码如下:

请求.js

export function request(method, uri, body, headers) {
  let config = {
    method: method.toLowerCase(),
    url: uri,
    baseURL: API_URL,
    headers: { 'Authorization': 'Bearer ' + getToken() },
    validateStatus: function (status) {
      return status >= 200 && status < 400
    }
  }

  ...

  return axios(config).then(
    function (response) {
      return response.data
    }
  ).catch(
    function (error) {
      console.log('Show error notification!')
      return Promise.reject(error)
    }
  )
}

Somewhere.js

export default class Somewhere extends React.Component {

  ...

  callSomeRequest() {
    request('DELETE', '/some/request').then(
      () => {
        console.log('Request successful!')
      }
    )
  }

  ...

}

【问题讨论】:

  • 你想打破承诺链吗?
  • 不确定。这是否会阻止我在调用请求函数时不必使用 catch?
  • 不成功的状态码在逻辑上是您应用程序中的异常状态吗?您希望调用代码对其做出什么反应?
  • 如果您将错误发送到成功路径,您很可能需要测试它们以便在更高级别分支。我想说让成功成为成功,让错误成为错误,并相应地 .catch()。

标签: javascript promise axios


【解决方案1】:

实际上,到目前为止,axios 是不可能的。只有2xx范围内的状态码才能被.then()捕获。

传统的方法是在catch() 块中捕获错误,如下所示:

axios.get('/api/xyz/abcd')
  .catch(function (error) {
    if (error.response) {
      // Request made and server responded
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // The request was made but no response was received
      console.log(error.request);
    } else {
      // Something happened in setting up the request that triggered an Error
      console.log('Error', error.message);
    }

  });

另一种方法是在请求或响应被 then 或 catch 处理之前拦截它们。

axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });

【讨论】:

  • 为什么你只在你的then 部分和Promise.reject 在你的catch 部分中使用简单的回报?对我来说似乎不太一致。
  • 我使用了来自github官方axios文档的sn-p。 github.com/axios/axios#interceptors
  • 无论如何,我认为您指的是interceptor 部分,但那里没有then。请求或响应在被处理之前被拦截,因此,我们还不想@​​987654333@。但是,如果遇到错误,如果我们愿意,我们可以Promise.reject()。或者,我们可以返回一些东西,然后在处理请求或响应时,我们可以使用Promise.reject()。同样的事情。
  • 啊,是的,这很有道理!因此,在我们已经知道的错误情况下,不需要进一步处理请求,因此可以拒绝承诺。
  • 谢谢,伙计!我不知道错误键也有error.request
【解决方案2】:

如果你想访问整个错误正文,如下所示:

 async function login(reqBody) {
  try {
    let res = await Axios({
      method: 'post',
      url: 'https://myApi.com/path/to/endpoint',
      data: reqBody
    });

    let data = res.data;
    return data;
  } catch (error) {
    console.log(error.response); // this is the main part. Use the response property from the error object

    return error.response;
  }

}

【讨论】:

  • 这真的解决了我的问题,谢谢elonaire
【解决方案3】:

你可以这样: error.response.data
就我而言,我从后端获得了 error 属性。所以,我使用了 error.response.data.error

我的代码:

axios
  .get(`${API_BASE_URL}/students`)
  .then(response => {
     return response.data
  })
  .then(data => {
     console.log(data)
  })
  .catch(error => {
     console.log(error.response.data.error)
  })

【讨论】:

    【解决方案4】:

    如果你想使用异步等待尝试

    export const post = async ( link,data ) => {
    const option = {
        method: 'post',
        url: `${URL}${link}`,
        validateStatus: function (status) {
            return status >= 200 && status < 300; // default
          },
        data
    };
    
    try {
        const response = await axios(option);
    } catch (error) {
        const { response } = error;
        const { request, ...errorObject } = response; // take everything but 'request'
        console.log(errorObject);
    }
    

    【讨论】:

    • 我已经看到 error.response 未定义,然后它将在解构中失败
    【解决方案5】:

    我尝试使用try{}catch{} 方法,但它对我不起作用。但是,当我切换到使用.then(...).catch(...) 时,AxiosError 被正确捕获,我可以使用它。当我在设置断点时尝试前者时,它不允许我看到 AxiosError 而是告诉我捕获的错误是未定义的,这也是最终在 UI 中显示的内容。

    不知道为什么会发生这种情况,我觉得这很简单。无论哪种方式,我都建议使用上面提到的常规.then(...).catch(...) 方法以避免向用户抛出未定义的错误。

    【讨论】:

    • 今天我也遇到了同样的情况:/
    • try..catch 仅适用于 async/await。您可以使用.catch()(捕获被拒绝的承诺)或try { await axios... } catch (err) {...}(捕获被拒绝的承诺导致的异常)来处理它
    【解决方案6】:

    如果我理解正确,您希望仅在请求成功时才调用请求函数的then,并且您希望忽略错误。为此,您可以创建一个新的 Promise 在 axios 请求成功时解决它,并且在失败的情况下永远不要拒绝它。

    更新后的代码如下所示:

    export function request(method, uri, body, headers) {
      let config = {
        method: method.toLowerCase(),
        url: uri,
        baseURL: API_URL,
        headers: { 'Authorization': 'Bearer ' + getToken() },
        validateStatus: function (status) {
          return status >= 200 && status < 400
        }
      }
    
    
      return new Promise(function(resolve, reject) {
        axios(config).then(
          function (response) {
            resolve(response.data)
          }
        ).catch(
          function (error) {
            console.log('Show error notification!')
          }
        )
      });
    
    }
    

    【讨论】:

    • 不会造成内存泄漏吗?用户体验也将是“奇怪的”。无尽的微调器,而不是在页面上的正确位置清除错误消息。猜猜这不是让用户高兴的原因。我投票赞成在您提出请求的每个地方添加诚实的错误处理程序!)
    【解决方案7】:

    从任何地方调用请求函数,而无需使用 catch()。

    首先,虽然在一个地方处理大多数错误是一个好主意,但请求并不那么容易。应该传递一些错误(例如 400 个验证错误,例如:“用户名被占用”或“无效电子邮件”)。

    所以我们现在使用基于 Promise 的函数:

    const baseRequest = async (method: string, url: string, data: ?{}) =>
      new Promise<{ data: any }>((resolve, reject) => {
        const requestConfig: any = {
          method,
          data,
          timeout: 10000,
          url,
          headers: {},
        };
    
        try {
          const response = await axios(requestConfig);
          // Request Succeeded!
          resolve(response);
        } catch (error) {
          // Request Failed!
    
          if (error.response) {
            // Request made and server responded
            reject(response);
          } else if (error.request) {
            // The request was made but no response was received
            reject(response);
          } else {
            // Something happened in setting up the request that triggered an Error
            reject(response);
          }
        }
      };
    

    然后你可以像这样使用请求

    try {
      response = await baseRequest('GET', 'https://myApi.com/path/to/endpoint')
    } catch (error) {
      // either handle errors or don't
    }
    

    【讨论】:

    • 抱歉吹毛求疵,但有两件事:如果你真的想使用 async 将它移到你的 promise resolve/reject 函数前面。或者理想情况下,甚至不用费心使用 Promise(因为您将 baseRequest 包装在异步装饰器中),只需继续您的 try/catch 和错误类型分支,只需使用 return 而不是 resolve。其次,我喜欢没有服务器响应的测试!但是当axios超时的时候,会不会抛出异常,被当作服务器没有返回响应?还是这些场景是一回事?
    • 感谢airtonix的建议。这个功能已经很老了,我总是很乐意改进代码。此函数中 async/await 和 Promises 的混合并不理想。你能编辑我的评论以反映这些变化吗? RE 您的问题 AFAIK axios 在代码的catch 部分中处理这两个问题。我会手动将超时设置为非常低,以测试我对超时的错误处理。 “缺席服务器”是指 404 错误?还是没有网络错误?全部在 catch 块中处理,因此请尝试自己触发它们进行测试。
    【解决方案8】:

    处理响应类型设置为流的 axios 错误的一种方法对我有用。

    .....
    .....
    try{
       .....
       .....
       // make request with responseType: 'stream'
       const url = "your url";
       const response = axios.get(url, { responseType: "stream" });
       // If everything OK, pipe to a file or whatever you intended to do
       // with the response stream
       .....
       .....
    } catch(err){
      // Verify it's axios error
      if(axios.isAxios(err)){
        let errorString = "";
        const streamError = await new Promise((resolve, reject) => {
          err.response.data
            .on("data", (chunk) => {
               errorString += chunk;
              }
            .on("end", () => {
               resolve(errorString);
             }
          });
        // your stream error is stored at variable streamError.
        // If your string is JSON string, then parse it like this
        const jsonStreamError = JSON.parse(streamError as string);
        console.log({ jsonStreamError })
        // or do what you usually do with your error message
        .....
        .....
      }
      .....
      .....
    }
       
      
    

    【讨论】:

      【解决方案9】:

      为了可重用性:

      创建文件errorHandler.js:

      export const errorHandler = (error) => {
        const { request, response } = error;
        if (response) {
          const { message } = response.data;
          const status = response.status;
          return {
            message,
            status,
          };
        } else if (request) {
          //request sent but no response received
          return {
            message: "server time out",
            status: 503,
          };
        } else {
          // Something happened in setting up the request that triggered an Error
          return { message: "opps! something went wrong while setting up request" };
        }
      };
      
      

      然后,每当你发现 axios 的错误时:

      Just import error handler from errorHandler.js and use like this.
        try {
          //your API calls 
        } catch (error) {
          const { message: errorMessage } = errorHandlerForAction(error);
           //grab message
        }
      

      【讨论】:

        猜你喜欢
        • 2020-10-12
        • 2018-04-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-20
        相关资源
        最近更新 更多