見出し画像

Nuxt.js SSR で Cookieが送られない

記事を書く動機

前回の記事で、認証することができましたが、クッキーがAPIサーバーに送られずに困っていました。同じ轍を踏まないためにも。

前提知識

これが公式のライフサイクルです。(公式はアップデートされています。以下に古いライフサイクルを載せています。)

画像1

背景と問題発生のプロセス

asyncDataメソッドは、データの用意をするためのコールバックです。例えば、ログイン完了し、認証済みのページに Navigate(nuxt-link)された時、このコールバックでAPIを使用してデータを準備することができます。

// private_page.vue

// ~中略~ 
import axios from 'axios';
export default {

   async asyncData() {
       // クッキーが必要なAPI
       const url = "https://localhost:3000/api/x"
       const result = await axios.get(url)

       return {
           result: result
       }
   }, 
// ~以下略~ 

このコードは、動くのですが、このページをブラウザでリロードすると、 おそらく 401 エラーになります。理由としては、クッキーが送られていないためです。ちなみに、問題を簡素化するために、いろいろ省いていますが、オレオレ証明書を使っている場合は、以下のパラメータをaxios に渡す必要があります。

import https from 'https';
// ここから
const agent = new https.Agent({rejectUnauthorized: false});
const options = {
   httpsAgent: agent, 
   withCredentials: true
}
// ここまで
const url = "https://localhost:3000/api/x"
const result = await axios.get(url, options)

なぜクッキーが送られないか

Navigateで遷移した場合は、asyncDataは、クライアントサイドで実行されます。この時、axiosはクライアントサイドのインスタンスです。ログインもクライアントサイドから APIを実行していると考えると、Cookieをaxiosから送ることができます。逆にブラウザリロードを行い、Incoming Request 経由でasyncDataが呼ばれた場合、かつSSRの時は、このコードはサーバサイドで動きます。サーバサイドのaxiosはクライアントサイドのaxiosとは関係がないためもちろんクッキーは共有されません。公式(非同期なデータ - コンテキスト)にも、asyncDataはサーバサイドおよびクライアントサイドで動作することが記載されています。process.server を使用すると、それがどちらで動いているかが確認できます。

サーバサイドでクッキーを送るには

Incoming Requestは、Contextから取得することができます。Contextにアクセスできれば、Incoming Request 本体を取り出し、そこからヘッダー、およびクッキーにアクセスすることもできます。Contextは、以下のように、asyncDataの引数として取得することができます。

async asyncData ({ req }) { ...

問題を修正したコード

Incoming Request(クッキーを持ったリクエスト)のヘッダー(のなかにクッキーがあるので今回はめんどくさいので)をそのまま、axiosに渡す。クライアントサイドで動く時は、reqは undefined なので、prcess.server によるチェックは必要です。

// private_page.vue

// ~中略~ 
import axios from 'axios';
export default {

   async asyncData({req}) { // 修正

       const agent = new https.Agent({rejectUnauthorized: false});
       var options = {
           httpsAgent: agent, 
           withCredentials: true
       }

       if (process.server) // 修正
           options.headers = req.headers // 修正

       const url = "https://localhost:3000/api/x"
       const result = await axios.get(url, options)

       return {
           result: result
       }
   }, 
// ~以下略~ 

おまけ: ハマりポイント

Nuxtで、デバッグモードにすると、エラー発生時、スタックトレースを綺麗にブラウザに表示してくれますよね。そのときにリクエストヘッダーとクッキーも表示してくれるのですが、そこには、クライアントサイドの、あくまでページを表示するために使用された - 要するにIncoming Requestのものです。なので、サーバサイドからヘッダーのログを見た時に、クッキーが送られていないことに違和感を感じます。Google デバッグコンソール上にも当該APIのログが出てないので、ここでようやく「そうやった、SSRしてたんだわ」となり、今回の問題解決の糸口になりました。あと、ここで道草もしました → Axios doesn't send cookies with POST and data

この記事が気に入ったらサポートをしてみませんか?