[NestJS 带你飞!] DAY21 - HTTP Module

很多时候我们会需要去串接第三方的 API,例如:绿界科技的金流服务、Binance 的 API 等,这时候如果第三方没有提供相关的 SDK 让我们使用的话,就必须自己用 HTTP Request 去存取对应的资料,早期的 node.js 开发人员可能会使用 request 来实作,但该函式库现在已经被弃用了,取而代之的是 node-fetchaxios,而 Nest 内建了 HTTP Module,它是基於 axios 进行包装的模组,让 Nest 开发人员不必为使用哪个套件烦恼,HTTP Module 即装即用!

使用 HTTP Module

HTTP Module 的 class 名称为 HttpModule,它汇出了一个 HttpService 的 Service,其提供 axios 的方法来处理 HTTP 请求,并且使用 Observable 的形式。

注意:想要知道 axios 具体有哪些方法可以查看官方说明

app.module.ts 为例,将 HttpModule 导入:

import { HttpModule, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [HttpModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

这里我们借用一下 JSONPlaceholder 做为第三方 API,并使用 todos 的资源,将其资料结构用 interface 的方式存在 src/common/models 资料夹中的 todo.model.ts

export interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

调整一下 app.service.ts 的内容,透过 getTodos 方法去取得 todos 的资源:

注意:由於 Agent 的问题,这里我们需要配置 httpsAgentrejectUnauthorizedfalse 以正常使用此 API。

import { HttpService, Injectable } from '@nestjs/common';

import { Agent } from 'https';

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { Todo } from './common/models/todo.model';

@Injectable()
export class AppService {

  constructor(
    private readonly http: HttpService
  ) {}

  getTodos(): Observable<Todo> {
    const httpsAgent = new Agent({ rejectUnauthorized: false });
    return this.http.get('https://jsonplaceholder.typicode.com/todos', { httpsAgent }).pipe(
      map((res) => res.data)
    );
  }

}

调整 app.controller.ts,在 getTodos 中调用 AppServicegetTodos

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('/todos')
  getTodos() {
    return this.appService.getTodos();
  }

}

透过 Postman 存取 http://localhost:3000/todos,会得到下方结果:
https://ithelp.ithome.com.tw/upload/images/20210610/20119338wKp4p3fw9d.png

预设 axios 配置

在上述范例中,会发现存取 JSONPlaceholder 的 todos 资源会碰到 Agent 的问题,如果有多个 API 都会碰到此问题,又不希望每次都要重复写一样的东西,这时候就可以运用 HttpModuleregister 方法来配置预设值,而这个预设值可以配置的项目与 HttpService 各方法中的 options 相同,具体的内容可以参考官方说明

这里以 app.module.ts 为例,将 Agent 的配置设为预设值:

import { HttpModule, Module } from '@nestjs/common';

import { Agent } from 'https';

import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    HttpModule.register({
      httpsAgent: new Agent({ rejectUnauthorized: false })
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

接着调整 app.service.ts,将本来配置好的 Agent 配置移除:

import { HttpService, Injectable } from '@nestjs/common';

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { Todo } from './common/models/todo.model';

@Injectable()
export class AppService {

  constructor(
    private readonly http: HttpService
  ) {}

  getTodos(): Observable<Todo> {
    return this.http.get('https://jsonplaceholder.typicode.com/todos').pipe(
      map((res) => res.data)
    );
  }

}

最後,透过 Postman 存取 http://localhost:3000/todos,会成功获取 todos 的资料:
https://ithelp.ithome.com.tw/upload/images/20210610/20119338vvfPoSXwoY.png

使用环境变数

HttpModule 有提供 registerAsync 方法,透过这个方法可以添加依赖的 Provider,并用工厂函式将其值带入 HttpModule,运用这样的机制来注入 ConfigService,进而将要配置的预设值带入。

这边来做个简单的范例,先在专案目录下新增 .env 并添加下方的环境变数:

HTTP_TIMEOUT=3000

修改 app.module.ts 的内容,在 registerAsync 汇入 ConfigModule 并在 injects 带入 ConfigService,最後在 useFactory 注入 ConfigService

import { HttpModule, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';

import { Agent } from 'https';

import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true
    }),
    HttpModule.registerAsync({
      imports: [ConfigModule],
      useFactory: (config: ConfigService) => ({
        httpsAgent: new Agent({ rejectUnauthorized: false }),
        timeout: config.get('HTTP_TIMEOUT')
      }),
      inject: [
        ConfigService
      ]
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

这样就可以顺利将环境变数带入至 HttpModule 来配置预设值了!

小结

Nest 将 Http Request 相关功能直接打包成内建模组实在很方便,不必再花时间去选择要使用哪个套件,也不必花时间将套件包装成 Nest 的模组,直接注入 HttpService 即可使用。这里附上今天的懒人包:

  1. HttpModule 为内建模组,透过注入 HttpService 来使用 axios 的方法。
  2. 可以透过 HttpModuleregister 来配置预设值。
  3. 可以透过 HttpModuleregisterAsync 来取得注入 Provider 的值,进而配置预设值。

<<:  Day28- 你都用什麽debug kubectl-debug

>>:  Prototype 原型模式

DAY17-MERN

前言: 在大致介绍完React的特性後,我们要延伸聊聊用React写网页时,最好用的前後端架构,M...

浅谈Web应用系统安全 - 骇客攻防战

跨站脚本攻击(XSS) 攻击 XXS就是透过网页没有适当筛选、处理文字造成的漏洞 例如有用户将<...

JavaScript Day04 - 变数(3)

变数(variable)-Boolean 主要为 true、false,内容为判断式也可以,如 2&...

Day 11 | 嵌套元件(二)

本来是没打算分成两天的。但第一天放入了程序码让文章看起来比较冗长,所以只好拆两天啦!今天一样也是 嵌...

[Day10] 文本/词表示方式(一)-前言

一. 前言 在如今社群网路蓬勃的时代,从网路充斥着许多文字资料,要如何有效的分析文字让电脑可以知道我...