1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Go】レスポンス

Last updated at Posted at 2024-07-10

はじめに

前回はリクエストの*http.Requestに焦点を当てて、どのようにリクエストの中身を読み取るのかについてまとめました。

今回はhttp.ResponseWriterとJSONに焦点を当ててまとめてみたいと思います。

ResponseWriter

サーバーを立てるときにはhttp.ResponseWriter*http.Requestを引数とした関数を用意します。

余談ですが、http.ResponseWriterはインターフェースなので一見値渡しのように見えますが、実際はresponseの参照渡しです。

ResponseWriterインターフェースには次の3つのメソッドがあります。

メソッド 説明
Write レスポンスボディにデータを書き込みます。引数としてバイトスライスを受け取り、書き込んだバイト数とエラーを返します。
WriteHeader レスポンスのステータスコードを設定します。通常、ステータスコードは最初の書き込みが行われる前に設定します。
Header HTTPレスポンスヘッダを返します。レスポンスヘッダを設定するために使用します。

ResponseWriterへの書き込み

では、Writeメソッドを使用して以下のように書き込んでみます。
Writeは引数にバイトスライスを受け取り、それをHTTPレスポンスのボディに書き込みます。

package main

import (
	"fmt"
	"net/http"
)

func writeExample(w http.ResponseWriter, r *http.Request) {
	// レスポンスボディにHTMLを書き込む
	html := `<html>
<head><title>Example</title></head>
<body><h1>Hello, World!</h1></body>
</html>`

	w.Write([]byte(html))
}

func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}

	http.HandleFunc("/write", writeExample)
	fmt.Println("Starting server at http://127.0.0.1:8080")
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println("Error starting server:", err)
	}
}

リクエストを投げてみると、

%curl -i http://127.0.0.1:8080/write

以下のようにhtmlが返却されます。

HTTP/1.1 200 OK
Date: Tue, 09 Jul 2024 14:16:24 GMT
Content-Length: 86
Content-Type: text/html; charset=utf-8

<html>
<head><title>Example</title></head>
<body><h1>Hello, World!</h1></body>
</html>%     

これまた余談ですが、なぜhtmlをバイトスライスに変換しているんだろうと気になり調べたところシリアライズというのがキーワードなのですね。お恥ずかしいですが、知りませんでした。。。

シリアライズすることで異なる環境やシステム間でデータを一貫して扱うことができるので、裏を返せばバイトスライスにしないと他の環境で読み込めない、となり得るということです。

io.WriteStringというものも用意されていて、こちらは引数に文字列を渡すことでレスポンスにデータを書き込めます。

WriteHeader

次にWriteHeaderですが、これはHTTPレスポンスのステータスコードを設定するためのメソッドです。
何も指定しない場合はデフォルトで200 OKをセットします。

また、WriteHeaderは呼び出された後にヘッダが変更されるのを防ぐので、コードの順番には注意が必要です。

例えば以下のようにWriteHeader(501)とすることでステータスコードを501としてレスポンスを返すことができます。

...
func WriteHeaderExample(w http.ResponseWriter, r *http.Request){
	w.WriteHeader(501)
	fmt.Fprintln(w, "そのようなサービスはありません。")
}


func main() {
	server := http.Server{
		Addr: "127.0.0.1:8080",
	}

	http.HandleFunc("/writeheader", WriteHeaderExample)
	err := server.ListenAndServe()
	if err != nil {
		fmt.Println("Error starting server:", err)
	}
}

リクエストを投げると、

%curl -i http://127.0.0.1:8080/writeheader

次のようにステータスコードが501で返却されます。

HTTP/1.1 501 Not Implemented
Date: Tue, 09 Jul 2024 14:21:25 GMT
Content-Length: 49
Content-Type: text/plain; charset=utf-8

そのようなサービスはありません。

それにしても名前がややこしいです。。。WriteHeaderはヘッダーの設定っぽい名前ですが、ステータスコードをセットするメソッドです。

ヘッダーをセットするのはHeaderメソッドの方なので勘違いしないよう注意が必要ですね。

Header

最後にHeaderメソッドを見てみます。
Headerメソッドは、HTTPレスポンスのヘッダを操作するために使用されます。このメソッドは、レスポンスヘッダを設定するために必要なヘッダオブジェクトを返します。

定義を一部抜粋しましたが、HeaderにはAddSet,Getなどのメソッドが用意されています。(個人的にはhasが気になりました。)

func (h Header) Add(key, value string) {
	textproto.MIMEHeader(h).Add(key, value)
}

func (h Header) Set(key, value string) {
	textproto.MIMEHeader(h).Set(key, value)
}

func (h Header) Get(key string) string {
	return textproto.MIMEHeader(h).Get(key)
}
...

今回はこの中のSetを使用してヘッダーに追加してみたいと思います。ちなみにSetAddと違って既存の値がある場合は値を置き換えるので注意が必要です。追加したいならAddを使用しましょう。

先ほどのコードに追加します。

...
func HeaderExample(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Location", "http://google.com")
	w.WriteHeader(302)
}
...
func main() {

...
	http.HandleFunc("/redirect", HeaderExample)
 ...

これでリクエストを投げると、

%curl -i http://127.0.0.1:8080/redirect   

Googleにリダイレクトされます。

HTTP/1.1 302 Found
Location: http://google.com
Date: Tue, 09 Jul 2024 14:34:58 GMT
Content-Length: 0

JSONの書き込み

最後にJSONの書き込み方についてです。
先ほどのコードに追加します。

まず、JSONはContent-Typeapplication/jsonなので、w.Header.Set(application/json)でセットする必要があります。

そして、Post構造体を用意し、使用します。これをjson.MarshalでJSON形式のバイトスライスに変換して、w.Write()で書き込みます。

...
type Post struct{
	User 		string
	Threads []string
}
...
func JsonExample(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Content-Type", "application/json")
	post := &Post{
		User: "TestUser",
		Threads: []string{"1", "2", "3"},
	}
	json, _ := json.Marshal(post)
	w.Write(json)
}

func main() {
...
	http.HandleFunc("/json", JsonExample)
...

リクエストを投げると、

%curl -i http://127.0.0.1:8080/json   

以下のようにJSONが返ってきます。

HTTP/1.1 200 OK
Content-Type: application/json
Date: Tue, 09 Jul 2024 14:53:52 GMT
Content-Length: 43

{"User":"TestUser","Threads":["1","2","3"]}

まとめ

今回はhttp.ResponseWriter+JSONの書き込みについて以下の項目でまとめてみました。

  • Write()
  • WriteHeader()
  • Header()
  • json.Marshal

個人的にWriteHeader()の名前が分かりづらいから変えてほしい。。。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?