![](/img/trans.png)
[英]extend express request object with sequelize model using typescript 2
[英]Extend Express Request object using Typescript
我正在嘗試添加一個屬性以使用 typescript 從中間件表達請求 object。 但是我不知道如何向 object 添加額外的屬性。 如果可能的話,我寧願不使用括號表示法。
我正在尋找一種解決方案,可以讓我寫出類似的東西(如果可能的話):
app.use((req, res, next) => {
req.property = setProperty();
next();
});
您想創建一個自定義定義,並使用 Typescript 中名為Declaration Merging的功能。 這是常用的,例如在method-override
中。
創建一個文件custom.d.ts
並確保將其包含在您的tsconfig.json
的files
-section(如果有)中。 內容可以如下所示:
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)
})
正如index.d.ts
中的注釋所建議的,您只需向全局Express
命名空間聲明任何新成員。 例子:
declare global {
namespace Express {
interface Request {
context: Context
}
}
}
import * as express from 'express';
export class Context {
constructor(public someContextVariable) {
}
log(message: string) {
console.log(this.someContextVariable, { message });
}
}
declare global {
namespace Express {
interface Request {
context: Context
}
}
}
const app = express();
app.use((req, res, next) => {
req.context = new Context(req.url);
next();
});
app.use((req, res, next) => {
req.context.log('about to return')
res.send('hello world world');
});
app.listen(3000, () => console.log('Example app listening on port 3000!'))
TypeScript Deep Dive 還介紹了擴展全局命名空間。
對於較新版本的 express,您需要擴充express-serve-static-core
模塊。
這是必需的,因為現在 Express 對象來自那里: https ://github.com/DefinitelyTyped/DefinitelyTyped/blob/8fb0e959c2c7529b5fa4793a44b41b797ae671b9/types/express/index.d.ts#L19
基本上,使用以下內容:
declare module 'express-serve-static-core' {
interface Request {
myField?: string
}
interface Response {
myField?: string
}
}
在嘗試了 8 個左右的答案並且沒有成功之后。 我終於設法讓它與jd291指向3mards repo的評論一起工作。
在名為types/express/index.d.ts
的基礎中創建一個文件。 並在其中寫道:
declare namespace Express {
interface Request {
yourProperty: <YourType>;
}
}
並將其包含在tsconfig.json
中:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
然后yourProperty
應該在每個請求下都可以訪問:
import express from 'express';
const app = express();
app.get('*', (req, res) => {
req.yourProperty =
});
接受的答案(與其他人一樣)對我不起作用,但
declare module 'express' {
interface Request {
myProperty: string;
}
}
做過。 希望這會幫助某人。
提供的解決方案都不適合我。 我最終只是簡單地擴展了 Request 接口:
import {Request} from 'express';
export interface RequestCustom extends Request
{
property: string;
}
然后使用它:
import {NextFunction, Response} from 'express';
import {RequestCustom} from 'RequestCustom';
someMiddleware(req: RequestCustom, res: Response, next: NextFunction): void
{
req.property = '';
}
編輯:根據您的 tsconfig,您可能需要這樣做:
someMiddleware(expressRequest: Request, res: Response, next: NextFunction): void
{
const req = expressRequest as RequestCustom;
req.property = '';
}
這實際上並沒有直接回答這個問題,但我提供了一個替代方案。 我在同一個問題上苦苦掙扎,並嘗試了此頁面上的幾乎所有接口擴展解決方案,但都沒有奏效。
這讓我停下來思考: “為什么我實際上要修改請求對象? ” 。
response.locals
Express 開發人員似乎認為用戶可能想要添加自己的屬性。 這就是為什么有一個locals
對象。 問題是,它不在request
中,而是在response
對象中。
response.locals
對象可以包含您可能想要的任何自定義屬性,封裝在請求-響應周期中,因此不會暴露給來自不同用戶的其他請求。
需要存儲用戶 ID? 只需設置response.locals.userId = '123'
。 無需為打字而苦苦掙扎。
它的缺點是您必須傳遞響應對象,但很可能您已經在這樣做了。
https://expressjs.com/en/api.html#res.locals
另一個缺點是缺乏類型安全性。 但是,您可以使用 Response 對象上的泛型類型來定義body
和locals
對象的結構:
Response<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
}
);
所有這些反應似乎都以某種方式錯誤或過時。
這在 2020 年 5 月對我有用:
在${PROJECT_ROOT}/@types/express/index.d.ts
:
import * as express from "express"
declare global {
namespace Express {
interface Request {
my_custom_property: TheCustomType
}
}
}
在tsconfig.json
中,添加/合並屬性,使得:
"typeRoots": [ "@types" ]
干杯。
在 2021 年,這一項正在發揮作用:
使用 express 4.17.1, https://stackoverflow.com/a/55718334/9321986 和 https://stackoverflow.com/a/58788706/9321986的組合起作用:
在types/express/index.d.ts
:
declare module 'express-serve-static-core' {
interface Request {
task?: Task
}
}
在tsconfig.json
中:
{
"compilerOptions": {
"typeRoots": ["./types"]
}
}
雖然這是一個非常古老的問題,但我最近偶然發現了這個問題。接受的答案工作正常,但我需要向Request
添加一個自定義接口——我在我的代碼中一直使用的接口,它與接受的答案。 從邏輯上講,我試過這個:
import ITenant from "../interfaces/ITenant";
declare namespace Express {
export interface Request {
tenant?: ITenant;
}
}
但這不起作用,因為 Typescript 將.d.ts
文件視為全局導入,並且當它們具有導入時,它們被視為普通模塊。 這就是為什么上面的代碼不適用於標准打字稿設置的原因。
這就是我最終做的
// typings/common.d.ts
declare namespace Express {
export interface Request {
tenant?: import("../interfaces/ITenant").default;
}
}
// interfaces/ITenant.ts
export interface ITenant {
...
}
這就是在使用 Nestjs 和 Express 時對我有用的方法。 截至 2020 年 11 月。
創建一個文件:./@types/express-serve-static-core/index.d.ts
注意:必須與上述路徑和文件名完全相同。 這樣 Typescript 聲明合並將起作用。
import { UserModel } from "../../src/user/user.model";
declare global{
namespace Express {
interface Request {
currentUser: UserModel
}
}
}
將此添加到您的 tsconfig.json
"typeRoots": [
"@types",
"./node_modules/@types",
]
在 TypeScript 中,接口是開放式的。 這意味着您可以通過重新定義它們從任何地方向它們添加屬性。
考慮到您正在使用這個express.d.ts文件,您應該能夠重新定義Request接口以添加額外的字段。
interface Request {
property: string;
}
然后在你的中間件函數中, req參數也應該有這個屬性。 您應該能夠在不更改代碼的情況下使用它。
如果您正在尋找與 express4 一起使用的解決方案,這里是:
@types/express/index.d.ts:--------必須是/index.d.ts
declare namespace Express { // must be namespace, and not declare module "Express" {
export interface Request {
user: any;
}
}
tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"typeRoots" : [
"@types", // custom merged types must be first in a list
"node_modules/@types",
]
}
}
參考來自https://github.com/TypeStrong/ts-node/issues/715#issuecomment-526757308
這個答案將對那些依賴 npm package ts-node
的人有益。
我也在為擴展請求對象而苦苦掙扎,我在堆棧溢出中遵循了很多答案,並以遵循下面提到的策略結束。
我在以下目錄中聲明了express的擴展類型。 ${PROJECT_ROOT}/api/@types/express/index.d.ts
declare namespace Express {
interface Request {
decoded?: any;
}
}
然后將我的tsconfig.json
更新為這樣的內容。
{
"compilerOptions": {
"typeRoots": ["api/@types", "node_modules/@types"]
...
}
}
即使做了以上步驟,visual studio 也不再報錯了,可惜ts-node
編譯器還是習慣了拋出。
Property 'decoded' does not exist on type 'Request'.
顯然, ts-node
無法找到請求對象的擴展類型定義。
最終花了幾個小時后,我知道 VS Code 沒有抱怨並且能夠找到打字定義,這意味着ts-node
編譯器有問題。
更新package.json
中的啟動script
為我修復了它。
"start": "ts-node --files api/index.ts",
--files
參數在這里發揮關鍵作用,確定自定義類型定義。
欲了解更多信息,請訪問:https ://github.com/TypeStrong/ts-node#help-my-types-are-missing
一種可能的解決方案是使用“雙重轉換到任何”
1-用你的屬性定義一個接口
export interface MyRequest extends http.IncomingMessage {
myProperty: string
}
2-雙投
app.use((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: Error) => void) => {
const myReq: MyRequest = req as any as MyRequest
myReq.myProperty = setProperty()
next()
})
雙重鑄造的優點是:
-noImplicitany
標志編譯罰款或者,有快速(無類型)路線:
req['myProperty'] = setProperty()
(不要使用您自己的屬性編輯現有定義文件 - 這是不可維護的。如果定義錯誤,請打開拉取請求)
編輯
請參閱下面的評論,在這種情況下,簡單的轉換工作req as MyRequest
在 2020 年 5 月下旬嘗試擴展 ExpressJS 的請求時,幫助任何只是在這里嘗試其他東西的人對我有用。 在使它起作用之前,我必須嘗試十幾件事:
"typeRoots": [
"./node_modules/@types",
"./your-custom-types-dir"
]
declare global {
namespace Express {
interface Request {
customBasicProperty: string,
customClassProperty: import("../path/to/CustomClass").default;
}
}
}
{
"restartable": "rs",
"ignore": [".git", "node_modules/**/node_modules"],
"verbose": true,
"exec": "ts-node --files",
"watch": ["src/"],
"env": {
"NODE_ENV": "development"
},
"ext": "js,json,ts"
}
也許這個問題已經回答了,但我想分享一點,現在有時像其他答案這樣的界面可能有點過於嚴格,但我們實際上可以維護所需的屬性,然后通過創建一個添加任何額外的屬性來添加鍵類型為string
,值類型為any
import { Request, Response, NextFunction } from 'express'
interface IRequest extends Request {
[key: string]: any
}
app.use( (req: IRequest, res: Response, next: NextFunction) => {
req.property = setProperty();
next();
});
所以現在,我們還可以向這個對象添加我們想要的任何其他屬性。
如果您嘗試了所有答案但仍然無法正常工作,這里有一個簡單的 hack
app.use((req, res, next) => {
(req as any).property = setProperty();
next();
});
這會將req
對象轉換為any
,因此您可以添加所需的任何屬性。 請記住,這樣做會失去req
對象的類型安全性。
我有同樣的問題並像這樣解決它:
// /src/types/types.express.d.ts
declare namespace Express {
export interface Request {
user: IUser
}
}
但是需要一些條件!
tsconfig.json
配置"paths": {
"*": [
"node_modules/*",
"src/types/*"
]
},
在此之后tsc
將構建捆綁包,但ts-node
不會。
--files
添加到編譯器命令ts-node --files src/server.ts
我使用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}` });
});
我通過創建一個新類型而不全局擴展 Request 類型解決了這個問題。
import { Request } from 'express'
type CustomRequest = Request & { userId?: string }
您必須將擴展屬性與optional(?)運算符一起使用,以免出現“沒有重載匹配此調用”錯誤。
包版本:
"@types/express": "^4.17.13",
"@types/morgan": "^1.9.3",
"@types/node": "^17.0.29",
"typescript": "^4.6.3",
"express": "^4.18.0",
在具有節點 12.19.0 和 express 4 的 mac 上,使用 Passport 進行身份驗證,我需要擴展我的 Session 對象
與上面類似,但略有不同:
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;
}```
對我有用的簡單解決方案是創建一個擴展快速請求的新自定義接口。 這個接口應該包含你所有的自定義 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){}
這個答案可能已經很晚了,但無論如何我是這樣解決的:
tsconfig
文件中包含您的類型源(這可能是一個全新的線程)express
的目錄express
目錄中創建一個文件並將其命名為index.d.ts
(必須完全一樣)declare module 'express' {
export interface Request {
property?: string;
}
}
這對我有用:
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,
///....
});
}));
對於簡單的情況,我在外部中間件中使用headers
屬性,稍后在內部中間件中獲取它。
// outer middleware
req.headers["custom_id"] = "abcxyz123";
// inner middleware
req.get("custom_id");
缺點:
string
。 如果要存儲其他類型,例如json
或number
,則可能需要稍后對其進行解析。headers
屬性沒有記錄。 Express 僅記錄req.get()
方法。 因此,您必須使用與 property headers
一起使用的 Express 的確切版本。 我已將響應類型更改為包括 ApiResponse(我的自定義響應 obj) 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'})
.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) => {
return // parse user from the header
}
const withUser = (callback: (foo, req, res) => void) => (req, res) => {
const user = auth(req)
if(!user)
return res.status(401).send({ result: 'unauthorized-error' })
return callback(user, req, res)
}
app.get('/something', withUser((user, req, res) => {
// do something
}))
如果需要,您甚至可以將它們堆疊起來:
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
}))
只需使用表達促進的語言而不是不安全的突變。
只需將屬性添加到 req.params 對象。
req.params.foo = "bar"
為什么我們需要像上面接受的答案那樣麻煩,當我們可以這樣做時
而不是我們的財產附加到請求時,我們可以將其連接到請求頭
req.headers[property] = "hello"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.