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

More than 3 years have passed since last update.

Golang入門.1 -http.HandleでHello World!-

Last updated at Posted at 2020-03-22

はじめに

 他の言語でweb開発の経験がある人 (筆者) がgolangに入門する際に勉強したことを一連の記事でまとめていきます。

やること

 記事においてはweb開発において必要な機能をはじめとした、ある程度実践的な範囲を対象とします。基本的には対象とする機能を実装したコードを記載します。またコードだけではなく、ドキュメントを踏まえた概念的な説明を挟んでいきます。

やらないこと

 開発環境の構築方法や基本文法の説明は省略します。

今回のテーマ

 http.Handleを用いてHello World!を表示させるまでの流れを説明します。

概念編

 以下は[net/httpのdocument] (https://godoc.org/net/http)から抜粋しています。
golangでは他の言語と同様にHTTP requestに応じてresponseを生成します。ざっくりとした流れは以下の通りです (簡単のため、今回はURLにのみ注目します) 。

  1. HTTP requestの中身を確認する
  2. URLをparseし、事前に登録しておいたpatternとmatchするものを探す
  3. matchするpatternがあれば、そのpatternに応じたhandlerを元にresponseを生成 (なければerror処理)

 上記の流れで必要なpatternとのmatchを行なったり、patternとhandlerの対応を登録したりするものをマルチプレクサと言います。net/httpにおいてはfunc Handleを呼び出した際に、引数として渡したhandlerが[type ServeMux] (https://godoc.org/net/http#ServeMux)に登録されています。そして、2や3においてServeMuxの情報を用いて処理が実行されます。

コード編

[github] (https://github.com/ryuji0123/go-samples/blob/master/mux.go)

mux.go
package main
import (
        "fmt"
        "net/http"
)

type HelloHandler struct{}

func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
}

func main() {
        hello := HelloHandler{}
        server := http.Server{
                Addr: "127.0.0.1:8080",
        }
        http.Handle("/", &hello)
        server.ListenAndServe()
}

コードの実行・確認

 ターミナルから実行できます。

go run mux.go

 ブラウザ上でlocalohost:8080と入力すれば結果が見れます。また、

curl localhost:8080/

としても確認できます。

コードの解説

 上記のコードのうち、以下の部分がやや奇妙な挙動をしているように感じます。

func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello World!")
}

fmt.Fprintfという部分からprint関数を呼び出していることがわかります。print関数といえば、ターミナルに出力を返すものとして使用することが多いと思います。例えばお馴染みのpython3では以下のように書きます。

print('Hello World!')

両者の違いは何でしょうか?ポイントはFprintf関数の第1引数として指定されているhttp.ResponseWriterです。
 fmt.Fprintfのdocumentによると以下のように説明がなされています。

func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
Fprintf formats according to a format specifier and writes to w. It returns the number of bytes written and any write error encountered.

 第1引数で指定したinput/output writerに出力先を指定していることが分かります。また、[実際のコード] (https://golang.org/src/fmt/print.go?s=5516:5593#L192)では以下のように記述されています。

// Fprintf formats according to a format specifier and writes to w.
// It returns the number of bytes written and any write error encountered.
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
}

 全てのコードを掲載するのは冗長なので流れを説明します。Fprintfで行なっている操作は以下の通りです。

  1. printer (p) の生成
  2. doPrintfを実行し、printerのbufferに情報を格納。この時、入力されたformatの型がstringからbyteに変換される。
  3. printerのbufferから情報を読み取りi/o writerが示す出力先に出力
  4. printerの解放

 このように、i/oなどにおいて一時的に情報を格納する領域をbufferと言います。fmtにおいてはprinterをtype pp structという構造体で定義した上で上記のようなbufferを用いた出力を可能にしています。なお、golangと対比させる形で紹介したpythonでも省略されている第3引数fileに明示的に出力先を指定することができます。これにより、stdoutつまりターミナルから別の場所へと出力先を変更できます。

最後に

 今後もライブラリのソースコードやdocumentの記載を用いる形で解説を行なっていきたいと思います。

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