虚拟字段装饰器
Express-Cargo 提供用于定义虚拟字段和请求派生字段的装饰器。这些装饰器允许你动态计算值,或将 Request 中的数据直接映射到对象属性。
内置虚拟装饰器
@Virtual<T>(transformer: (obj: object) => T)
@Virtual 装饰器定义一个计算属性,该属性并不直接来自请求。它的值由对象的其他属性派生而来。
transformer:接收对象实例并返回计算值的函数。
@Request<T>(transformer: (req: Request) => T)
@Request 装饰器将 Express Request 对象中的值映射到类属性。
transformer:接收 Request 对象并返回要绑定值的函数。
@Request 会按原样赋值,不执行内置类型转换。一个常见用法是绑定 Passport.js 等认证中间件设置在 req.user 上的用户对象。
如何选择
@Virtual 和 @Request 属于两个不同的分类,一个字段只能带其中一个。同时使用二者会在注册路由时报出 @Request cannot be combined with @Virtual,而把其中任意一个与 @Body 这样的来源装饰器搭配也会被同样拒绝——三者回答的是同一个问题:字段的值从哪里来。
- 当值位于原始
Request对象上,且需要在对象自身字段绑定之前取得时,使用@Request。 - 当值由正在构建的对象的其他字段推导得出时,使用
@Virtual。
@Virtual 在所有来源字段绑定完成之后执行,因此无论这些字段以什么顺序声明都能读取到。只有依赖另一个 @Virtual 字段的 @Virtual 字段才对顺序敏感。
使用示例
import express from 'express'
import passport from 'passport'
import { Strategy as BearerStrategy } from 'passport-http-bearer'
import { Body, Virtual, Request, bindingCargo, getCargo } from 'express-cargo'
class OrderExample {
@Body('price')
price!: number
@Body('quantity')
quantity!: number
// 请求中不存在的计算字段
@Virtual((obj: OrderExample) => obj.price * obj.quantity)
total!: number
}
class PassportExample {
@Request<object>(req => req.user!)
user!: object
}
const EXAMPLE_TOKEN = 'express-cargo-token'
passport.use(
new BearerStrategy((token, done) => {
if (token !== EXAMPLE_TOKEN) {
return done(null, false)
}
return done(null, { id: 'test-user-id', role: 'admin' })
}),
)
const app = express()
app.use(express.json())
app.use(passport.initialize())
app.post('/orders', bindingCargo(OrderExample), (req, res) => {
const orderData = getCargo<OrderExample>(req)
res.json({
message: 'Order data processed with virtual fields!',
data: orderData,
})
})
app.get('/passport', passport.authenticate('bearer', { session: false }), bindingCargo(PassportExample), (req, res) => {
const cargo = getCargo<PassportExample>(req)
res.json(cargo)
})
Passport 认证必须在 bindingCargo() 之前运行。认证成功后,Passport 会设置 req.user,随后 @Request<object> 将该对象绑定到 PassportExample.user。如果凭据缺失或无效,Passport 会在 cargo 绑定前拒绝请求。
curl 'http://localhost:3000/passport' \
-H 'Authorization: Bearer express-cargo-token'
{
"user": {
"id": "test-user-id",
"role": "admin"
}
}
使用 object 可使示例不依赖特定的用户模型。如果应用代码需要访问 user.id 等属性,请将 object 替换为具体的用户类型,并将该类型传给 @Request<T>。