简体   繁体   English

如何处理 axios 中的 401(身份验证错误)并做出反应?

[英]How to handle 401 (Authentication Error) in axios and react?

I have one file request.js which contains wrapper for axios ajax request.我有一个文件 request.js,其中包含 axios ajax 请求的包装器。 I am calling request function from multiple react components and when one of the request fails I want to refresh the token and retry all the failed requests again.我正在从多个反应组件调用请求 function,当其中一个请求失败时,我想刷新令牌并再次重试所有失败的请求。 I can use intercepters, but I don't know how to implement it.我可以使用拦截器,但我不知道如何实现它。 Please help.请帮忙。

request.js请求.js

 var client = axios.create({
   baseURL: 'http://192.168.1.3:3000',
     headers: {
     appID: 8,
     version: "1.1.0",
     empID: localStorage.getItem('empID'),
     token: localStorage.getItem('accessToken')
    }
 });

 const request = function(options) {
     const onSuccess = function(response) {
         console.debug('Request Successful!', response);
         return response.data;
     } 
     const onError = function(error) {
         console.error('Request Failed:', error.config);
         if (error.response) {
             console.error('Status:',  error.response.status);
             console.error('Data:',    error.response.data);
             console.error('Headers:', error.response.headers);
         } else {
             console.error('Error Message:', error.message);
         }

         return Promise.reject(error.response || error.message);
     }

     return client(options)
         .then(onSuccess)
         .catch(onError);
         options
 }

 export default request;

If you want to use interceptors to handle 401 error here is code snippet.如果你想使用拦截器来处理 401 错误,这里是代码片段。

axios.interceptors.response.use(response => {
   return response;
}, error => {
  if (error.response.status === 401) {
   //place your reentry code
  }
  return error;
});

I got it working with following code我用下面的代码让它工作

import axios from 'axios';
import config from '../../configuration.json';
import qs from 'qs';

const baseURL = config['baseUrl_local'];
let authTokenRequest;

/**
  * @description axios instance for ajax requests
*/ 

var client = axios.create({
baseURL: baseURL,
headers: {
    appID: 8,
    version: "1.1.0",
    empID: localStorage.getItem('empID'),
    token: localStorage.getItem('accessToken')
}
});

/**
 * @description this method calls a requestNewToken method to issue a 
 new token to the client
*/ 

 function getAuthToken() {
   if (!authTokenRequest) {
   authTokenRequest = requestNewToken();
   authTokenRequest.then(resetAuthTokenRequest, resetAuthTokenRequest);
 }
 return authTokenRequest;
 }

/**
  * @description this method requests the server to issue a new token, 
  the server response is updated in local storage accessToken
*/ 

function requestNewToken() {
  var newToken = request({
  method: "post",
  url: '/sign-in',
  data:  qs.stringify({
         "userName":localStorage.getItem('userName'),
         "password":localStorage.getItem('password')
         })  
  }).then((res)=>{
  if(res.status == "success"){
    localStorage.setItem('accessToken',res.data.accessToken);
    //if featureArray is present in response object, update the 
    featureArray in local storage
    if(res.data.features){
      localStorage.setItem(
      'featureArray',
     JSON.stringify(res.data.features));
    }
    client = axios.create({
     baseURL: baseURL,
     headers: {
          appID: 8,
          version: "1.1.0",
          empID: localStorage.getItem('empID'),
          token: localStorage.getItem('accessToken')
      }
   });
 } else {
  window.location = "/logout";
 }
});
 return newToken;
}

function resetAuthTokenRequest() {
  authTokenRequest = null;
 }

/**
  * @description if any of the API gets 401 status code, this method 
   calls getAuthToken method to renew accessToken
  * updates the error configuration and retries all failed requests 
  again
*/ 

client.interceptors.response.use(undefined, err => {
  const error = err.response;
  // if error is 401 
  if (error.status===401 && error.config && 
  !error.config.__isRetryRequest) {
  // request for a new token
  return getAuthToken().then(response => {
   // update the error config with new token
   error.config.__isRetryRequest = true;
   error.config.headers.token= localStorage.getItem("accessToken");
   return client(error.config);
  });
 } 
});

/**
 * @description wrapper for making ajax requests
 * @param {object} object with method,url,data etc.
*/ 

const request = function(options) {
  const onSuccess = function(response) {
    return response.data;
  }
 const onError = function(error) {
  //console.error('Request Failed:', error.config);
   if (error.response) {
  //console.error('Status:',  error.response.status);
  //console.error('Data:',    error.response.data);
  //console.error('Headers:', error.response.headers);
  } else {
  console.error('Error Message:', error.message);
  }
 return Promise.reject(error.response || error.message);
 }

return client(options)
        .then(onSuccess)
        .catch(onError);
        options
}

export default request;

[EDIT] Its 2019, Here is yet another implementation for the same. [编辑] 它是 2019 年,这是另一个实现。 The above solution is great but does not work well with multiple failed request, in turn it calls getToken with the updated token as well.上面的解决方案很好,但不能很好地处理多个失败的请求,反过来它也用更新的令牌调用 getToken 。

 import axios from "axios";

 /* @internal */
 import config from "../config";
 import TokenService from "./token_service";

