如何处理 axios 中的 401(身份验证错误)并做出反应?
How to handle 401 (Authentication Error) in axios and react?
我有一个文件 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;
如果你想使用拦截器来处理 401 错误,这里是代码片段。
axios.interceptors.response.use(response => {
return response;
}, error => {
if (error.response.status === 401) {
//place your reentry code
}
return error;
});
我用下面的代码得到了它
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;
[编辑] 现在是 2019 年了,这是相同的另一个实现。上面的解决方案很好,但不能很好地处理多个失败的请求,反过来它也会用更新的令牌调用 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;
这个有效:
// 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);
}
});
摘自:
https://gist.github.com/yajra/5f5551649b20c8f668aec48549ef5c1f
我有这个额外的问题:
"Network Error"无任何回应
tl;dr - 这是 CORS 及其设置方式的问题,因此 axios 从未从浏览器获取信息。得从服务器端解决。
描述
如果您有类似的问题,您会在浏览器控制台中看到它。浏览器将阻止您通过 ajax.
访问不同的 url
在我的特定情况下 (node.js - express) 是过滤器的顺序,CORS 过滤器(开发环境)是在这个特定请求的处理程序之后添加的,所以服务器没有发送当时正确的 CORS headers,因此浏览器甚至不允许请求发生(没有对服务器的任何调用,因此没有返回错误 object)。
我们可以用这个方法抓到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
}
})
在模块中使用Axios时,我还没有找到一个清晰简洁的答案。您需要将拦截器添加到您正在使用的 axios 的实例。
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;
然后正常使用
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}`);
}
},
我咨询了其他一些问题,这是我的代码
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;
我有一个文件 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;
如果你想使用拦截器来处理 401 错误,这里是代码片段。
axios.interceptors.response.use(response => {
return response;
}, error => {
if (error.response.status === 401) {
//place your reentry code
}
return error;
});
我用下面的代码得到了它
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;
[编辑] 现在是 2019 年了,这是相同的另一个实现。上面的解决方案很好,但不能很好地处理多个失败的请求,反过来它也会用更新的令牌调用 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;
这个有效:
// 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);
}
});
摘自: https://gist.github.com/yajra/5f5551649b20c8f668aec48549ef5c1f
我有这个额外的问题:
"Network Error"无任何回应
tl;dr - 这是 CORS 及其设置方式的问题,因此 axios 从未从浏览器获取信息。得从服务器端解决。
描述
如果您有类似的问题,您会在浏览器控制台中看到它。浏览器将阻止您通过 ajax.
访问不同的 url在我的特定情况下 (node.js - express) 是过滤器的顺序,CORS 过滤器(开发环境)是在这个特定请求的处理程序之后添加的,所以服务器没有发送当时正确的 CORS headers,因此浏览器甚至不允许请求发生(没有对服务器的任何调用,因此没有返回错误 object)。
我们可以用这个方法抓到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
}
})
在模块中使用Axios时,我还没有找到一个清晰简洁的答案。您需要将拦截器添加到您正在使用的 axios 的实例。
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;
然后正常使用
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}`);
}
},
我咨询了其他一些问题,这是我的代码
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;