Node.js表示空URL参数



我需要为我在Node.js express应用程序中支持的api定义路径。
我正面临一个与接收URL变量相关的问题。

// app.js
const bodyParser = require('body-parser');
const express = require('express');
const addressRouter = require('./routes/address-router');
const http = require('http');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.set('port', 8081);
app.use((req, res, next) => {
res.status(404).send({
error: 'path not found',
});
});
app.use('/v1/users/:userId/zip/:zipNumber/address', addressRouter);
const server = http.createServer(app);
server.on('error', onError);
server.on('listening', onListening);
server.listen(8081);
// addressRouter.js
const express = require('express');
const router = express.Router();
router.get('/zip-get', async (req, res, next) => {
console.log('GET: /zip-get: req: ', req);
res.send('Done');
});
module.exports = router;
我正在调用的API:
curl http://localhost:8081/v1/users/123-456/zip/1234567/address/zip-getaddressRouter.js中,当我打印收到的请求时,我得到:
params: {}

baseUrl: '/v1/users/:userId/zip/:zip/address',
originalUrl: '/v1/users/:userId/zip/:zip/address/zip-get',


在请求对象中,我看到以下字段:这是否表明传入请求尚未结束?
ReadableState {
objectMode: false,highWaterMark: 16384,buffer: BufferList{头:null,尾:null,长度:0},长度:0,管道:[],流动:空,结束:假的,endEmitted:假的,阅读:假的,同步:没错,needReadable:假的,emittedReadable:假的,readableListening:假的,resumeScheduled:假的,errorEmitted:假的,emitClose:没错,autoDestroy:假的,毁:假的,错误:无效,关闭:假的,closeEmitted:假的,defaultEncoding:"use utf8",awaitDrainWriters:空,multiAwaitDrain:假的,readingMore:没错,dataEmitted:假的,解码器:空,编码:空,[符号(kPaused)]:空


}我不知道是什么原因引起的。

这里有几个问题:根据如何处理404的官方快速文档,您需要移动404处理程序。Express对路由的评估是"自顶向下"的,所以你的404处理程序目前对所有的都使用

同样,不需要http模块,express是一个web服务器,它已经给你app.listen来启动服务器。

但是最重要的是路由器是它们自己的东西。通过使用它们,你创建了一个与应用的其余部分隔离的路由上下文,其中包括拥有自己的params对象,该对象将包含路由器本身定义的参数,所以因为你的/zip-get路由没有参数,req.params对象将在路由器的中间件链中为空。

然而,在app.use中间件链中,它有一个路径,其中确实有参数,req.params具有您期望的所有值,因此:将您需要的值从req.params复制到res.locals作为app.use中间件链的一部分,以便任何下游中间件(包括下游路由器)都可以访问它们。

import express from "express";
const app = express();
const router = express.Router();
router.get('/zip-get', (req, res) => {
console.log(`zip-get stored values:`, res.locals);
res.send('Done');
});
function copyParams(req, res, next) {
res.locals = {...req.params};
console.log(`app level params:`, req.params);
next();
}
app.use('/v1/users/:userId/zip/:zipNumber/address', copyParams, router);
// This has to come last.
app.use((req, res, next) => {
res.status(404).json({
error: 'path not found',
});
});
app.listen(8081, () => console.log(` http://localhost:8081`));

或者,你可以告诉路由器,当你调用new Router时,它应该保留参数,通过传递mergeParams: true作为一个选项,但是你需要问自己为什么要使用路由器:express基于使用中间件链来完成"基于当前上下文的单个工作"的概念,因此任何依赖于将:userId:zipNumber转化为实际事物的代码都应该在您遇到路由器中间件之前启动,中间数据存储在res.locals中。按照您编写的方式,/zip-get不应该对这些参数做任何操作,因为它不对它们负责。

相反,根据需要组织API,例如,有一个真正最小的主文件:

import express from "express";
import userRoutes from "./routes/user.js";
const app = express();
app.use('/v1/users', userRoutes);
app.use((_req, res, _next) => {
res.status(404).json({
error: 'path not found',
});
});
app.listen(8081, () => console.log(` http://localhost:8081`));

用户路由负责用户,只有用户:

import express from "express";
import db from "./somewhere-or-other.js";
import zipRoutes from "./zip.js";
...
function findUser(req, res, next) {
db.getUserById(req.params.userId, (err, user) => {
if (errr) {
return next(err);
}
res.locals.user = user;
next();
});
}
const userRoutes = express.Router();
userRoutes.use('/:userId/zip', findUser, zipRoutes);
userRoutes.use('/:userId/something-else', findUser, someOtherRoutes);
export default userRoutes;

使用单独的zip routes处理程序:

import express from "express";
import db from "./somewhere-or-other.js";
...
function getZipObject(req, res, next) {
zipManager.getZipObject(req.params.zipNumber, (err, zipData) => {
if (errr) {
return next(err);
}
res.locals.zip = zipData;
next();
});
}
const zipRoutes = express.Router();
zipRoutes.get('/:zipNumber/address/zip-get', getZipObject, (req, res) => {
// Render the page for this route, with res.locals as its context
// so that the page template can access the user and zip data.
res.render(`zip-get.html`, res.locals);
});
export default zipRoutes;

并且注意,我们在一个函数中结束,该函数除了将适当的模板呈现为页面响应之外,什么也不做。当我们到达最终的响应函数时,所有需要发生的工作都应该已经完成,并转化为可以直接传递给模板渲染调用的res.local数据。

是的,您可以按照要求将路径/users/:userId/zip/:zip/移动到addressRouter.js路由器的路径上。这是可行的,因为通过默认每个路由器都可以访问在其自己的路径中定义的req.params

另一个选项是使用express.Router()函数调用的可选对象,它具有mergeParams的属性并传递true的值-这将允许您保留父路由器的req.params值。

addressRouter.js修改为

const express = require('express');
// Here pass in the mergeParams property set to true
const router = express.Router({ mergeParams: true });
router.get('/zip-get', async (req, res, next) => {
console.log('GET: /zip-get: req: ', req);
res.send('Done');
});
module.exports = router;