programing

Typescript를 사용하여 Express Request 개체 확장

lastmemo 2023. 3. 17. 19:33
반응형

Typescript를 사용하여 Express Request 개체 확장

타이프 스크립트를 사용하여 미들웨어에서 요청 개체를 표현하기 위해 속성을 추가하려고 합니다.그러나 개체에 추가 속성을 추가하는 방법을 찾을 수 없습니다.가능하면 괄호 표기법은 사용하지 않았으면 합니다.

다음과 같은 내용을 쓸 수 있는 솔루션을 찾고 있습니다(가능한 경우).

app.use((req, res, next) => {
    req.property = setProperty(); 
    next();
});

사용자 정의 정의를 만들고 Typescript의 Declaration Merge(선언 병합) 기능을 사용합니다.이것은, 예를 들면, 에서 자주 사용됩니다.

" " " 를 만듭니다.custom.d.ts 꼭 이 을 담으세요.tsconfig.json의 »files- 경우). - 섹션(있는 경우)을 사용하다

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)
})

의 코멘트에서 제시된 바와 같이 단순히 글로벌에 선언합니다.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를 .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개 정도의 답을 시도했지만 성공하지 못했다.3mards repo를 가리키는 jd291의 코멘트로 겨우 작업을 할 수 있었습니다.

.types/express/index.d.ts이치노릇을 하다

declare namespace Express {
    interface Request {
        yourProperty: <YourType>;
    }
}

and에 포함하다.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

익스프레스 개발자들은 사용자가 자신의 속성을 추가하고 싶어할 수 있다고 생각한 것 같습니다.locals물건.함정은, 그것이 그 안에 있지 않다는 것이다.request ,에서는response★★★★★★ 。

response.locals오브젝트에는 요구와 응답 사이클에 캡슐화되어 다른 사용자의 다른 요구에 노출되지 않는 임의의 커스텀속성을 포함할 수 있습니다.

userId를 ? ★★★★★★★★★★를 설정해 주세요.response.locals.userId = '123' 는 없습니다. 타이핑과 씨름할 필요가 없습니다.

단점은 응답 개체를 전달해야 한다는 것입니다. 그러나 이미 이 개체를 전달하고 있을 가능성이 높습니다.

https://expressjs.com/en/api.html#res.locals

타자 치기

또 다른 단점은 안전성이 떨어진다는 것이다.하여 ''할 수 있습니다.bodylocals★★★★★★★★★★★★★★★★★★:

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
  }
);

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"]
    }
}

이 모든 응답은 어떤 면에서 잘못되었거나 시대에 뒤떨어진 것으로 보인다.

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" ]

건배.

이것은 매우 오래된 질문이지만, 나는 최근에 이 문제를 우연히 발견했다.하지만, 했습니다.Request로, 코 、 - 、 - 、 - 、 - 、 - 、 - 、 - - - - - - 。논리적으로, 나는 이것을 시도했다.

import ITenant from "../interfaces/ITenant";

declare namespace Express {
    export interface Request {
        tenant?: ITenant;
    }
}

가 취급하기 때문에 효과가 없었습니다..d.ts파일은 글로벌 Import로 간주되며 Import가 있는 경우 일반 모듈로 취급됩니다.그렇기 때문에 위의 코드는 표준 타이프스크립트 설정으로 동작하지 않습니다.

결국 이렇게 된 거야

// 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",
    ]        

express4에 대응한 솔루션을 찾고 있는 경우는, 이하를 참조해 주세요.

@types/sys/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에서 레퍼런스

TypeScript에서는 인터페이스는 오픈엔드로 되어 있습니다.즉, 속성을 재정의하는 것만으로 어디에서나 속성을 추가할 수 있습니다.

express.d.ts 파일을 사용하고 있는 것을 고려하면 Request 인터페이스를 재정의하여 추가 필드를 추가할 수 있습니다.

interface Request {
  property: string;
}

미들웨어 기능에서는 req 파라미터에도 이 속성이 있어야 합니다.코드를 변경하지 않고 사용할 수 있습니다.

패키지에 하는 사용자에게 합니다.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"]
      ...
  }
}

