1
1

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 5 years have passed since last update.

Google App EngineのHTTPレスポンスヘッダにContent-Typeを書き込む順序について

Last updated at Posted at 2019-04-05

net/http の ResponseWriter へのレスポンスヘッダへの書き込みは、以下のようにWriteしたあとに変更しても影響はないとされている。

Changing the header map after a call to WriteHeader (or Write) has no effect unless the modified headers are trailers.
https://golang.org/src/net/http/server.go#L99

またGoogle App Engineのドキュメントには、BodyからContent-Typeを推定すると記載されている。

Content-Type
If you do not explicitly set this header, the http.ResponseWriter class detects the content type from the start of the response body, and sets the Content-Type header accordingly.
このヘッダーを明示的に設定しない場合は、http.ResponseWriter クラスがレスポンス本文の先頭からコンテンツ タイプを検出し、それに応じて Content-Type ヘッダーが設定されます。

しかし上記の仕様とは異なる結果となることがあり、
実行環境、Content-Typeを設定するタイミングHTTPメソッドによってResponseとして返ってくるContent-Typeが変わる。

ローカルで実行したときの4つの結果はすべて想定通りである。

想定外の挙動は以下の2つ。

  1. GAE(appdngine.Main)とGAE(http.ListenAndServe)どちらもGETのときはContent-Typeがapplication/jsonにならない。
  2. GAE(appdngine.Main) | Writeの後 | POST の場合に、Content-Typeがapplication/jsonになっている。
実行環境 Content-Typeを設定するタイミング HTTPメソッド Responseとして返ってくるContent-Type
ローカル Writeの前 GET application/json
ローカル Writeの前 POST application/json
ローカル Writeの後 GET text/plain; charset=utf-8
ローカル Writeの後 POST text/plain; charset=utf-8
GAE(appdngine.Main) Writeの前 GET text/html; charset=UTF-8
GAE(appdngine.Main) Writeの前 POST application/json
GAE(appdngine.Main) Writeの後 GET text/html; charset=UTF-8
GAE(appdngine.Main) Writeの後 POST application/json
GAE(http.ListenAndServe) Writeの前 GET text/html; charset=UTF-8
GAE(http.ListenAndServe) Writeの前 POST application/json
GAE(http.ListenAndServe) Writeの後 GET text/html; charset=UTF-8
GAE(http.ListenAndServe) Writeの後 POST text/plain; charset=utf-8

まだコードは読めていないが予想としては、UTF-8とutf-8と大文字、小文字のパターンが存在している。Go標準は小文字なので、大文字のときはGAEが書き換えている可能性がある。
つまりGAEでGETのときはresponseを書き換えられている。GAE(http.ListenAndServe) | Writeの後 | POST のときだけは、Go標準の挙動としてtext/plain; charset=utf-8が返っている。

検証コード

package main

import (
	"net/http"

	"google.golang.org/appengine"
)

// 正しい実装
func handler1(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte(`{"test": "hello"}`))
}
// 正しくない実装
func handler2(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`{"test": "hello"}`))
	w.Header().Set("Content-Type", "application/json")
}

func main() {
	http.HandleFunc("/1", handler1)
	http.HandleFunc("/2", handler2)

	// ローカル
	// http.ListenAndServe(":8080", nil)
	// GAE(appdngine.Main)
	// appengine.Main()
	// GAE(http.ListenAndServe)
	// http.ListenAndServe(fmt.Sprintf(":%s", os.Getenv("PORT")), nil) 
}

まだ実装の調査できてないけどいったんメモです。

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?