在做oauth的单点登录的时候,发现用axios去请求后端返回302状态码和重定向地址,但不能正确跳转,重定向地址被当成get请求去发送。查了一下axios的issues,整理一下一些解决方法。
- 如果是Node的环境,可以通过把
maxRedirects
设置为0,来解决问题。
// `maxRedirects` defines the maximum number of redirects to follow in node.js.
// If set to 0, no redirects will be followed.
maxRedirects: 5, // default
- 通过设置请求头部信息来解决问题。
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
- 需要后端配合,服务器用401未经授权之类的方式响应而不通过302。然后,前端再用axios拦截次响应,使用Javascript进行重定向。即是重定向逻辑不由浏览器实现,而是在前端实现。
axios.post("quote", params)
.catch(function(error) {
if (error.response && error.response.status === 401) {
window.location.href = "logon";
} else {
// Handle error however you want
}
});
- 如果只想使用302进行重定向,则可以通过使原始请求在前端运行来实现。例如,可以使用HTML表单提交来代替使用Axios进行请求。
<form action="/path/on/your/server" method="post">
<!-- ... -->
</form>
- 如果返回的状态码是200,这时候通过
response.request.responseURL
获取地址,然后进行重定向
axios.post('.', Data)
.then(response => {
if (response.status === 200) {
window.location.href = response.request.responseURL;
}
})
.catch(error => {console.log(error)});
- 如果返回的是error.response对象未定义,那可以这么处理
axios.interceptors.response.use((response) => {
return response
}, (error) => {
// we can't seem to catch the 302 status code as an error,
// however, since it redirects to another domain (login.microsoftonline.com) it causes
// a CORS error which makes error.response be undefined here. This assumes that any time
// error.response is undefined that we need to redirect to the login page
if (typeof error.response === 'undefined') {
window.location = 'https://login.microsoftonline.com'
} else {
return Promise.reject(error)
}
})
- 用fetch代替axios,设置
redirect
为manual
fetch(url, {
redirect: "manual"
}).then((res) => {
if (res.type === "opaqueredirect") {
// redirect to login page
window.location.href = response.url;
} else {
// handle normally / pass on to next handler
}
}).catch(...);
- 贴一个点赞数最高的解决方法:
Browsers always follow redirects for XHRs or fetch() requests. There is no library that could prevent the redirect. What you need to do on your server-side is distinguish between XHR requests and normal browser navigation requests and send either a 403 w/ JSON and specify the URL you want to redirect to in there or send a 302 if the request is being made by a browser navigation request. You can do this a couple different ways, but I’ll list some here assuming you’re using Express:
Check the
req.xhr
to respond based on its boolean value// The user needs to login again if (req.xhr) { res.status(403).json({ error: 'You must login to see this', location: 'https://login.microsoftonline.com' }) } else { res.redirect('https://login.microsoftonline.com') }
Use
res.format()
to respond based on what the client acceptsBy default axios sends an
Accept
header ofapplication/json, text/plain, */*
where as browser generally send theAccept
header withtext/html
being listed first.// The user needs to login again res.format({ json: () => res.status(403).json({ error: 'You must login to see this', location: 'https://login.microsoftonline.com' }), html: () => res.redirect('https://login.microsoftonline.com') default: () => res.redirect('https://login.microsoftonline.com') })
Then on your client-side you would use something like this when using either of the above solutions:
axios.interceptors.response.use((response) => { return response }, (error) => { if (error.response && error.response.data && error.response.data.location) { window.location = error.response.data.location } else { return Promise.reject(error) } })
参考资料:
Interceptor for 302 responses #980
302 received but browser does not redirect. #396
Need some advice about handling 302 redirects from Ajax #932