NestJS拦截并修改即将发送的HTTP请求

10

我可能遗漏了一些东西或者做错了什么。 我有一个NestJS应用程序,它正在尝试向外部API发出http请求。 我想能够拦截这个发送的请求并在执行之前修改其标头。

我尝试使用拦截器,但没有成功,入站的http请求被拦截了,但是出站的没有。 任何建议或帮助将不胜感激。


1
如果您调用外部API,应使用http / axios或类似工具,并创建带有标头的客户端。此调用与nestjs无关。个人建议使用axios客户端,因为nestjs无法绑定拦截器,因为它对内部逻辑一无所知。 - tano
这个答案对我有用:https://dev59.com/97roa4cB1Zd3GeqPu_o- - myol
3个回答

7

首先让我们处理一下:

我尝试使用拦截器,但并没有成功,入站http请求被拦截了,但出站请求却没有。

根据文档的说明,完全可以拦截响应。

@Injectable()
export class TransformHeadersInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    call$: Observable<any>,
  ): Observable<any> {
    // Get request headers, e.g.
    const userAgent = context.switchToHttp().getRequest().headers['user-agent'];

    // Not sure if headers are writeable like this, give it a try
    context.switchToHttp().getResponse().headers['x-api-key'] = 'pretty secure';

    return call$;
  }
}

如果您想根据响应数据操作标头,可以像这样访问数据:

如果要根据响应数据操作标头。您可以像这样访问数据:

return call$.pipe(map(data => {
    // Your code here
    return data;
}));

我对以下内容有一些想法:

我有一个NestJS应用程序,试图向外部API发出http请求。我希望能够拦截这个即将发出的请求并在执行之前修改其中的标头。

因此,我认为有两种使用情况。首先,您有一组默认标头,最初分配给http客户端并与每个请求一起发送。例如:

import { HTTP_TOKEN } from './constants';
import * as http from 'request-promise-native';

export const httpProviders: any = [
  {
    provide: HTTP_TOKEN,
    useFactory: () => {
      return http.defaults({
        headers: {
          'Accept': 'application/json',
          'Content-type': 'application/json',
          'User-agent': 'my--app',
        },
      });
    },
  },
];

其次,您需要根据请求创建和分配标头。这是使用拦截器时的情况。在身份验证方面,您可以考虑使用守卫,就像tano在他的答案中建议的那样。


1
这是否意味着我们无法将HTTP标头附加到出站请求?我遇到了这样一种情况,需要拦截一个出站请求并在调用请求之前附加标头。https://dev59.com/srnoa4cB1Zd3GeqPaO-L @Upvote? - Ankit Tanna

7

我曾经遇到过类似的问题,需要修改/添加响应头。以下代码对我有用:

@Injectable()
export class TransformHeadersInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    call$: Observable<any>,
  ): Observable<any> {

    return call$.pipe(
            map((data) => {
                // pipe call to add / modify header(s) after remote method
                let req = context.switchToHttp().getRequest();
                req.res.header('x-api-key', 'pretty secure');
                return data;
            }),
        );
  }
}

2
可能需要添加 `const res = context.switchToHttp().getResponse();if (!res.headersSent) { // for res.redirect()` - Ricardo Saracino
2
这似乎对我没有添加任何标题。 - Jim
为什么要更改响应?你需要保护请求! - Douglas Caina

0

我可以举一个使用外部 API 调用的例子:

import { PaginateModel, PaginateResult, Document } from 'mongoose';
import { AxiosInstance } from 'axios';
import { UseGuards, InternalServerErrorException, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Context } from './decorators/ctx.decorator';

@Injectable()
@UseGuards(AuthGuard('jwt'))
export abstract class ServiceBase<T extends Document> {
    protected abstract readonly path: string;

    constructor(protected readonly externals: Object, protected readonly model: PaginateModel<T>) {}

    async create(data: T, ctx: Context): Promise<T> {
        try {
            this.validate(data);
            const { lng, core } = this.separate(data);
            const catalog = new this.model(core);
            const head = await catalog.save();
            Object.assign(head, lng);
            const Authorization = ctx.token;
            const axios: AxiosInstance = this.externals[ctx.lang];
            try {
                const resp = await axios.post(`${this.path}`, head, { headers: { Authorization } });
                return resp.data;
            } catch (err) {
                // in case of any error the head record should be removed.
                catalog.remove();
                throw err;
            }
        } catch (err) {
            console.log(err);
            throw new InternalServerErrorException(err);
        }
    }

    abstract async validate(data: T): Promise<any>;

    abstract separate(data: T);

    async update(id: string, data: T, ctx: Context): Promise<T> {
        try {
            const curr = await this.model.findById(id).exec();
            const { lng, core } = this.separate(data);
            Object.assign(curr, core);
            await curr.save();
            Object.assign(core, lng);
            const Authorization = ctx.token;
            const axios: AxiosInstance = this.externals[ctx.lang];
            const resp = await axios.put(`${this.path}/${id}`, core, { headers: { Authorization } });
            return resp.data;
        } catch (err) {
            throw new InternalServerErrorException(err);
        }
    }

    async get(id: string, ctx: Context): Promise<T> {
        try {
            const Authorization = ctx.token;
            const axios: AxiosInstance = this.externals[ctx.lang];
            const resp = await axios.get(`${this.path}/${id}`, { headers: { Authorization } });
            return resp.data;
        } catch (err) {
            console.log(err);
            return null;
        }
    }

    async findOne(query: object): Promise<T> {
        const data = await this.model.findOne(query, { _class: 0 }).exec();
        return data;
    }

    async findAll(ctx: Context): Promise<T[]> {
        try {
            const Authorization = ctx.token;
            const axios: AxiosInstance = this.externals[ctx.lang];
            const resp = await axios.get(`${this.path}`, {
                headers: { Authorization },
            });
            return resp.data;
        } catch (err) {
            console.log(err);
            return null;
        }
    }

    async find(query: {} = {}, page: number, rows: number, ctx: Context): Promise<PaginateResult<T>> {
        try {
            const Authorization = ctx.token;
            const axios: AxiosInstance = this.externals[ctx.lang];
            const resp = await axios.get(`${this.path}`, {
                params: { page, rows },
                headers: { Authorization },
            });
            return resp.data;
        } catch (err) {
            console.log(err);
            return null;
        }
    }
}

其中 externals 是:

import axios, { AxiosInstance } from 'axios';

const config = require('../../config/settings.json');

export const externalProviders = {
    provide: 'ExternalToken',
    useFactory: () => {
        const externals = {};
        for (const lang in config.externals) {
            externals[lang] = axios.create({
                baseURL: config.externals[lang],
            });
        }
        return externals;
    }
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接