Axios在请求拦截器中不发送头信息。

3

在我的前端代码中,我有这段Axios代码:

import axios from "axios";
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'

const apiUrl = process.server ? `${process.env.FRONT}api/` : '/api/';

const api = axios.create({
  baseURL: apiUrl,
  headers: {
    'Content-Type': 'application/json'
  }
})

api.interceptors.request.use(function (config) {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.common['Authorization'] = 'Bearer ' + token
  }
  return config;
}, function (error) {
  return Promise.reject(error);
});

export const login = async (payload) => {
  const { data } = await api.post(`login`, payload)
  return data
}

这段代码的作用是设置一些带有令牌的头部信息。然后,该请求将转发到前端服务器:
router.post(`/login`, async (req, res) => {
  try {
    const data = await api.post('/login', req.body)
    res.json(data.data)
  } catch (e) {
    res.status(e.response.status).json(e.response.data)
  }
})

如果你在这里使用console.log(req.headers),一切都会没问题,头部信息看起来像这样:

req.headers {
  accept: 'application/json, text/plain, */*',
  'content-type': 'application/json',
  host: 'localhost:8010',
  connection: 'keep-alive',
  'content-length': '898',
  authorization: 'Bearer ...token...',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
...
}

以下是问题所在,当请求到后端时,它会被中间件auth捕获:

router.post('/login', auth, accountController.login)

下面是这个中间件的样子:

import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";

export default async (req: Request, res: Response, next: any) => {
  try {
    if (req.headers.authorization) {
      const user = await jwtService.getUser(req.headers.authorization.split(' ')[1])
      if (user) next();
      else return CommonResponse.common.unauthorized({ res })
    } else {
      return CommonResponse.common.unauthorized({ res })
    }
  } catch (e) {
    return CommonResponse.common.unauthorized({res});
  }
}

以下是问题所在,如果您在此处执行console.log(req.headers),您会看到:

{
  accept: 'application/json, text/plain, */*',
  'content-type': 'application/json',
  'user-agent': 'axios/0.26.0',
  'content-length': '898',
  host: 'localhost:3000',
  connection: 'close'
}

我的头信息怎么了?

顺便说一下,如果在前端服务器上执行以下操作:

const data = await api.post('/login', req.body, {headers: req.headers})

这样做是可行的,但是发生了什么?这就是拦截器应该工作的方式吗?


我不确定我是否理解正确,但是您确实有两个服务器,它们都监听/login路由。您有一个前端,使用api.post向您的第一个(前端)服务器发送请求。该请求使用router.post(..进行监听,并使用api.post(发送到后端服务器。我不理解这个前端服务器,但我不认为它可以访问localStorage。因此,在我看来,localStorage.get将无法工作。因为localStorage是浏览器方面的。如果我遗漏或误解了任何内容,请纠正我。 - archon
@archon,我想纠正一下你的说法。实际上,正如你所称呼的那样,“前端服务器”可以访问localStorage。如果在执行const { data } = await api.post(login, payload)之前,你执行了console.log(req.headers),你会看到这里有一个Bearer *token*,但是这个前端头部信息并没有传递到后端服务器,这就是问题所在。 - dokichan
我可能还是有些误解,但我的意思是console.log(req.headers)会起作用,因为你正在记录req.headers,而我试图告诉你的是,如果在Node上运行,window.localStorage.get将不起作用,因为不应该有任何window。因为localStorage实际上在window对象内部,我们不能在node中使用它。这是特定于浏览器的。因此,您的前端代码可以访问localStorage并且拦截器将正常工作,并且它将正确地将数据传递到服务器(req.headers),但是同样的拦截器在服务器上不起作用。 - archon
你能否尝试在前端服务器上的拦截器回调中记录错误?@dokichan - archon
我仍然认为这是问题所在。你的第一个 console.log(req.headers) 是好的很正常,因为你是从客户端传递它的,客户端有运行在浏览器上的 HTML 网页,并且可以访问 localStorage ,所以它可以使用拦截器而没有任何问题并通过headers传递数据。但是如果你在 frontend-server 上使用具有此特定拦截器的相同 api 模块,它将失败。因为它不在浏览器上运行,它没有访问任何浏览器功能。 而 localStorage.get 可能会引发错误。 - archon
显示剩余3条评论
3个回答

1
也许原因是你正在分配给request.headers.common。当我分配给req.headers时,它对我有效。
api.interceptors.request.use(
   (req) => { // I'm using req instead of config just for clarity
      req.headers['Authorization'] = `token ${token}`
      return req;
   },
   (err) => {
      return Promise.reject(err);
   }
);

在您的服务器端,将 req.headers.authorization 更改为 req.headers.Authorization
import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";

export default async (req: Request, res: Response, next: any) => {
  try {
    if (req.headers.Authorization) {
      const user = await jwtService.getUser(req.headers.authorization.split(' ')[1])
      if (user) next();
      else return CommonResponse.common.unauthorized({ res })
    } else {
      return CommonResponse.common.unauthorized({ res })
    }
  } catch (e) {
    return CommonResponse.common.unauthorized({res});
  }
}

0

将您的拦截器代码更改如下:

api.interceptors.request.use(function (config) {
 const token = localStorage.getItem('token')
 if (token) {
   config.headers['Authorization'] = 'Bearer ' + token
 }
 return config;
}, function (error) {
    return Promise.reject(error);
});

在中间件中,您应该使用 Authorization 而不是 authorization,例如:

import * as jwtService from './../services/jwtService';
import { Request, Response } from 'express';
import { CommonResponse } from "../responses/response";

export default async (req: Request, res: Response, next: any) => {
  try {
    if (req.headers.Authorization) { // correct this line
      const user = await jwtService.getUser(req.headers.Authorization.split(' ')[1]) // correct this line
      if (user) next();
      else return CommonResponse.common.unauthorized({ res })
    } else {
      return CommonResponse.common.unauthorized({ res })
    }
  } catch (e) {
    return CommonResponse.common.unauthorized({res});
  }
 }

同时确保您的localStorage中有令牌,并在仍然遇到问题时提供反馈。


不行,还是得到了默认的请求头: { accept: 'application/json, text/plain, */*', 'content-type': 'application/json', 'user-agent': 'axios/0.26.0', 'content-length': '898', host: 'localhost:3000', connection: 'close' } - dokichan

0

代码似乎没有问题。

尝试这个演示,它可以正常工作。 https://github.com/indolent-developer/axiosDemo

很可能是您的本地存储出了问题。通常我喜欢避免if而不是else。您可以添加一些控制台日志并查看它是否正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接