Axios 发布请求适用于 Postman,但不适用于浏览器(它返回“错误 401-未授权”)
Posted
技术标签:
【中文标题】Axios 发布请求适用于 Postman,但不适用于浏览器(它返回“错误 401-未授权”)【英文标题】:Axios post request works on Postman but not on browser (it returns "Error 401- Not Authorized") 【发布时间】:2021-10-15 08:34:03 【问题描述】:我在使用 axios 发布请求时遇到问题。在浏览器中,如果我在控制台中选中“网络”,则会显示错误“401 - 未授权”。但是用 Postman 进行测试一切正常。 我已经尝试了几个在类似问题中公开的解决方案,但都没有成功。
这是我设置快递服务器的代码:
import express from "express";
import mongoose from "mongoose";
import cors from "cors";
import dotenv from "dotenv";
import authRouter from "./routes/auth.js";
import categoryRouter from "./routes/category.js";
//DOTENV CONFIG
dotenv.config();
const MONGODB_CONNECTION = process.env.MONGO_URI;
//INITIALIZE APP
const app = express();
//MIDDLEWARES
app.use(function (req, res, next)
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
);
app.use(express.json());
app.use(cors());
//ROUTES
app.use("/api/auth", authRouter);
app.use("/api/categories", categoryRouter);
//PORT
const PORT = process.env.PORT || 5020;
//CONNECT MONGODB
const connectDB = () =>
mongoose
.connect(MONGODB_CONNECTION,
useNewUrlParser: true,
useFindAndModify: true,
useUnifiedTopology: true,
useCreateIndex: true,
)
.then(() => console.log("MONGODB is connected"))
.catch((err) => console.log("MONGODB connection error:", err));
;
connectDB();
//INITIALIZE SERVER
app.listen(PORT, () =>
console.log(`Connection is established and running on port $PORT`)
);
这是与路由器设置相关的代码:
import express from "express";
import getCategories, createCategory from "../controllers/category.js";
import authMiddleware, authAdminMiddleware from "../middlewares/auth.js";
const router = express.Router();
router.route("/get-categories").get(getCategories);
router
.route("/create-category")
.post(authMiddleware, authAdminMiddleware, createCategory);
export default router;
用于身份验证的中间件:
import jwt from "jsonwebtoken";
import dotenv from "dotenv";
import User from "../models/User.js";
dotenv.config();
const JWT_SECRET = process.env.JWT_SECRET;
export const authMiddleware = async (req, res, next) =>
try
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
)
token = req.headers.authorization.split(" ")[1];
if (!token)
return res.status(401).json( errorMessage: "Invalid Authentication" );
const decoded = jwt.verify(token, JWT_SECRET);
const user = await User.findById(decoded.user._id);
if (!user)
return res.status(404).json( errorMessage: "No user found" );
req.user = user;
next();
catch (error)
return res.status(500).json( errorMessage: "Not authorized" );
;
export const authAdminMiddleware = async (req, res, next) =>
try
const user = await User.findOne(
_id: req.user.id,
);
if (req.user.role === 0)
return res
.status(400)
.json( errorMessage: "Not Authorized. Admin private route." );
next();
catch (error)
return res
.status(401)
.json( errorMessage: "Not Authorized. Admin private route." );
;
POST 请求控制器:
import Category from "../models/Category.js";
import slugify from "slugify";
export const createCategory = async (req, res) =>
const name, parentId = req.body;
try
if (!name)
return res
.status(400)
.json( errorMessage: "Category name field is required." );
const category = await Category.findOne( name );
if (category)
return res.status(409).json( errorMessage: "Category already exist." );
let id;
if (parentId)
const findParentId = await Category.findOne( name: parentId );
if (findParentId)
id = findParentId._id;
const newCategory = new Category(
name,
slug: slugify(name),
parentId: id,
);
newCategory.save((err, saved) =>
if (err)
return res.status(400).json( errorMessage: err.message );
if (saved)
return res
.status(201)
.json( successMessage: "Category created successfully!" );
);
catch (error)
res.status(500).json( errorMessage: error.message );
;
前端的 API 调用:
import axios from "axios";
import getCookies from "../helpers/storage&cookies/storage&cookies";
console.log(getCookies("token"));
const config =
headers:
"Content-Type": "application/json",
Authorization: "Bearer " + getCookies("token"),
,
;
export const postCategory = async (data) =>
const response = await axios.post(
"http://localhost:5020/api/categories/create-category",
data,
config
);
return response;
;
具体的客户端发布请求:
import React, useState from "react";
import CreateCategoryModalUI from "./CreateCategoryModalUI";
import postCategory from "../../../api/category";
const CreateCategoryModal = ( categories ) =>
const [createCategoryData, setCreateCategoryData] = useState(
name: "",
parentId: "",
errorMessage: "",
successMessage: "",
loading: false,
);
const name, parentId, successMessage, errorMessage, loading =
createCategoryData;
const handleChange = (e) =>
if (e.target.type == "text")
setCreateCategoryData(
...createCategoryData,
name: e.target.value,
successMessage: "",
errorMessage: "",
);
else
setCreateCategoryData(
...createCategoryData,
parentId: e.target.value,
errorMessage: "",
successMessage: "",
);
;
const handleSubmit = (e) =>
e.preventDefault();
if (!name)
setCreateCategoryData(
...createCategoryData,
errorMessage: "Category name field is required.",
);
else
const data = name, parentId ;
console.log(data);
setCreateCategoryData(
...createCategoryData,
loading: true,
);
postCategory(data)
.then((response) =>
setCreateCategoryData(
name: "",
parentId: "",
errorMessage: "",
successMessage: response.data.successMessage,
loading: false,
);
)
.catch((error) =>
if (error.response.status === 409)
setCreateCategoryData(
...createCategoryData,
errorMessage: "Category already exists",
loading: false,
);
else
setCreateCategoryData(
...createCategoryData,
errorMessage: error.toString(),
loading: false,
);
);
;
return (
<CreateCategoryModalUI
createCategoryData=createCategoryData
categories=categories
handleChange=handleChange
handleSubmit=handleSubmit
/>
);
;
export default CreateCategoryModal;
我该如何解决这个问题? 非常感谢!
【问题讨论】:
使用您的浏览器开发工具 Network 面板检查请求。Authorization
标头中是否有正确的不记名令牌?此外,如果您使用 cors
包,则无需手动设置 CORS 响应标头
是的,我已经检查过了:授权标头是正确的。此外,我还手动设置了 CORS 响应标头,因为对于另一个 POST 请求,我遇到了类似的问题,通过添加这两行代码莫名其妙地解决了。
【参考方案1】:
这是一个评论,但我没有足够的声誉来这样做,这就是我发布答案的原因。
您可以尝试在创建 Axios 实例时将 withCredentials 设置为 true,withCredentials 根据 Axios 存储库执行以下操作:
withCredentials
表示是否跨站访问控制请求 应该使用凭据制作
官方文档没有添加任何进一步的解释...
axios.create( withCredentials: true, )
【讨论】:
OP 正在使用不需要withCredentials
的不记名令牌
谢谢,我也尝试使用 withCredentials: true,但尽管将“Access-Control-Allow-Origin”设置为我的客户端本地主机地址,但我收到此错误:“访问 XMLHttpRequest at . .. from origin ...已被 CORS 策略阻止:对预检请求的响应未通过访问控制检查:响应中的“Access-Control-Allow-Origin”标头的值不能是通配符“* ' 当请求的凭证模式为 'include' 时。由 XMLHttpRequest 发起的请求的凭证模式由 withCredentials 属性控制。"【参考方案2】:
谢谢大家,我解决了!这个问题,正如我经常发生的那样,是一个疏忽。具体来说,在身份验证中间件中。 在这些代码行中:
const user = await User.findById (decoded.user._id)
我在“解码”后添加了一个多余的“用户”,但对象键只是“_id”。
【讨论】:
以上是关于Axios 发布请求适用于 Postman,但不适用于浏览器(它返回“错误 401-未授权”)的主要内容,如果未能解决你的问题,请参考以下文章
POST 请求适用于 Postman,但不适用于 axios.post()
使用 JWT 访问 Rest API 适用于 Postman 但不适用于 Axios
GET 请求适用于 Postman,但为啥它不适用于 ReactJS fetch?