关于 axios 302 重定向的问题

在做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,设置redirectmanual
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 accepts

By default axios sends an Accept header of application/json, text/plain, */* where as browser generally send the Accept header with text/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

Response undefined for 302 status axios