概要
文字通りhttpのredirectを行う機能です。
下記の通り、構文はシンプルです。
http.Redirect(w http.ResponseWriter, r *http.Request, url string, code int)
最後のコードはstatus codeです。「http.StatusFound」のように記述します。
今回の例は、viewとeditの2ページ構成とし、
「edit」のsaveボタンを押下すると、url末尾のPathをtitleとするテキストファイルが保存され、
そのテキストファイルの中身を読み込むhtml「view」が閲覧できる仕様とします。
http.Redirectは、上記保存完了後と、
存在しないpathのviewにアクセスしようとした場合の、2箇所で使用します。
事前にhtmlファイル(今回はview.html)を同階層に作成し、
後で示すコードを記述したmain.goを実行します。
htmlファイルの記述
まずはtemplateとなるhtmlファイルの記述方法です。
基本的には通常のhtmlと同じ書き方ですが、
goファイルから渡されるPage structの中身などを表示したい場合に、工夫が必要です。
view.html
<!-- 「.<フィールド名>」で、http.ResponseWriterに登録したgoファイルの変数を表示 --> <h1>{{.Title}}</h1> <!-- editは別途必要。文字列の中にも「.<フィールド名>」を記述できる --> <p><a href="/edit/{{.Title}}">Edit</a></p> <!-- 「printf %s .<フィールド名>」で、byte配列をキャスト --> <div>{{printf "%s" .Body}}</div>
edit.html
<h1>Editing {{.Title}}</h1> <!-- actionは"/save/" + title --> <form action="/save/{{.Title}}" method="POST"> <div> <!-- nameは"body" --> <textarea name="body" rows="20" cols="80">{{printf "%s" .Body}}</textarea> </div> <div> <input type="submit" value="Save"/> </div> </form>
上記のように、structの場合、「.<フィールド名>」と記述すれば、
template.ParseFiles("~.html").Execute(w http.ResponseWriter, i interface{}) で登録した
goファイルからの内容を表示することができます。
ただし、byte配列の場合は「printf %s .<フィールド名>」としてstring型にキャストします。
goファイルの記述
saveHandler内の処理は、htmlファイルのtemplateに記載した、
edit.htmlのformがsubmitされた際のactionにより、実行されます。
type Page struct { Title string Body []byte } func (p *Page) save() error { // ファイルを作成するメソッド filename := p.Title + ".txt" return ioutil.WriteFile(filename, p.Body, 0600) // Webサーバーを起動したユーザーが読み書きできる権限設定 } func load(title string) (*Page, error) { filename := title + ".txt" body, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return &Page{Title: title, Body: body}, nil } func renderTemplate(w http.ResponseWriter, templ string, p *Page) { t, _ := template.ParseFiles(templ + ".html") // 同階層にあるhtmlファイルを読み込む t.Execute(w, p) // 読み込んだ内容をhttp.ResponseWriterに登録する } func viewHandler(w http.ResponseWriter, r *http.Request) { // /view/~にアクセスした場合のHandler title := r.URL.Path[len("/view/"):] p, err := load(title) if err != nil { http.Redirect(w, r, "/edit/"+title, http.StatusFound) // 存在しない.txtファイルのtitleの場合、新規ファイルのeditにredirect return } renderTemplate(w, "view", p) // templateを使用する } func editHandler(w http.ResponseWriter, r *http.Request) { // /edit/~にアクセスした場合のHandler title := r.URL.Path[len("/edit/"):] p, err := load(title) if err != nil { p = &Page{Title: title} // htmlファイルの{{.Title}}に反映される } renderTemplate(w, "edit", p) } func saveHandler(w http.ResponseWriter, r *http.Request) { // /save/~にアクセスした場合のHandler title := r.URL.Path[len("/save/"):] body := r.FormValue("body") // htmlファイル内のform name="body"の内容をstring型で返す p := &Page{Title: title, Body: []byte(body)} err := p.save() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) // 500エラーを返す return } http.Redirect(w, r, "/view/"+title, http.StatusFound) // 成功したらviewへredirect } func main() { http.HandleFunc("/view/", viewHandler) http.HandleFunc("/edit/", editHandler) http.HandleFunc("/save/", saveHandler) log.Fatalln(http.ListenAndServe(":8080", nil)) // Handlerを事前に登録してからサーバーを起動。「:」の左に何も記述がない場合、ローカルサーバー。第2引数にnilを指定するとdefault設定が適用される }
上記main.goを実行し、http://localhost:8080/view/testにアクセスします。
関連記事
tech-up.hatenablog.com