我正在尝试使用typescript在中间件中添加一个表示请求对象的属性。但是我无法弄清如何向该对象添加额外属性。如果可能,我希望不使用方括号表示。
我正在寻找一种解决方案,使我可以编写类似于以下内容的代码(如果可能):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
我正在尝试使用typescript在中间件中添加一个表示请求对象的属性。但是我无法弄清如何向该对象添加额外属性。如果可能,我希望不使用方括号表示。
我正在寻找一种解决方案,使我可以编写类似于以下内容的代码(如果可能):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
我使用response.locals
对象来存储新属性。以下是完整代码:
export function testmiddleware(req: Request, res: Response, next: NextFunction) {
res.locals.user = 1;
next();
}
// Simple Get
router.get('/', testmiddleware, async (req: Request, res: Response) => {
console.log(res.locals.user);
res.status(200).send({ message: `Success! ${res.locals.user}` });
});
对于我来说有效的简单解决方案是创建一个扩展express Request的新自定义接口。该接口应包含所有自定义req属性(可选)。将此接口设置为中间件请求的类型。
// ICustomRequset.ts
import { Request } from "express"
export default interface ICustomRequset extends Request {
email?: string;
roles?: Array<string>;
}
// AuthMiddleware.ts
...
export default async function (
req: ICustomRequset,
res: Response,
next: NextFunction
) {
try {
// jwt code
req.email=jwt.email
req.roles=jwt.roles
next()
}catch(err){}
将COMMENT放在"declare global"中,像这样:
declare global {
declare module 'express-serve-static-core' {
interface Request {
userId?: string;
}
}
}
文件结构:
/typeDeclarations/express/index.d.ts
/tsconfig.json
我还将我的声明路径添加到了tsconfig文件中,但是即使没有它一切也能正常工作。
"typeRoots": [
"./node_modules/@types",
"./typeDeclarations"
],
可能已经太晚了,但是无论如何,这就是我解决问题的方法:
tsconfig
文件中包含了你的类型源(这可能需要一个全新的线程)express
的目录express
目录中创建一个文件,并将其命名为 index.d.ts
(必须完全一样)declare module 'express' {
export interface Request {
property?: string;
}
}
import { Request } from "express";
declare global {
namespace Express {
export interface Session {
passport: any;
participantIds: any;
uniqueId: string;
}
export interface Request {
session: Session;
}
}
}
export interface Context {
req: Request;
user?: any;
}```
最简单的方法是扩展您想要的类型并添加自己的属性
在 tsconfig.ts 中指定本地类型的根目录
{
// compilerOptions:
"typeRoots": ["node_modules/@types", "**/@types"],
}
现在在@types文件夹中创建任何.d.ts文件,您可以将@types放在根目录或任何地方。
@types/express.d.ts
declare namespace Express {
interface Request {
// add arbitrary keys to the request
[key: string]: any;
}
}
headers
属性,并稍后在内层中间件中获取它。// outer middleware
req.headers["custom_id"] = "abcxyz123";
// inner middleware
req.get("custom_id");
缺点:
string
。如果您想存储其他类型,如json
或number
,可能需要稍后解析。headers
属性未经文档化。Express仅记录req.get()
方法。因此,您必须使用与属性headers
兼容的确切版本的Express。declare namespace e {
export interface Request extends express.Request {
user:IUserReference,
[name:string]:any;
}
export interface Response extends express.Response {
[name:string]:any;
}
}
export type AsyncRequestHandler = (req:e.Request, res:e.Response, logger?:Logger) => Promise<any>|Promise<void>|void;
export type AsyncHandlerWrapper = (req:e.Request, res:e.Response) => Promise<void>;
app.post('some/api/route', asyncHandlers(async (req, res) => {
return await serviceObject.someMethod(req.user, {
param1: req.body.param1,
paramN: req.body.paramN,
///....
});
}));
以下是可能的选项:
async function getRequestUser(req:Request):Promise<ICustomUser> {
let currentUser:ICustomUser = req[UserSymbolProp] || null as ICustomUser;
// if user not set already, try load it, if no such user but header present, throw error?
return currentUser;
}
app.get('/api/get/something', async(req, res, next) => {
try {
let user = await getRequestUser(req);
//do something else
} catch(err) {
next(err);
}
});
function getAPIContext(req:Request):IAPIContext {
let ctx = req[APICtxSymbol] || null as IApiContext;
if (!ctx) {
ctx = prepareAPIContext(req);
req[APICtxSymbol] = ctx;
}
return ctx;
}
app.get('/api/to/get/something', async(req, res, next) => {
try {
let ctx = getAPIContext(req);
///use context somehow
let reply = await doSomething(ctx);
res.json(reply);
} catch(err) {
next(err);
}
}
第二种方法更好,因为您可以创建使用测试特殊上下文实现的单元测试,并直接在 doSomething 上进行单元测试(当然要导出该代码)。 第二个结构可以通过类似 wrapHandler 的函数重复使用,该函数接受真实的处理函数。
function wrapHandler<T>(handler: (req:IAPIContext) => Promise<T>|T) => (req:Request, res:Response, next:NextFunction) => Promise<void>|void;
AsyncRequestHandler
和 AsyncHandlerWrapper
类型有什么作用吗?它们被声明了但在你的示例中没有被使用。 - devklickasyncHandlers
,因此我将这些类型作为使用上下文的一部分进行了复制,以确保req和res被视为express Request和Response类型,而不是DOM fetch Request和Response类型。如果您使用原始的express,请在处理程序函数参数声明中明确指定req
和res
的类型,以确保接口合并按预期工作。 - Kote Isaeve
和命名空间 Express
之间有什么区别?因为在 DefinitelyTyped 中,似乎 @types/express
使用了 e
,而 @types/express-serve-static-core
使用了 Express
,但我无法确定具体的区别是什么。 - damd"@types/express": "^4.17.17"
"typescript": "^5.1.6"
我的目标是将__platform
添加为req对象的属性,并定义req.body和req.query的类型。以下是我想要使用的具体代码。import Request from 'express';
interface QueryProps{
username: string;
}
interface BodyProps{
witdh: number;
height: number;
}
type IRequest = Request<object,object,BodyProps,QueryProps>
function testHandler(req:IRequest,res,next){
req.__platform // no error
req.query.username // no error
req.body.with // no error
req.body.height // no error
}
// index.d.ts
import { Request as ExpressRequest } from 'express';
declare module 'express' {
interface Request extends ExpressRequest {
__platform: string;
}
}
declare module 'express-serve-static-core' {
interface Request {
__platform: string;
}
}
我已经将响应类型更改为包括 ApiResponse(自定义响应对象)Response<ApiResponse>
export interface ApiResponse {
status?: string
error?: string
errorMsg?: string
errorSubject?: string
response?: any
}
const User = async (req: Request, res: Response<ApiResponse>, next: NextFunction) => {
try {
if (!username) return res.status(400).send({ errorMsg: 'Username is empty' })
/* ... */
} catch(err){
/* ... */
}
}