Chapter 31

techniques-cookies

kisihara.c
kisihara.c
2021.04.25に更新

cookie

HTTPクッキーはユーザーのブラウザが保存する小さなデータだ。ウェブサイトがステートフルな情報を記憶する為の信頼できるメカニズムとして設計されている。ユーザーが再度ウェブサイトにアクセスすると、クッキーは自動的にリクエストともに送信される。

まず必要なパッケージ(とTypeScriptユーザーならその型)をインストールしよう。

$ npm i cookie-parser
$ npm i -D @types/cookie-parser

インストールが完了したら、cookie-parserミドルウェアをグローバルミドルウェアとして適用する(例えば、main.ts内で)。

import * as cookieParser from 'cookie-parser';
// 初期化ファイルがあるどこか
app.use(cookieParser());

いくつかのオプションをcookieParserミドルウェアに渡せる。

  • secret クッキーの書名に使用する文字列または配列。これは省略可能で、指定されない場合は署名されたクッキーを解析しない。文字列が指定された場合、これはシークレットとして扱われる。配列した場合は、各シークレットを順番に使ってクッキーの書名を解除しようとする。
  • options 2番めのオプションとしてクッキーに渡されるオブジェクト。詳しくはcookieを参照のこと。

ミドルウェアはリクエストのCookieヘッダを解析し、cookieデータをreq.cookiesプロパティとして、またシークレットが提供されている場合はreq.signedCookiesプロパティとして公開する。これらのプロパティは、クッキー名とクッキーの値のname valueペアだ。

secretが提供されると、このモジュールは署名されたクッキーの値の書名解除と検証を行い、そのname valueペアをreq.cookiesからreq.signedCookiesに移動させる。署名付きクッキーとは、値の前にs:がついているクッキーの事だ。書名の検証に失敗した署名付きクッキーは、改ざんされた値の代わりにfalseの値を持つ。

結果、以下のようにルートハンドラ内からクッキーを読み取れるようになった。

@Get()
findAll(@Req() request: Request) {
  console.log(request.cookies); // か"request.cookies['cookieKey']"
  // もしくはconsole.log(request.signedCookies);
}

HINT
@Req()デコレータは@nestjs/commonから、Requestexpressパッケージからインポートされている。

発信するレスポンスにクッキーを添付するには、Response#cookie()メソッドを使用する。

@Get()
findAll(@Res({ passthrough: true }) response: Response) {
  response.cookie('key', 'value')
}

WARNING
レスポンス処理のロジックをフレームワークに任せたい場合は、上記のようにpassthroughオプションをtrueに忘れずに設定する事。詳しくはこちら

HINT
@Resデコレータは@nestjs/commonから、Responseexpressパッケージからインポートされている。

Fastifyとの併用

まず、必要なパッケージをインストールする。

$ npm i fastify-cookie

インストールが完了したら、fastify-cookieプラグインを登録する。

import fastifyCookie from 'fastify-cookie';

// 初期化ファイルがあるどこか
const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter(),
);
app.register(fastifyCookie, {
  secret: 'my-secret', // クッキーの署名の為のもの
});

こうすれば、ルートハンドラ内からクッキーを読み取れる。

@Get()
findAll(@Req() request: FastifyRequest) {
  console.log(request.cookies); // か "request.cookies['cookieKey']"
}

HINT
@Req()デコレータは@nestjs/commonFastifyRequestfastifyパッケージからインポートされている。

発信するレスポンスにクッキーを添付するには、FastifyReply#setCookie()メソッドを使う。

@Get()
findAll(@Res({ passthrough: true }) response: FastifyReply) {
  response.setCookie('key', 'value')
}

FastifyReply#setCookie()の詳細はこちら

WARNING
レスポンス処理のロジックをフレームワークに任せたい場合は、上記のようにpassthroughオプションをtrueに忘れずに設定する事。詳しくはこちら

HINT
@Res()デコレータは@nestjs/commonから、FastifyReplyfastifyパッケージからインポートされている。

カスタムデコレータの作成(クロスプラットフォーム)

受信したクッキーにアクセスする為の便利な宣言的方法を提供する為に、カスタムデコレータを作成する。

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const Cookies = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return data ? request.cookies?.[data] : request.cookies;
  },
);

@Cookies()デコレータはres.cookiesオブジェクトからすべてのcookie、または名前付きcookieを抽出し、その値をデコレーションされた値に入力する。

これで、次のようにルートハンドラのシグネチャでデコレータを使用できるようになった。

@Get()
findAll(@Cookies('name') name: string) {}