0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ヘッダーの検証のためにNest.jsでInterceptorを実装する

Last updated at Posted at 2022-06-07

やりたいこと

到達したリクエストのヘッダーを検証し、Invalidなら401を返し,ValidならAPIの呼び出しを許可する。

昔、AngularでもInterceptorを作ったのですが、それのNest.js版です。

環境

今回は、現時点で最新のNest.js 8系で行います。
説明用にできるだけシンプルに実装します。

基本的には公式ドキュメントに則ってます。

手順

Nest CLIを使ってファイルを作成します

nest g in interceptor/request

コマンド一発でファイルと雛形を作ってくれるのはAngular/Nestの良いところの一つです。

それでできるInterceptorの雛形↓
ここにヘッダーを検証する処理を書いていきます。

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'
import { Observable } from 'rxjs'

@Injectable()
export class RequestInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle()
  }
}

書いたらこんな感じ

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
  UnauthorizedException,
} from '@nestjs/common'
import { map, Observable } from 'rxjs'
import { checkToken } from 'src/util/validation'

@Injectable()
export class RequestInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest()
    const token = request.headers['authorization']
    // tokenを検証する処理
    const valid = checkToken(token)

    return next.handle().pipe(
      map((data) => {
        if (!valid) throw new UnauthorizedException()
        return data
      })
    )
  }
}

Interceptorの適用方法

3種類あります。

  • クラスレベル
  • メソッドレベル
  • グローバル

クラスレベル

コントローラー単位でInterceptorを適用します。

@UseInterceptors(new RequestInterceptor()) // Controllerにデコレーターとして呼び出す
@Controller('item')
export class ItemController {
  constructor(private itemService: ItemService) {}

  // 複数のアイテムを取得
  // 価格でのソートに対応
  @Get()
  async getItems(): Promise<item[]> {
    const items = await this.itemService.getItems()
    const response = items
    return response
  }

  // 単一のアイテムを取得
  @Get(':id')
  async getItem(@Param('id', new ParseIntPipe()) itemId: number): Promise<item> {
    const item = await this.itemService.getItem(itemId)
    return item
  }
}

メソッドレベル

メソッド単位でInterceptorを適用します。

@Controller('item')
export class ItemController {
  constructor(private itemService: ItemService) {}

  // 複数のアイテムを取得
  // 価格でのソートに対応
  @UseInterceptors(new RequestInterceptor()) // Methodにデコレーターとして呼び出す
  @Get()
  async getItems(): Promise<item[]> {
    const items = await this.itemService.getItems()
    const response = items
    return response
  }

  // 単一のアイテムを取得
  @Get(':id')
  async getItem(@Param('id', new ParseIntPipe()) itemId: number): Promise<item> {
    const item = await this.itemService.getItem(itemId)
    return item
  }
}

グローバルレベル

全てのAPIにInterceptorを適用します。

async function bootstrap() {
  const app = await NestFactory.create(AppModule)
  app.useGlobalInterceptors(new RequestInterceptor()) // 追加
  await app.listen(3000)
}
bootstrap()

弾かれると、ちゃんと401を返してくれるようになります。
image.png

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?