やりたいこと
到達したリクエストのヘッダーを検証し、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()