はじめに
この記事はクライアントから特定のレスポンスヘッダーにアクセスできないバグに関しての記事です。
このバグはフロントエンドにNext.jsを、バックエンドにGoを採用した学校の課題で、本番環境で動作させた時に発生しました。
POSTメソッドのレスポンスとしてLocation
ヘッダーをセットしたのですが、クライアントから取得することができませんでした。
Swagger UIやネットワークタブからはレスポンスヘッダーの存在を確認できたにも関わらず、なぜか取得できなかったため、原因が中々推測できず30分ほど悩みました。
今後忘れたころにまた遭遇する可能性もありますので、ここで一旦記事にしたいと思います。
先に解消方法から
バグの原因はCORSのAccess-Control-Expose-Headers
レスポンスヘッダーの設定ミスでした。
Location
ヘッダーはセーフリストに含まれていないため、クライアントからアクセスするにはAccess-Control-Expose-Headers
ヘッダーに加える必要がありました。
例えば今回採用したGinの場合は、
// 略
- c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")
+ c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length, Location")
// 略
とすることで、クライアントからLocation
ヘッダーにアクセスすることができるようになりました。
問題の発生状況
POSTメソッドのLocation
レスポンスヘッダーに新しいリソースのIDをセットするために、以下のようなコードを書きました。WebフレームワークとしてGinを採用しました。
c.Header("Location", fmt.Sprintf("/%s", output.ID))
c.JSON(http.StatusCreated, nil)
それからしばらくして、本番環境でフロントエンドとバックエンドの疎通を確認することになりました。
Next.jsからAxiosを使ってリクエストを送信し、リソースのIDを取得してページ遷移しようとしたところ、Locationヘッダーの値を取得することができない。というエラーが発生しました。
ネットワークタブのレスポンスヘッダーを確認してみたところ、確かにLocationヘッダーとその値がセットされていました。また、Swagger UI側からも確認してみましたが、当然、Locationヘッダーがセットされていました。
しかしクライアント側から取得することができません。console.log
でレスポンスヘッダーをコンソールに出力してみると、Content-Length
以外の情報がありませんでした。
Access-Control-Expose-Headersヘッダー
以前の章にもある通り、原因はAccess-Control-Expose-Headers
ヘッダーでした。
Access-Control-Expose-Headers レスポンスヘッダーは、レスポンスの一部としてどのヘッダーを公開するかを、その名前を列挙して示します。
という内容のようです。
規定では7種類のレスポンスヘッダーが公開されるそうなのですが、この中にLocation
ヘッダーは含まれていません。
クライアントが他のヘッダーにアクセスできるようにするには、ここに公開するヘッダーを列挙する必要があります。
Access-Control-Expose-Headers
ヘッダーの構文は以下の通りです。
Access-Control-Expose-Headers: <header-name>, <header-name>,
Access-Control-Expose-Headers: *
まとめ
この記事ではクライアントから特定のレスポンスヘッダーにアクセスできないバグに関する記事をまとめました。