如何在NestJS中记录所有Axios外部http请求

pgx2nnw8  于 2023-10-18  发布在  iOS
关注(0)|答案(3)|浏览(153)

我希望能够记录每个完整的网址axios请求,头等,但目前没有找到这样做的方法.
到目前为止,我所做的就是编写一个基于此answer的Http拦截器

export class HttpLoggerInterceptor 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
        const req = context.switchToHttp().getRequest();
        return data;
      }),
    );
  }
}

现在我在调试中浏览对象reqcontext prop ,但看不到Asios请求URL等。除非我错过了。
我的控制器路由(在这种情况下是api/data)有N个http外部调用发生,但拦截器只拦截控制器控制器调用,而不是Axios调用。
有什么想法吗?
context对象:

args:Array(2) [IncomingMessage, ServerResponse]
constructorRef:class AppController { … }
getRequest:() => …
getResponse:() => …
handler:data() { … }
__proto__:Object {constructor: , getClass: , getHandler: , …}

req

_dumped:false
_events:Object {}
_eventsCount:0
_maxListeners:undefined
_parsedOriginalUrl:Url {protocol: null, slashes: null, auth: null, …}
_parsedUrl:Url {protocol: null, slashes: null, auth: null, …}
_readableState:ReadableState {objectMode: false, highWaterMark: 16384, buffer: BufferList, …}
baseUrl:""
body:Object {}
client:Socket {connecting: false, _hadError: false, _handle: TCP, …}
complete:true
connection:Socket {connecting: false, _hadError: false, _handle: TCP, …}
destroyed:false
fresh:false
headers:Object {accept: "application/json, text/plain, */*", user-agent: "axios/0.18.0", host: "localhost:3000", …}
host:"localhost"
hostname:"localhost"
httpVersion:"1.1"
httpVersionMajor:1
httpVersionMinor:1
ip:"::ffff:127.0.0.1"
ips:Array(0)
method:"GET"
next:function next(err) { … }
originalUrl:"/api/data"
params:Object {}
__proto__:Object {constructor: , __defineGetter__: , __defineSetter__: , …}
path:"/api/data"
protocol:"http"
query:Object {}
rawHeaders:Array(8) ["Accept", "application/json, text/plain, */*", "User-Agent", …]
rawTrailers:Array(0) []
readable:true
readableBuffer:BufferList
readableFlowing:null
readableHighWaterMark:16384
readableLength:0
res:ServerResponse {_events: Object, _eventsCount: 1, _maxListeners: undefined, …}
route:Route {path: "/api/data", stack: Array(1), methods: Object}
secure:false
socket:Socket {connecting: false, _hadError: false, _handle: TCP, …}
stale:true
statusCode:null
statusMessage:null
subdomains:Array(0)
trailers:Object {}
upgrade:false
url:"/api/data"
xhr:false
fnx2tebb

fnx2tebb1#

拦截器只处理控制器处理的请求和发出的响应。如果您在处理控制器请求时使用Axios发出http请求,则拦截器不会处理这些请求。

Axios Interceptor

HttpService直接通过get axiosRef()公开其axios示例。使用它,您可以添加axios interceptor

this.httpService.axiosRef.interceptors.request.use(config => { /*...*/ return config })

例如,您可以在AppModuleonModuleInit()中执行此操作。

委托到Facade

作为替代方案,您可以创建一个HttpService facade,记录请求并将所有调用委托给内置的HttpService

@Injectable()
export class MyHttpService {
  private logger: Logger = new Logger(MyHttpService.name);

  constructor (private httpService: HttpService) {}
  
  public get<T = any>(url: string, config?: AxiosRequestConfig): Observable<AxiosResponse<T>> {
    this.logger.log({url, config});
    return this.httpService.get(url, config)
       .pipe(tap(response => this.logger.log(response)));
  }

  // ... all the other methods you need.

}

您可以创建自己的LoggingHttpModule,导入内置的HttpModule并导出您的MyHttpService

t9eec4r0

t9eec4r02#

npm i @types/morgan

npm i morgan

然后

import * as morgan from 'morgan';

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(morgan('combined'))
      .forRoutes('*');
  }
}
rkue9o1l

rkue9o1l3#

对于那些寻找没有额外库的集中式解决方案的人来说,这就是我如何将我的bearer token添加到所有外部API调用中,并在发生时记录日志:

@Injectable()
export class IdkAxiosInterceptor implements OnModuleInit {
  private readonly logger = new Logger(IdkAxiosInterceptor.name);
  private token = undefined;

  constructor(
    @Inject(HttpService)
    private httpService: HttpService,
    @Inject(TokenExchangeService)
    private tokenExchangeService: TokenExchangeService,
  ) {
    this.refreshToken();
  }

  onModuleInit(): any {
    const { axiosRef: axios } = this.httpService;

    axios.interceptors.request.use((config) => {
      return this.onRequest(config);
    }, Promise.reject);
  }

  onRequest(config) {
    this.logger.log('external call for ' + config.url);
    config.headers['Authorization'] = `Bearer ${this.token}`;

    return config;
  }

  @Cron(EVERY_50_MINUTES)
  refreshToken() {
    this.tokenExchangeService.getAccessToken(Environment.PRODUCTION).subscribe(({ access_token }) => {
      this.token = access_token;
    });
  }
}

现在你只需要把它作为一个提供者添加到你的主模块中就可以了。
如果你愿意,我可以为它发布测试:D

相关问题