2019年8月31日 星期六

[Axios] Interceptor - Error handling


 Axios   Interceptor   Error handling   Typescript 



Introduction


Axios is the Promise based HTTP client, and is often used in my Vue.js projects.
This article shows how to integrate the custom error to handle non-2XX response and timeout request, so that we can show customized message for end-user, not the real error.

The concept and sample codes in based on Axios interceptor by Typescript.
However, they can be apply to other Http Client framework, and of course, write the code in Javascript.


Environment


axios 0.19.0
typescript 3.2.1



Implement

          
(Optional) Set default valid Http Status Codes

The default valid Http Status Codes are >= 200 and < 300.
That means the response with status 200~299 will NOT be rejected by Axios.
You can change the default value by setting axios.defaults.validateStatus,

For example, the 498 response will not be rejected by Axios with the following configuration:

import axios, { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';

const instance = axios.create();

// Overwrite the ValidateStatus settings
instance.defaults.validateStatus = (status: number) => {
  return (status >= 200 && status < 300) || status == 498;
};

export default instance;



Error handling for rejected Http response

We can create custom error class with custom (and international!) error message for certain rejected response.

MyCustomError.ts


export default class MyCustomError extends Error {
  public url: string;
  public statusCode: number | undefined;
  constructor(message?: string, url?: string, status?: number) {
    super(message);
    this.name = 'MyCustomError';
    this.url = String(url);
    this.statusCode = status;
    this.stack = new Error().stack;
  }
  /**
   * Error msg for server side
   * @memberof HttpRequestError
   */
  public toServerString = (): string => {
    return `[${this.name}][${this.statusCode || '??'}][${this.url}] ${this.message}`;
  }
  /**
   * Error msg for end-user
   * @memberof HttpRequestError
   */
  public toString = (): string => {
    return i18n.tc('error.httpRequestError');
  }
}


Notice that we separate the client-side error message from the server-side message because we don’t want the end-user to get the error’s detail J


Error handling for rejected Http response

Now we can integrate the custom error into the Axios interceptor.

import axios, { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios';
import JL from '@/modules/jsnlogger';
import store from '@/store';
import { MyCustomError } from '../error-model';

const instance = axios.create();

instance.interceptors.response.use( (response: AxiosResponse<any>) => {
  return response;
}, async (error: any) => {
 
  const { config, response: { status } } = error;
  const originalRequest = config;
  const url = originalRequest.url;
 
// Error handling here ...
  // For example, use JsnLog to save server-side log on server side
  const httpErr = new HttpRequestError(error, url, status);
JL.error(httpErr.toServerString());
 
// Reject the error
  return Promise.reject(httpErr);
});

export default instance;



With the above Axios instance, we can easily to catch the custom error and show the custom client-side’s error message as following,

try {
    const url = 'api/xxxx';
    const res: any = await axios.get(url);
    return res.data;
} catch (err) {
    console.log(String(err)); // This will shows the custom client-side msg
}


For example, zh-TW:



en-US:





Error handling for Timeout request

Axios has default timeout as 0 (no timeout).
We can change the default timeout as following,

const instance = axios.create();
instance.defaults.timeout = 3000; // Million seconds


Or by interceptor…


instance.interceptors.request.use( (config: AxiosRequestConfig) => {
 
  config.timeout = 3000; // Million seconds
  return config;

}, (error) => {
  return Promise.reject(error);
});



We can create another custom error model: MyTimeoutError (just like MyCustomError), to handle the timeout error.
Then we can update the Axios interceoptor:

instance.interceptors.response.use( (response: AxiosResponse<any>) => {
  return response;
}, async (error: any) => {
  if (error.code === 'ECONNABORTED') { // Timeout error
    const timeoutErr = new MyTimeoutError(error, error.config.url);
    // Do something…
    return Promise.reject(timeoutErr);
  } else {
    // Handle other err with MyCustomError model ...
  } 
});



Source code





Conclusion

The interceptor allows us to handle and control corresponding error on different responses or scenarios.
So that we can focus on the presentation or business logics when sending a Http request.



Reference







沒有留言:

張貼留言