을 멈췄습니다만,도 「아쉽게도」, 「아쉽게도」, 「아쉽게도」, 「아쉽다」, 「아쉽다」, 「아쉽다」, 「아쉽다」, 「아쉽다」, 「아쉽다」, 「아쉽다」,ts-node컴파일러는 여전히 던지곤 합니다.

 Property 'decoded' does not exist on type 'Request'.

그...ts-node요청 객체의 확장 유형 정의를 찾을 수 없었습니다.

시간 후 타이핑 수 은 VS 코드가 입니다.이것은, 에 문제가 있는 것을 나타내고 있습니다.ts-node컴파일러

start (업데이트 시작)scriptpackage.json쳐주주고

"start": "ts-node --files api/index.ts",

--files여기서 인수는 커스텀유형 정의를 결정하는 중요한 역할을 합니다.

상세한 것에 대하여는, https://github.com/TypeStrong/ts-node#help-my-types-are-missing 를 참조해 주세요.

Request type을 글로벌하게 확장하지 않고 새로운 type을 생성하여 이 문제를 해결했습니다.

import { Request } from 'express'
    
type CustomRequest = Request & { userId?: string }

'No overload match this call' 오류가 발생하지 않도록 하려면 옵션(?) 연산자와 함께 확장 속성을 사용해야 합니다.

패키지 버전:

    "@types/express": "^4.17.13",
    "@types/morgan": "^1.9.3",
    "@types/node": "^17.0.29",
    "typescript": "^4.6.3",
    "express": "^4.18.0",

가능한 해결책 중 하나는 "모든 것에 이중 주조"를 사용하는 것입니다.

1- 속성과의 인터페이스를 정의합니다.

export interface MyRequest extends http.IncomingMessage {
     myProperty: string
}

더블 캐스트

app.use((req: http.IncomingMessage, res: http.ServerResponse, next: (err?: Error) => void) => {
    const myReq: MyRequest = req as any as MyRequest
    myReq.myProperty = setProperty()
    next()
})

더블 캐스팅의 장점은 다음과 같습니다.

  • 타이핑 사용 가능
  • 기존 정의를 오염시키지 않고 확장하여 혼란을 방지한다.
  • 명시적이기 을 with팅이 since팅이 、 since since 、 since since 、 since since 、 since since since since since since since since since 。-noImplicitany

또는 퀵(입력되지 않은) 루트가 있습니다.

 req['myProperty'] = setProperty()

(기존 정의 파일을 자신의 속성으로 편집하지 마십시오.유지관리가 불가능합니다.정의가 잘못된 경우 풀 요청을 엽니다.)

편집

은 아래해 주세요.이 경우는 간단한 캐스팅이 가능합니다.req as MyRequest

여기서 다른 것을 찾고 있는 사람들을 돕는 것이 익스프레스를 확장하려고 했던 2020년 5월 말에 나에게 효과가 있었다.JS의 요구.저는 이 작업을 수행하기 전에 수십 가지 이상의 시도를 해야 했습니다.

  • tsconfig.json의 "typeRoots"에서 모든 사용자가 권장하는 순서를 바꿉니다(TSconfig에 ".src" 등의 rootDir 설정이 있는 경우 src 패싱을 삭제하는 것을 잊지 마십시오).예:
"typeRoots": [
      "./node_modules/@types",
      "./your-custom-types-dir"
]
  • 커스텀 확장자('/your-custom-types-dir/express/index.d.ts")의 예.내 경험에서 클래스를 유형으로 사용하려면 인라인 Import 및 기본 내보내기를 사용해야 했습니다.이것도 마찬가지입니다.
declare global {
  namespace Express {
    interface Request {
      customBasicProperty: string,
      customClassProperty: import("../path/to/CustomClass").default;
    }
  }
}
  • nodeemon.json 파일을 업데이트하여 "--files" 명령을 ts-node에 추가합니다.예:
{
  "restartable": "rs",
  "ignore": [".git", "node_modules/**/node_modules"],
  "verbose": true,
  "exec": "ts-node --files",
  "watch": ["src/"],
  "env": {
    "NODE_ENV": "development"
  },
  "ext": "js,json,ts"
}

모든 답을 시도해 봤지만 제대로 작동하지 않았다면, 여기 간단한 해킹이 있습니다.

app.use((req, res, next) => {
    (req as any).property = setProperty(); 
    next();
});

하면 ★★★★★★★★★★★★★★★★★★★★▼reqany따라서 원하는 속성을 추가할 수 있습니다..req★★★★★★ 。

같은 문제가 있어 다음과 같이 해결합니다.

// /src/types/types.express.d.ts
declare namespace Express {
    export interface Request {
        user: IUser
    }
}

하지만 몇 가지 조건이 필요합니다!

  1. 가가에 tsconfig.json
"paths": {
    "*": [
        "node_modules/*",
        "src/types/*"
    ]
},

후에 ★★★★tsc만, 「번들」은,ts-node

  1. 붙여야 요.--files컴파일러
ts-node --files src/server.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();
});

