演習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)
- package main
- import (
- "fmt"
- "net/http"
- "log"
- )
- func helloHandler(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "こんにちは,世界! %s", r.URL.Path[1:])
- }
- func main() {
- http.HandleFunc("/", helloHandler)
- log.Fatal(http.ListenAndServe(":12345",nil))
- }
解説(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)
- package main
- import (
- "net/http"
- "log"
- )
- func main() {
- http.Handle("/", http.FileServer(http.Dir(".")))
- log.Fatal(http.ListenAndServe(":12345",nil))
- }
解説(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)
- package main
- import (
- "fmt"
- "net/http"
- "log"
- )
- func helloJHandler(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "こんにちは,世界!")
- }
- func helloEHandler(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hello, World!")
- }
- func main() {
- http.HandleFunc("/J", helloJHandler)
- http.HandleFunc("/E", helloEHandler)
- http.Handle("/fixed/", http.FileServer(http.Dir(".")))
- http.Handle("/R/", http.RedirectHandler("/J", 301))
- log.Fatal(http.ListenAndServe(":10080",nil))
- }
解説(3)
- 8行目:helloJHandler:"こんにちは,世界!" と表示するハンドラー.
- 12行目:helloEHandler:"Hello, World!" と表示する.
- 17行目:"/J"にアクセスするとhelloJHandlerを呼ぶ.
- 18行目:"/E"にアクセスするとhelloEHandlerを呼ぶ.
- 19行目:"/fixed/"にアクセスすると,そのディレクトリのファイルを表示する..FileServerについては,解説(2)を参照.
- 20行目:"/R/"にアクセスすると,"/J"にリダイレクト.RedirectHandlerについては,解説(2)を参照.301は,指定したリソースが新しいURIに移動したことを表すステータスコード.