【问题标题】:nestjs intercept and modify outgoing http requestnestjs 拦截和修改传出的 http 请求
【发布时间】:2019-02-19 09:40:21
【问题描述】:

所以我很可能错过了什么或做错了什么。 我有一个 NestJS 应用程序试图向外部 API 发出 http 请求。 我希望能够在执行它之前拦截这个传出请求并修改它的标头。

我尝试使用拦截器无济于事,传入的 http 请求被拦截,但传出的请求没有被拦截。 任何建议或帮助将不胜感激。

【问题讨论】:

  • 如果您调用外部 api,您应该使用 http/axios 或其他东西,然后创建一个带有标头的客户端。这个调用独立于nestjs。个人使用 axios 客户端,nestjs 无法绑定拦截器,因为它对内部逻辑一无所知。
  • This answer 为我工作

标签: typescript http nestjs


【解决方案1】:

我可以举一个我使用外部 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;
        }
    }
}

外部在哪里:

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

【讨论】:

    【解决方案2】:

    我们先来处理

    我试过使用拦截器无济于事,传入的http请求 被截获但不是传出。

    根据文档https://docs.nestjs.com/interceptors,应该完全可以拦截响应。

    @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 客户端并随每个请求一起发送。例如:

    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 在他的回答中建议的那样。

    【讨论】:

    • 这是否意味着我们不能将 http 标头附加到传出请求?我遇到了一种情况,我需要在调用请求之前拦截传出请求并附加标头。 stackoverflow.com/questions/60809063/…@Upvote?
    【解决方案3】:

    我在修改/添加响应标头时遇到了类似的问题。 以下代码对我有用:

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

    【讨论】:

    • 可能要添加 ` const res = context.switchToHttp().getResponse(); if (!res.headersSent) { // 用于 res.redirect()`
    • 这似乎没有为我添加任何标题。 ?
    • 为什么要改变响应?您需要确保请求的安全!
    猜你喜欢
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 2020-12-12
    • 2013-01-31
    • 2017-02-28
    相关资源
    最近更新 更多