이제 이 개체에 원하는 속성을 추가할 수도 있습니다.

간단한 해결책은 express request를 확장하는 새로운 커스텀인터페이스를 만드는 것입니다.이 인터페이스에는 옵션으로 모든 커스텀 요구 속성이 포함되어 있어야 합니다.이 인터페이스를 미들웨어 요구 유형으로 설정합니다.

// 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){}

하였습니다.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}` });
});

이 답변에는 이미 늦은 감이 있지만, 어쨌든 제가 해결한 방법은 다음과 같습니다.

  1. 유형 소스가 에 포함되어 있는지 확인합니다.tsconfigfile스레드일 수 )file(파일명: 새로운 스레드일 수 있음)
  2. 유형 디렉토리 내에 새 디렉토리를 추가하고 확장하거나 유형을 생성할 패키지로 이름을 지정합니다. 「Directory」를 「」로 합니다.express
  3. <고객명>expressa it it 、 「 」라고 합니다.index.d.ts (정확히 이와 같아야 합니다.)
  4. 마지막으로 다음과 같은 코드를 입력하면 유형을 확장할 수 있습니다.
declare module 'express' {
    export interface Request {
        property?: string;
    }
}

노드 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;
}```

이 방법은 효과가 있었습니다.

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>;

또, 이러한 시그니처를 가지는 함수를 export 하는 것과 같은 코드로 사용하고 있습니다.

app.post('some/api/route', asyncHandlers(async (req, res) => {
        return await serviceObject.someMethod(req.user, {
            param1: req.body.param1,
            paramN: req.body.paramN,
            ///....
        });
    }));

경우라면, 저는 단한, 경 the를 합니다.headers외부 미들웨어에 속하고 나중에 내부 미들웨어에 속합니다.

// outer middleware
req.headers["custom_id"] = "abcxyz123";

// inner middleware
req.get("custom_id");

결점:

  • 할 수 것은 「」입니다.string 타입을 json ★★★★★★★★★★★★★★★★★」number을 사용하다
  • headers속성이 문서화되지 않았습니다.Express는 메서드만 문서화합니다.따라서 Property에서 동작하는 Express의 정확한 버전을 사용해야 합니다.headers.

ApiResponse 응답 ApiResponse(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){
        /* ... */
    }
}

.d.ts 선언은 해킹입니다.express' 미들웨어 시스템은 타자용 시스템이 아니라는 사실을 받아들이세요.그러니까 쓰지 마세요.

잘못된 코드 예시:

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
}))

안전하지 않은 돌연변이가 촉진되는 대신 언어를 사용하세요.

편집: 처음 제안한 솔루션을 사용합니다.단, 인증 함수가 에러를 발생시키는 차이점에서는 올바른 응답을 검출하여 반환할 수 있기 때문에 컨트롤러에서 실행할 필요가 없습니다.예를 들어 다음과 같습니다.

app.get('/something', withResponse((req) => {
   const user = auth(req)
   return success({
     message: `Hi ${user.name}`
   })
}))

res.send를 수동으로 호출하지 않고 반품 타입도 보냅니다.응답도 입력할 수 있습니다.tRPC도 확인해 보시기 바랍니다.

req.params 객체에 속성을 추가합니다.

req.params.foo = "bar"

언급URL : https://stackoverflow.com/questions/37377731/extend-express-request-object-using-typescript

반응형