class Request {
    constructor() {
        this.baseURL = config.baseUrl;
        this.isRefreshing = false;
        this.failedRequests = [];
        this.tokenService = new TokenService();
        this.client = axios.create({
            baseURL: config.apiServerBaseUrl,
            headers: {
               clientSecret: this.clientSecret,
            },
        });
        this.beforeRequest = this.beforeRequest.bind(this);
        this.onRequestFailure = this.onRequestFailure.bind(this);
        this.processQueue = this.processQueue.bind(this);
        this.client.interceptors.request.use(this.beforeRequest);
        this.client.interceptors.response.use(this.onRequestSuccess, 
this.onRequestFailure);
}

beforeRequest(request) {
    const token = TokenService.getAccessToken();
    request.headers.Authorization = `Token ${token}`;
    return request;
}

static onRequestSuccess(response) {
    return response.data;
}

async onRequestFailure(err) {
    const { response } = err;
    if (response.status === 401 && err && err.config && !err.config.__isRetryRequest) {
        if (this.isRefreshing) {
            try {
                const token = await new Promise((resolve, reject) => {
                    this.failedRequests.push({ resolve, reject });
                });
                err.config.headers.Authorization = `Bearer ${token}`;
                return this.client(err.config);
            }
            catch (e) {
                return e;
            }
        }
        this.isRefreshing = true;
        err.config.__isRetryRequest = true;
        return new Promise((resolve, reject) => {
            this.tokenService.refreshAccessToken().then((token) => {
                this.tokenService.setAccessToken(token);
                err.config.headers.Authorization = `Bearer ${token}`;
                this.isRefreshing = false;
                this.processQueue(null, token);
                resolve(this.client(err.config));
            }).catch((e) => {
                this.processQueue(e, null);
                reject(err.response);
            });
        });
    }
    throw response;
}

processQueue(error, token = null) {
    this.failedRequests.forEach((prom) => {
        if (error) {
            prom.reject(error);
        } else {
            prom.resolve(token);
        }
       });
        this.failedRequests = [];
    }

}

const request = new Request();

export default request.client;

This works:这有效:

// Add a 401 response interceptor
window.axios.interceptors.response.use(function (response) {
    return response;
}, function (error) {
    if (401 === error.response.status) {
        // handle error: inform user, go to login, etc
    } else {
        return Promise.reject(error);
    }
});

Extracted from: https://gist.github.com/yajra/5f5551649b20c8f668aec48549ef5c1f摘自: https : //gist.github.com/yajra/5f5551649b20c8f668aec48549ef5c1f

I had this extra issue:我有这个额外的问题:

"Network Error" without any response “网络错误”没有任何反应

tl;dr - It was a problem with CORS and how it was setup, so axios never got the information back from the browser. tl;dr - 这是 CORS 及其设置方式的问题,因此 axios 从未从浏览器获取信息。 You have to sort it out from the server side.您必须从服务器端对其进行排序。

Description说明

if you have a similar problem, you'll see it in the browser console.如果您有类似的问题,您会在浏览器控制台中看到它。 The browser will prevent you from accessing a different url over ajax.浏览器将阻止您通过 ajax 访问不同的 url。

In my particular case (node.js - express) was the order of the filter, the CORS filter (dev environment) was added after the handler for this particular request, so the server wasn't sending the proper CORS headers at the time and thus the browser wasn't even allowing the request to take place (there was no call to the server whatsoever, thus no error object got returned).在我的特殊情况下(node.js - express)是过滤器的顺序,CORS 过滤器(开发环境)是在这个特定请求的处理程序之后添加的,所以服务器当时没有发送正确的 CORS 标头,并且因此浏览器甚至不允许发生请求(没有对服务器的任何调用,因此没有返回错误对象)。

We can catch axios 401 using this method.我们可以使用这种方法捕获 axios 401。

 axios.post('/add')
 .then(function (response) {...})
 .catch(function (error) {
     console.log(error.response.status) // 401
     console.log(error.response.data.error) //Please Authenticate or whatever returned from server
   if(error.response.status==401){
     //redirect to login
   }
 })

I consulted some other questions and here is my code我咨询了其他一些问题,这是我的代码

import axios from 'axios';

const instance = axios.create({
    baseURL: window.location.hostname === 'localhost' ? 'http://localhost:5001/api/v1' : 'https://api.mysite.com/api/v1'
});
instance.defaults.headers.common['Content-Type'] = 'multipart/form-data';

//validate response
instance.interceptors.response.use((response) => {
    return response;
}, (error) => {
        if (error.response.status === 401) {

            return window.location.href = '/login'
        }
    return Promise.reject(error);
});

// Set the AUTH token for any request
instance.interceptors.request.use(
    (config) => {
        const token = localStorage.getItem('token');
        config.headers.Authorization =  token ? `Bearer ${token}` : '';
        return config;
    }
)

export default instance;

I haven't found a clear concise answer when using Axios in a module.在模块中使用 Axios 时,我还没有找到一个清晰简洁的答案。 You need to add the interceptors to the instance<\/em> of axios you are using.您需要将拦截器添加到您正在使用的 axios实例<\/em>中。

api.js<\/strong> api.js<\/strong>

import axios from 'axios'
import store from '../state'

//Defaults will be combined with the instance
axios.defaults.baseURL = '/some/page.aspx';

//Create Axios Instance
const axiosInstance = axios.create({
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json; charset=UTF-8'
    }
});

//Add interceptors to instance
axiosInstance.interceptors.response.use(
    response => response,
    error => {
        if (!error.response) {
            store.commit('setServiceAvailable', false);
        }
        else if (error.response.status === 401) {
            store.commit('setUserAuthorised', false);
        }
        return error;
    });

export default axiosInstance;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM