演習a3

httpパッケージについて,極々単純なWebアプリケーションで演習します.

演習(補足:httpパッケージ)

(1)

  • Webブラウザからアクセスすると,"こんにちは,世界!"という文字列と,URLのパスを表示するhttpサーバ(Webサーバ)を作成せよ.このサービスのポートは123456とする.
  • 例えば,http://localhost:123456/abc にアクセスすると,"こんにちは,世界! abc" と表示する.

(2)

  • Webブラウザからアクセスすると,サーバのファイルシステムの内容を表示するhttpサーバ(Webサーバ)を作成せよ.
  • 例えば,http://localhost:123456/index.html にアクセスすると,サーバの実行ディレクトリにあるindex.htmlを表示する.
  • 例えば,http://localhost:123456/fixed/ にアクセスすると,サーバの実行ディレクトリのサブディレクトリfixedの情報を表示する.

(3)

  • 次のようなhttpサーバ(Webサーバ)を作成せよ.
  • http://localhost:123456/J にアクセスすると,"こんにちは,世界!" と表示する.
  • http://localhost:123456/E にアクセスすると,"Hello, World!" と表示する.
  • http://localhost:123456/R/* にアクセスすると(R/の後にどのような文字列があっても),http://localhost:123456/J にリダイレクトする.すなわち,"こんにちは,世界!" と表示する.
  • http://localhost:123456/fixed/* にアクセスすると,サーバの実行ディレクトリのサブディレクトリfixedの情報を表示する.
  • それ以外のURLでアクセスすると,エラーを表示する.

解答例(1)

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "log"
  6. )
  7. func helloHandler(w http.ResponseWriter, r *http.Request) {
  8. fmt.Fprintf(w, "こんにちは,世界! %s", r.URL.Path[1:])
  9. }
  10. func main() {
  11. http.HandleFunc("/", helloHandler)
  12. log.Fatal(http.ListenAndServe(":12345",nil))
  13. }

解説(1)

net/httpパッケージは,拡張可能なHTTPサーバと基本的なHTTPクライアントを提供します.

概要

  • 13行目のhttp.HandleFuncで,Webのルート"/"における全てのリクエストをhelloHandlerで処理することを登録しています.
  • 14行目のhttp.ListenAndServeで,ポート123456でのリクエストの受付を開始しています.
  • 8行目のHelloHandler関数はhttp.HandlerFunc型で,http.ResponseWriteとhttp.Requestを引数として取ります.http.ResponseWriterに書き込むことによってHTTPサーバのレスポンスが生成され,HTTPクライアントにデータが送信されます.
  • http.RequestはHTTPリクエストを格納したデータ構造です.文字列r.URL.Pathは,リクエストのパス部分です.[1:]によって,最初の一文字「/」を除去した部分文字列(スライス)を作成し,それを表示するようにしています.
  • URL http://localhost:10080/golang にアクセスすると「こんにちは世界! golang」と表示されます.

関数,メソッド,型の説明

  • func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
    • DefaultServeMuxのパターン(第1引数patternで指定)に対しハンドラ関数(第2引数handlerで指定)を登録します.DefaultServeMuxは,デフォルトのHTTPリクエストのマルチプレクサです.登録されているパターンのリストと各リクエストのURLを比較し,URLと最も一致するパターンに登録されているハンドラを呼び出す.
    • 第1引数のpatternは/で始まる必要があります.
    • "/favicon.ico"のようにスラッシュで終わらないパターンは固定パスを表し,"/images/"のようにスラッシュで終わるパターンはサブツリーを表します.
    • 長いパターンは短いパターンより優先されます.例えば,"/images/"として登録されたハンドラと,"/images/thumbnails/"として登録されたハンドラがあるとき,"/images/thumbnails/"で始まるパス("/images/thumbnails/thum001.gif"など)は後者のハンドラが呼び出され,それ以外の"/images/"サブツリーへのパス("images/001.gif"など)では前者が呼び出されます.
  • func ListenAndServe(addr string, handler Handler) os.Error
    • TCPネットワークアドレスaddrでリッスンし,HTTP接続を受け付けると,それぞれに新しいサービススレッドを作成します.
    • このサービススレッドはリクエストを読み込み,それに応答するためにhandlerを呼び出します.handlerは通常nilであり,このときはDefaultServeMuxが使われます.

Request型,URL型

