如何处理401(认证错误)在轴和反应?

我有一个文件 request.js,它包含 Axios ajax 请求的包装器。我从多个响应组件调用请求函数,当其中一个请求失败时,我想刷新令牌并重试所有失败的请求。我可以使用拦截器,但我不知道如何实现它。请帮帮我。

Request.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;
181437 次浏览

If you want to use interceptors to handle 401 error here is code snippet.

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. 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.

 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

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. 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.

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).

We can catch axios 401 using this method.

 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 haven't found a clear concise answer when using Axios in a module. You need to add the interceptors to the instance of axios you are using.

api.js

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;

Then use as normal

component.js

import api from '../api'
...
async getServersJson() {
try {
var response = await api.post('GetReportAsServers', {name: 'get-servers', args: null});
this.serversJson = this.prettifyJson(response.data.d);
}
catch (error) {
console.error(`Exception getting servers. ${error}`);
}
},

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;