Open3

net/httpで静的ファイルを送信しながら任意の処理を実現する方法が知りたい

TansanSui-H2CO3TansanSui-H2CO3

やりたいこと

  • 「localhost:8080/」のformに値を入力
  • 「localhost:8080/submit.html」へsubmit (POST)
    • 遷移先では「処理中です」などのダイアログを表示
  • サーバでPOSTされた値を処理
  • サーバの処理が終了次第,「localhost:8080/」に遷移させる

このスクラップの目標

  1. クライアントからPOSTされたデータをサーバで受け取り,なんらかの処理を施す
  2. それと同時に,任意の静的htmlファイルをクライアントへ返す

※サーバ側の処理が終了した後の,クライアント側の画面遷移に関しては別のスクラップにまとめようと思います.

(ここに,該当スクラップのURLを貼る)

最初に書いたコード

package main

import (
	"log"
	"net/http"
)

func main() {
	serve()
}

func submitHandler(res http.ResponseWriter, req *http.Request) {
	// POSTされた値を取得
	content := req.FormValue("content")
	
	// contentを出力(ここは好きな処理を記述する予定)
	log.Println(content)
}

// サーバを立てる
func serve() {
	fs := http.FileServer(http.Dir("./static/html/"))
	http.Handle("/", fs) // 静的ファイルの送信
	http.HandleFunc("/submit.html", submitHandler) // submit.htmlに遷移したときに別処理させたい
	log.Fatal(http.ListenAndServe(":8080", nil))
}

発生している問題

  • 「localhost:8080/submit.html」にアクセスしたとき,submitHandler()の処理はされるが静的ファイル(./static/html/submit.html)がブラウザ側で反映されない
    • つまり,http.HandleFunc()が優先的に実行され,http.Handle()の静的ファイル送信ができていない

困っていること

  • 静的ファイルを送信するためにはhttp.Handle()が必要だが,ページごとに処理を分けて記述するためにはhttp.HandleFunc()が必要.これらを同時に満たすような記述方法はないだろうか...
  • 一番愚直なのは,htmlファイルべた書きを持っておいて,fmt.Fprint(res, [べた書きhtml])を使ってブラウザに反映させる方法だと思うが,もっとスマートな方法があれば知りたい
TansanSui-H2CO3TansanSui-H2CO3

考えた解決策

  • io/ioutilを使って目当てのhtmlファイルの中身を取ってくる
  • fmt.Fprint(res, [取ってきた中身])で反映させる

書いたコード

  • readFileメソッドでファイルの中身を取ってくる
func readFile(fileName string) string {
	bytes, err := ioutil.ReadFile("./static/html/" + fileName)
	if err != nil {
		panic(err)
	}

	return string(bytes)
}
  • fmt.Fprint(res, readFile("submit.html"))submitHandler()内に追加し,反映させる

現時点のコード

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	serve()
}

func submitHandler(res http.ResponseWriter, req *http.Request) {
	content := req.FormValue("content")
	fmt.Fprint(res, readFile("submit.html")) // ページ反映
	
	// contentを出力(ここは好きな処理を記述する予定)
	log.Println(content)
}

func readFile(fileName string) string {
	bytes, err := ioutil.ReadFile("./static/html/" + fileName)
	if err != nil {
		panic(err)
	}

	return string(bytes)
}

func serve() {
	http.Handle("/", http.FileServer(http.Dir("./static/html")))
	http.HandleFunc("/submit.html", submitHandler)
	log.Fatal(http.ListenAndServe(":8080", nil))
}
TansanSui-H2CO3TansanSui-H2CO3

他の解決策

  • html/templateを活用
    • こっちの方が応用が効いて良さそう

改変案

func submitHandler(res http.ResponseWriter, req *http.Request) {
	content := req.FormValue("content")
	tplt := template.Must(template.ParseFiles("./static/html/submit.html"))
	err := tplt.Execute(res, "")
	if err != nil {
		panic(err.Error())
	}
	
	// contentを出力(ここは好きな処理を記述する予定)
	log.Println(content)
}
  • やっていることは単純で,./static/html/submit.htmlをテンプレートファイルとして呼び出して,適当な値をページに渡しながらレスポンス(Executeメソッドは第2引数が必須だった)
    • ./static/html/submit.htmlの中に{{.}}の記述がない限りは静的htmlファイルの中身がそのまま表示される
    • サーバ側からクライアントへ値を渡すように仕様変更されたとき,改変箇所が少なくて済むのも良い気がする