type Request struct {

Method string // GET, POST, PUTなど。

RawURL string // リクエストで使われた生のURL。

URL *URL // 解析済みURL。

Proto string // "HTTP/1.0"

ProtoMajor int // 1

ProtoMinor int // 0

Header map[string]string // リクエストの行を値にマップ

Body io.ReadCloser // メッセージのボディ

ContentLength int64 // コンテンツの長さ

TransferEncoding []string // 転送に使われたエンコーディングのリスト

Close bool // 応答後に接続を閉じるかどうか

Host string // URLによって要求されたホスト

Referer string // 参照元URL

UserAgent string // 「User-Agent:」ヘッダの文字列

Form map[string][]string // 解析済みフォーム

Trailer map[string]string // トレーラーのキーを値にマップ

}

type URL struct {

Raw string // 元の文字列

Scheme string // スキーマ

RawAuthority string // [userinfo@]host

RawUserinfo string // ユーザ情報

Host string // ホスト

RawPath string // /path[?query][#fragment]

Path string // /path

OpaquePath bool // パスが不明確 (スキーマが指定されたのにルートからのパスでない)

RawQuery string // クエリ

Fragment string // フラグメント

}

func Fatal(v ...interface{})

  • log.Fatalは,Print()のあとにos.Exit(1)を呼び出すことと同じです.
  • os.Exitは,ステータスコードとともにカレントのプログラムを終了させます.ステータスコードは0のとき成功,それ以外の時エラーを表します.

解答例(2)

  1. package main
  2. import (
  3. "net/http"
  4. "log"
  5. )
  6. func main() {
  7. http.Handle("/", http.FileServer(http.Dir(".")))
  8. log.Fatal(http.ListenAndServe(":12345",nil))
  9. }

解説(2)

概要

  • 8行目のhttp.Handleで,Webのルート"/"における全てのリクエストをhttp.FileServerで処理することを登録しています.
  • 9行目のhttp.ListenAndServeで,ポート123456でのリクエストの受付を開始しています.
  • 8行目のhttp.FileServer関数はHandler型で,カレントディレクトリ"."をルート"/"とするファイルシステムの内容を返すハンドラを返します.
  • これによって,このサーバを実行したディレクトリをWebのルートとするWebサーバとなります.
  • URL http://localhost:123456/ にアクセスすると,カレントディレクトリの内容が表示されます.

関数の説明

func Handle(pattern string, handler Handler)

  • Handleは、DefaultServeMuxのpatternに対しhandlerを登録します。
  • (1)のHandleFuncと同じ機能です.違いは第2引数です.HandleFunc関数はfunc(ResponseWriter, *Request)型を引数にとりますが,Handle関数はHandler型を引数にとります.
  • パッケージには,FileServer,NotFoundHandler,RedirectHandlerというハンドラーを返す関数が定義されています.

func FileServer(root, prefix string) Handler

  • HTTPリクエストに対し,rootを起点とするファイルシステムの内容を返すハンドラを返します.

func NotFoundHandler() Handler

  • 各リクエストに対して“404 page not found”を返すだけの単純なリクエストハンドラを返します.

func RedirectHandler(url string, code int) Handler

  • 各リクエストに対して与えられたurlとステータスコードでリダイレクトを行うリクエストハンドラを返します.

解答例(3)

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "log"
  6. )
  7. func helloJHandler(w http.ResponseWriter, r *http.Request) {
  8. fmt.Fprintf(w, "こんにちは,世界!")
  9. }
  10. func helloEHandler(w http.ResponseWriter, r *http.Request) {
  11. fmt.Fprintf(w, "Hello, World!")
  12. }
  13. func main() {
  14. http.HandleFunc("/J", helloJHandler)
  15. http.HandleFunc("/E", helloEHandler)
  16. http.Handle("/fixed/", http.FileServer(http.Dir(".")))
  17. http.Handle("/R/", http.RedirectHandler("/J", 301))
  18. log.Fatal(http.ListenAndServe(":10080",nil))
  19. }

解説(3)

  • 8行目:helloJHandler:"こんにちは,世界!" と表示するハンドラー.
  • 12行目:helloEHandler:"Hello, World!" と表示する.
  • 17行目:"/J"にアクセスするとhelloJHandlerを呼ぶ.
  • 18行目:"/E"にアクセスするとhelloEHandlerを呼ぶ.
  • 19行目:"/fixed/"にアクセスすると,そのディレクトリのファイルを表示する..FileServerについては,解説(2)を参照.
  • 20行目:"/R/"にアクセスすると,"/J"にリダイレクト.RedirectHandlerについては,解説(2)を参照.301は,指定したリソースが新しいURIに移動したことを表すステータスコード.