HTTP応答にコンテンツを書き込む3つの方法を見てきました。
func Handler(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "blabla.\n")
}
そして:
func Handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("blabla\n"))
}
またあります:
fmt.Fprintf(w, "blabla")
それらの違いは何ですか?どちらを使用するのが好ましいですか?
io.Writer
出力ストリームは、バイトシーケンスを書き込むことができるターゲットを表します。 Goでは、これは一般的な io.Writer
インターフェイスによってキャプチャされます。
type Writer interface {
Write(p []byte) (n int, err error)
}
この単一のWrite()
メソッドを持つすべてのものを出力として使用できます。たとえば、ディスク上のファイル( os.File
)、ネットワーク接続( net.Conn
)またはメモリ内バッファ( bytes.Buffer
)。
HTTP応答を構成し、クライアントにデータを送信するために使用される http.ResponseWriter
もそのようなio.Writer
であり、送信するデータ(応答本文)が組み立てられます(必ずしも一度だけではなく)ResponseWriter.Write()
(一般的なio.Writer
を実装するため)を呼び出します。これは、http.ResponseWriter
インターフェースの実装について(ボディの送信に関して)唯一の保証です。
WriteString()
次に、WriteString()
に進みます。多くの場合、テキストデータをio.Writer
に書き込みます。はい、それはstring
を[]byte
に変換するだけで可能です。
w.Write([]byte("Hello"))
期待どおりに動作します。ただし、これは非常に頻繁な操作であるため、 io.StringWriter
インターフェイス( Go 1.11 、それが輸出されなかった前に):
type StringWriter interface {
WriteString(s string) (n int, err error)
}
このメソッドにより、[]byte
の代わりにstring
値を書き込むことができます。そのため、(io.Writer
も実装する)何かがこのメソッドを実装する場合、[]byte
変換なしでstring
値を渡すことができます。 これはコードの単純な単純化のようですが、それ以上です。string
を[]byte
に変換するには、string
コンテンツのコピーを作成する必要があります(string
の値はGoでは不変です。詳細についてはこちらをご覧ください: golang:[] byte(string)vs [] byte(* string) ) string
が「より大きい」場合、および/またはこれを何度も行う必要がある場合に顕著になります。
io.Writer
の性質と実装の詳細に応じて、string
の内容を[]byte
に変換せずに記述して、上記のオーバーヘッドを回避できる場合があります。
例として、io.Writer
がメモリ内バッファーに書き込むものである場合(bytes.Buffer
はそのような例です)、組み込みの copy()
関数を使用できます。
Copy組み込み関数は、ソーススライスからターゲットスライスに要素をコピーします。 (特別な場合として、文字列からバイトをスライスにコピーします。)
copy()
は、string
を[]byte
に変換せずに、string
の内容(バイト)を[]byte
にコピーするために使用できます。例:
buf := make([]byte, 100)
copy(buf, "Hello")
現在、「ユーティリティ」関数 io.WriteString()
があり、string
をio.Writer
に書き込みます。しかし、最初に、渡されたio.Writer
がWriteString()
メソッドを持っているかどうかをチェックすることでこれを行います。もしそうなら、それが使用されるでしょう(実装はおそらくより効率的です)。渡されたio.Writer
にそのようなメソッドがない場合、一般的なconvert-to-byte-slice-and-write-thatメソッドが「フォールバック」として使用されます。
このWriteString()
はメモリ内バッファの場合にのみ有効であると思われるかもしれませんが、そうではありません。 Web要求の応答もしばしば(メモリ内バッファを使用して)バッファされるため、http.ResponseWriter
の場合もパフォーマンスが向上する可能性があります。そして、http.ResponseWriter
の実装を見ると、それはWriteString()
(現在の行#1212)を実装する、エクスポートされていない型http.response
( server.go
現在行#308)です。改善。
全体として、string
値を記述するときは常に、io.WriteString()
を使用することをお勧めします。より効率的(高速)になる可能性があるためです。
fmt.Fprintf()
これは、多少パフォーマンスが低下する代わりに、書き込みたいデータにさらに多くのフォーマットを追加する便利で簡単な方法と見なすべきです。
したがって、簡単な方法でフォーマットされたstring
を作成する場合は、 fmt.Fprintf()
を使用します。例:
name := "Bob"
age := 23
fmt.Fprintf(w, "Hi, my name is %s and I'm %d years old.", name, age)
これにより、次のstring
が書き込まれます。
Hi, my name is Bob and I'm 23 years old.
忘れてはならないことの1つは、fmt.Fprintf()
にはformat stringが必要であるため、前処理され、そのまま出力に書き込まれないことです。簡単な例として:
fmt.Fprintf(w, "100 %%")
"100 %%"
が(2文字の%
文字で)出力に書き込まれることを期待しますが、フォーマット文字列%
は特殊文字であり、%%
は出力に1つの%
のみを送信するように送信されます。
string
パッケージを使用してfmt
を記述したい場合は、フォーマットstring
を必要としない fmt.Fprint()
を使用します。
fmt.Fprint(w, "Hello")
fmt
パッケージを使用するもう1つの利点は、string
sだけでなく、他のタイプの値も書き込むことができることです。
fmt.Fprint(w, 23, time.Now())
(もちろん、任意の値をstring
に、そして最終的に一連のバイトに変換するルールは、fmt
パッケージのドキュメントで明確に定義されています。)
「単純な」形式の出力の場合、fmt
パッケージは問題ない場合があります。複雑な出力ドキュメントの場合、 text/template
(一般的なテキストの場合)および html/template
(出力がHTMLの場合)の使用を検討してください。 )。
http.ResponseWriter
の受け渡し完全を期すために、Webレスポンスとして送信するコンテンツは、結果の「ストリーミング」をサポートする「何か」によって生成されることが多いことに言及する必要があります。例は、構造体またはマップから生成されるJSON応答です。
そのような場合、http.ResponseWriter
であるio.Writer
をこれに渡すか、渡すのがより効率的であることがよくあります-somethingその場でio.Writer
への結果の書き込みをサポートしている場合。
これの良い例は、JSON応答の生成です。もちろん、 json.Marshal()
を使用してオブジェクトをJSONにマーシャリングできます。これにより、バイトスライスが返され、ResponseWriter.Write()
を呼び出すだけで送信できます。
ただし、json
パッケージにio.Writer
があり、最終的には結果を送信したいことを知らせる方が効率的です。この方法では、最初にバッファーにJSONテキストを生成する必要はありません。これを応答に書き込んでから破棄します。新しい json.Encoder
を作成するには、http.ResponseWriter
をio.Writer
として渡すことができる json.NewEncoder()
を呼び出して、呼び出します。 Encoder.Encode()
その後、JSONの結果が直接応答ライターに書き込まれます。
ここでの短所の1つは、JSON応答の生成に失敗した場合、部分的に送信/コミットされた応答が返される可能性があることです。これが問題になる場合は、バッファに応答を生成する以外の選択肢はありません。マーシャリングが成功した場合は、すぐに完全な応答を作成できます。
here(ResponseWriter) からわかるように、これはWrite([]byte) (int, error)
メソッドとのインターフェースです。
したがって、_io.WriteString
_と_fmt.Fprintf
_では、両方とも Writer をWrite(p []byte) (n int, err error)
メソッドをラップするインターフェイスでもある第1引数として取ります
_type Writer interface {
Write(p []byte) (n int, err error)
}
_
Io.WriteString(w、 "blah")を呼び出すとき ここでチェック
_func WriteString(w Writer, s string) (n int, err error) {
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s)
}
return w.Write([]byte(s))
}
_
またはfmt.Fprintf(w、 "blabla") ここをチェック
_func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
n, err = w.Write(p.buf)
p.free()
return
}
_
両方のメソッドでResponseWriter
変数を渡すため、間接的にWriteメソッドを呼び出すだけです。
したがって、w.Write([]byte("blabla\n"))
を使用して直接呼び出してはいけません。答えが得られれば幸いです。
PS:JSON応答として送信する場合は、それを使用する別の方法もあります。
_json.NewEncoder(w).Encode(wrapper)
//Encode take interface as an argument. Wrapper can be:
//wrapper := SuccessResponseWrapper{Success:true, Data:data}
_