我试图添加一个属性来表达使用typescript从中间件请求对象。但是,我不知道如何向对象添加额外的属性。如果可能的话,我宁愿不用括号。
我正在寻找一个解决方案,允许我写类似的东西(如果可能的话):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
我试图添加一个属性来表达使用typescript从中间件请求对象。但是,我不知道如何向对象添加额外的属性。如果可能的话,我宁愿不用括号。
我正在寻找一个解决方案,允许我写类似的东西(如果可能的话):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
当前回答
对于简单的情况,我在外部中间件中使用headers属性,然后在内部中间件中获得它。
// outer middleware
req.headers["custom_id"] = "abcxyz123";
// inner middleware
req.get("custom_id");
缺点:
只能存储字符串。如果您想存储其他类型,如json或number,您可能必须稍后解析它。 headers属性没有文档化。Express只记录了req.get()方法。因此,您必须使用与属性头文件一起工作的Express的确切版本。
其他回答
我也有同样的问题,我是这样解决的:
// /src/types/types.express.d.ts
declare namespace Express {
export interface Request {
user: IUser
}
}
但有一些条件是必须的!
添加到tsconfig。json配置
"paths": {
"*": [
"node_modules/*",
"src/types/*"
]
},
在此之后,tsc将构建bundle,而ts-node则不会。
必须在编译器命令中添加——files
ts-node --files src/server.ts
你想要创建一个自定义定义,并使用Typescript中称为声明合并的特性。这是常用的,例如在方法重写中。
创建一个custom.d.ts文件,并确保将其包含在tsconfig中。Json的文件节(如果有的话)。内容如下所示:
declare namespace Express {
export interface Request {
tenant?: string
}
}
这将允许你在代码的任何地方使用这样的东西:
router.use((req, res, next) => {
req.tenant = 'tenant-X'
next()
})
router.get('/whichTenant', (req, res) => {
res.status(200).send('This is your tenant: '+req.tenant)
})
.d。Ts声明是黑客。简单地接受这样一个事实:express的中间件系统不适合typescript。所以不要用它。
错误代码示例:
const auth = (req) => {
const user = // parse user from the header
if(!user)
return res.status(401).send({ result: 'unauthorized-error' })
req.user = user
return next()
}
app.get('/something', auth, (req, res) => {
// do something
})
更好的代码:
const auth = (req) => {
return // parse user from the header
}
app.get('/something', (req, res) => {
const user = auth(req)
if(!user)
return res.status(401).send({ result: 'unauthorized-error' })
// do something
})
你可以使用更高阶的函数来恢复中间件的使用:
Const auth = (req) => { 从头文件返回// parse user } const withUser =(回调:(foo, req, res) => void) => (req, res) => { Const user = auth(req) 如果用户(!) 返回res.status(401)。发送({result: 'unauthorized-error'}) 返回回调(user, req, res) } app.get('/something', withUser((user, req, res) => { //做某事 }))
如果你愿意,你甚至可以把它们堆叠起来:
const withFoo = (callback) => (req, res) => { /* ... */ } const withBar = (callback) => (req, res) => { /* ... */ } const withBaz = (callback) => (req, res) => { /* ... */ } const withFooBarAndBaz = (callback) => (req,res) => { withFoo((foo) => withBar((bar) => withBaz((baz) => callback({ foo, bar, baz }, req, res) )(req,res) )(req,res) )(req,res) } app.get('/something', withFooBarAndBaz(({ foo, bar, baz }, req, res) => { // do something with foo, bar and baz }))
只要使用语言,而不是表达促进的不安全突变。
编辑:我使用第一个建议的解决方案。但不同的是,我的auth函数抛出了一个错误,我可以捕捉并返回正确的响应,所以我不需要在控制器中这样做。例如:
app.get('/something', withResponse((req) => {
const user = auth(req)
return success({
message: `Hi ${user.name}`
})
}))
我还发送返回类型,而不是手动调用res.send。它还允许我输入响应。我建议你也去查查tRPC。
公认的答案(和其他答案一样)对我不适用,但是
declare module 'express' {
interface Request {
myProperty: string;
}
}
所做的。希望这能帮助到一些人。
可选择的解决方案
这实际上并没有直接回答这个问题,但我提供了一个替代方案。我也在与同样的问题作斗争,并尝试了该页面上几乎所有的界面扩展解决方案,但没有一个有效。
这让我停下来思考:“为什么我实际上在修改请求对象?”
使用response.locals
Express开发人员似乎认为用户可能希望添加自己的属性。这就是为什么有一个locals对象。问题是,它不在请求中,而是在响应对象中。
响应。Locals对象可以包含您可能希望拥有的任何自定义属性,这些属性封装在请求-响应周期中,因此不会暴露给来自不同用户的其他请求。
需要存储userId?只需设置response.locals.userId = '123'。没有必要纠结于打字。
它的缺点是,您必须传递响应对象,但很可能您已经这样做了。
https://expressjs.com/en/api.html#res.locals
打字
另一个缺点是缺乏类型安全性。然而,你可以在Response对象上使用泛型类型来定义body和locals对象的结构:
响应< MyResponseBody, MyResponseLocals >
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L127
警告
你不能保证userId属性确实在那里。您可能希望在访问它之前进行检查,特别是在对象的情况下。
使用上面的例子来添加一个userId,我们可以有这样的东西:
interface MyResponseLocals {
userId: string;
}
const userMiddleware = (
request: Request,
response: Response<MyResponseBody, MyResponseLocals>,
next: NextFunction
) => {
const userId: string = getUserId(request.cookies.myAuthTokenCookie);
// Will nag if you try to assign something else than a string here
response.locals.userId = userId;
next();
};
router.get(
'/path/to/somewhere',
userMiddleware,
(request: Request, response: Response<MyResponseBody, MyResponseLocals>) => {
// userId will have string type instead of any
const { userId } = response.locals;
// You might want to check that it's actually there
if (!userId) {
throw Error('No userId!');
}
// Do more stuff
}
);