NestJS 拦截器:无法在传出请求上设置 HTTP 标头

Ank*_*nna 6 interceptor node.js express typescript nestjs

我正在 NestJS 中编写 API,其中有一组通用标头。我决定使用拦截器将标头附加到传出请求中。标头不会附加到请求中,因此请求不断失败。

拦截器

import * as utils from '../utils/utils';
import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor
} from '@nestjs/common';
import { HEADERS } from '../middlewares/headers.constant';
import { Observable } from 'rxjs';
import { Request } from 'express';
import { DATA_PARTITION_ID } from '../app.constants';

@Injectable()
export class HeadersInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<void> {
    const ctx = context.switchToHttp();
    const request: Request = ctx.getRequest();

    this.setHeaders(request);

    return next.handle();
  }

  private setHeaders(request): void {
    this.updateHeaders(request, HEADERS.ACCEPT, 'application/json');
    this.updateHeaders(request, HEADERS.CONTENT_TYPE, 'application/json');
    this.updateHeaders(request, HEADERS.ACCEPT_ENCODING, 'gzip, deflate, br');
    this.updateHeaders(
      request,
      HEADERS.DATA_PARTITION_ID,
      DATA_PARTITION_ID
    );
    this.updateHeaders(
      request,
      HEADERS.AUTHORIZATION,
      `Bearer ${utils.parseCookies(request).stoken}`
    );
    this.updateHeaders(request, HEADERS.APP_KEY, '');
  }

  private updateHeaders(
    request: Request,
    property: string,
    value: string
  ): void {
    if (!request.headers.hasOwnProperty(property)) {
      request.headers[property] = value;
    } else {
      void 0;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

该拦截器只做一件事:访问请求并附加标头并将控制权传递给下一个处理程序。

枚举

export enum HEADERS {
  DATA_PARTITION_ID = 'Data-Partition-Id',
  AUTHORIZATION = 'Authorization',
  CONTENT_TYPE = 'Content-Type',
  APP_KEY = 'appkey',
  ACCEPT = 'accept',
  ACCEPT_ENCODING = 'accept-encoding'
}
Run Code Online (Sandbox Code Playgroud)

控制器

import { Body, Controller, Post, Req, UseInterceptors } from '@nestjs/common';
import { HeadersInterceptor } from '../interceptors/headers.interceptor';
import { SearchData } from './models/search-data.model';
import { SearchResults } from './models/search-results.model';
import { SearchService } from './search.service';

@Controller('')
@UseInterceptors(new HeadersInterceptor())
export class SearchController {
  constructor(private searchService: SearchService) {}

  @Post('api/search')
  async searchDataById(@Body() searchData: SearchData, @Req() req): Promise<SearchResults> {
    console.log(req.headers);
    return await this.searchService.getSearchResultsById(searchData);
  }
}
Run Code Online (Sandbox Code Playgroud)

服务

import { HttpService, HttpStatus, Injectable } from '@nestjs/common';
import { AppConfigService } from '../app-config/app-config.service';
import { DataMappingPayload } from './models/data-mapping-payload.model';
import { SearchData } from './models/search-data.model';
import { SearchModelMapper } from './search.service.modelmapper';
import { SearchResults } from './models/search-results.model';
import { ServiceException } from '../exception/service.exception';

@Injectable()
export class SearchService {
  constructor(
    private searchModelMapper: SearchModelMapper,
    private configService: AppConfigService,
    private readonly httpService: HttpService
  ) {}

  async getSearchResultsById(searchData: SearchData): Promise<SearchResults> {
    if (searchData.filters.collectionId) {
      console.log(this.configService.appConfig.urls.SEARCH_RESULTS_BY_COLLECTION_ID_URL.replace(
          '${collectionId}',
          searchData.filters.collectionId
        )
      );
      const searchResultsAPI = await this.httpService
        .get(
          this.configService.appConfig.urls.SEARCH_RESULTS_BY_COLLECTION_ID_URL.replace(
            '${collectionId}',
            searchData.filters.collectionId
          )
        )
        .toPromise();
      const kinds = this.searchModelMapper.getUniqueKinds(
        searchResultsAPI.data.results
      );
      const mappingPayload = await this.getDataMapping(kinds);
      return this.searchModelMapper.generateSearchResults(
        kinds,
        mappingPayload,
        searchResultsAPI.data.results
      );
    } else {
      this.raiseException();
    }
  }

  async getDataMapping(kinds: string[]): Promise<[]> {
    const entityKindNames: DataMappingPayload = {
      entityKindNames: kinds
    };
    const dataMappingAPI = await this.httpService
      .post(
        this.configService.appConfig.urls.DATA_CATALOG_SERVICE_URL,
        JSON.stringify(entityKindNames)
      )
      .toPromise();

    return dataMappingAPI.data.entityViewData;
  }

  // To be moved to util functions
  private raiseException(): void {
    throw new ServiceException(
      {
        message: 'This does not have a collection id',
        missing: 'Collection Id',
        code: HttpStatus.BAD_REQUEST
      },
      HttpStatus.BAD_REQUEST
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

当我req.headers控制器中访问时,我确实获得了需要通过拦截器设置的所有标头。

{
[0]   'accept-encoding': 'gzip, deflate, br',
[0]   'accept-language': 'en-US,en;q=0.9',
[0]   cookie: '',
[0]   'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
[0]   'content-type': 'application/json',
[0]   accept: 'application/json',
[0]   appkey: '',
[0]   'cache-control': 'no-cache',
[0]   'postman-token': 'cb397012-71aa-460a-b66b-28600538faf9',
[0]   host: 'localhost:8080',
[0]   'content-length': '77',
[0]   connection: 'keep-alive',
[0]   'Data-Partition-Id': 'tenant1',
[0]   Authorization: 'Bearer TOKEN_HERE'
[0] }
Run Code Online (Sandbox Code Playgroud)

当我检查实际请求的日志时,它显示Authorization is null。这意味着请求不会被拦截,也不会附加标头。

有人遇到过类似的问题吗?

Jay*_*iel 6

如果我正确理解您的意思,您希望将标头添加到从HttpService. NestJS 中的interceptor作用于IncomingMessage(一般传入请求)和ServerResponse(或一般传出响应)。它看不到从 HTTP 客户端HttpService或任何其他 HTTP 客户端发送的内容。相反,您需要在方法级别或模块级别设置标头(如果它们都是通用值)。它HttpModule有一个registerandregisterAsync方法,可用于将值传递给每个HttpService调用,因此如果您有通用标头,您可以通过以下方式管理它们:

@Module({
  imports: [
    HttpModule.register({
      headers: {} // object of headers you want to set
    }),
  ]
})
export class MyModule {}
Run Code Online (Sandbox Code Playgroud)

现在,当您使用时,httpService.get(url)您将随它发送标头。