NodeJS+Express服务GET/POST等请求返回404故障求助
NodeJS + Express 接口全请求返回404问题排查修复
问题场景
基于NodeJS + Express搭建接口服务时,已完成/api/goals路径下GET、POST、PUT、DELETE请求的路由与控制器绑定,服务启动无报错,但Postman测试所有匹配该路径的请求均返回404状态码。
排查确认控制器文件已正确导出、路由文件已正确导入控制器,服务运行无模块加载相关错误,不希望回退到将处理逻辑直接写在路由回调中的冗余写法。
相关代码
server.js(服务入口文件)
const express = require('express') const dotenv = require('dotenv').config() const port = process.env.PORT ||9999 const goals = require('./routes/goalRoutes') const app = express() app.use('/api/goals', goals) app.listen(port, () => console.log(`Server started on port ${port}`))
goalRoutes.js(路由定义文件)
用于定义/api/goals路径下的接口路由规则:
const express = require('express') const router = express.Router() const { createGoal, getGoals, updateGoal, deleteGoal, } = require('../controllers/goalController') router.route('/').post(createGoal).get(getGoals) router.route('/:id').put(updateGoal).delete(deleteGoal) module.exports = router
goalController.js(业务控制器文件)
用于实现各路由对应的业务处理逻辑:
//@desc Create goal //@route POST /api/goals //@access Private const createGoal = (err, req, res, next) => { res.status(200).json({message: "Create goal"}) } //@desc Get goals //@route GET /api/goals //@access Private const getGoals = (err, req, res, next) => { res.status(200).json({message: "Read goals"}) } //@desc Update goal //@route PUT /api/goals:id //@access Private const updateGoal = (err, req, res, next) => { res.status(200).json({message: `Update this goal: ${req.params.id}`}) } //@desc Get goals //@route DELETE /api/goal/:id //@access Private const deleteGoal = (err, req, res, next) => { res.status(200).json({message: `Delete this goal: ${req.params.id}`}) } module.exports = { createGoal, getGoals, updateGoal, deleteGoal, }
问题根因
所有控制器函数的参数定义不符合Express路由回调的规范:
Express中只有错误处理中间件的回调函数才需要将err作为第一个参数,固定格式为(err, req, res, next);普通路由处理回调的第一个参数是请求对象req,第二个是响应对象res,第三个是next函数(可选),格式为(req, res, next)。
当前代码给所有普通路由回调错误添加了第一个err形参,Express无法将这些函数识别为有效的路由处理逻辑,相当于路由上没有绑定可执行的处理函数,请求进入后找不到匹配的处理逻辑,直接返回404。
修复方案
- 修改所有控制器函数的参数定义,移除错误添加的
err形参,不需要调用next方法时可直接省略next参数,仅保留(req, res)即可。修复后的控制器代码如下:
//@desc Create goal //@route POST /api/goals //@access Private const createGoal = (req, res) => { res.status(200).json({message: "Create goal"}) } //@desc Get goals //@route GET /api/goals //@access Private const getGoals = (req, res) => { res.status(200).json({message: "Read goals"}) } //@desc Update goal //@route PUT /api/goals/:id //@access Private const updateGoal = (req, res) => { res.status(200).json({message: `Update this goal: ${req.params.id}`}) } //@desc Delete goal //@route DELETE /api/goals/:id //@access Private const deleteGoal = (req, res) => { res.status(200).json({message: `Delete this goal: ${req.params.id}`}) } module.exports = { createGoal, getGoals, updateGoal, deleteGoal, }
- 在server.js的路由挂载代码前添加请求体解析中间件,否则POST、PUT请求无法正常读取JSON或表单格式的请求参数:
// 加在app初始化后、路由挂载前 app.use(express.json()) app.use(express.urlencoded({ extended: false })) app.use('/api/goals', goals)
- 接口测试注意:PUT、DELETE请求的路径需要携带id参数,正确格式为
/api/goals/[id值](例如/api/goals/1),不要漏写id和路径之间的斜杠。
内容的提问来源于stack exchange,提问作者Gustavo Araiza
相关产品推荐
相关产品推荐

