目次
EchoのTemplates機能でForm送信を実装
前の記事でJSONレスポンスのサンプルを、HTMLのformタグから送信するように変更、ブラウザから登録・変更・削除できるようにします。
ディレクトリ構造(変更)
ディレクトリにHTMLテンプレートを追加して、構造は下記のように変更になります。
└── src
└── sample
├── controller
│ └─ user_controller.go
├── go.mod
├── go.sum
├── main.go
├── model
│ ├── db.go
│ └── user.go
├── public
│ └── views
│ ├── signup.html
│ └── user.html
├── router
│ └── router.go
追加変更したソース
前の記事から追加変更したソースのみ記載します。
main.go、そしてmodelフォルダ内のdb.goとuser.goは変更ありません。
controller/user_controller.go
ここにテンプレート機能の呼び出しを追加しています。
重要なポイントとして、現在のブラウザはmethodがGETかPOSTの送信方式にしか対応していません。
PUTやDELETEを指定しても非対応です。
これがPHPなどでは、<input type=”hidden” name=”_method” value=”PUT”>とformタグ内に入れることでPUT方式として受信することができますが、Goの場合はそのままで送信してもGET扱いになります。
Echoの公式ドキュメントにmethodの上書きについて記載があります。
Method OverrideするmiddleWareを追加して、PUTやDELETEに対応させるようにします。
https://echo.labstack.com/middleware/method-override
※本当はユーザー情報の登録や更新に際して、入力内容のバリデーションやサニタイズなどのセキュリティ対策をすべきです。ですのでこのサンプルソースをそのままプロダクトに利用しないでください。この記事ではあくまでもTemplates機能の説明のみに焦点を当てています。
package controller
import (
"net/http"
"strconv"
"sample/model"
"github.com/labstack/echo/v4"
)
type User struct {
ID uint `json:"id" form:"id" query:"id"`
Name string `json:"name" form:"name" query:"name"`
Age uint `json:"age" form:"age" query:"age"`
}
func MethodOverride(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if c.Request().Method == "POST" {
method := c.Request().PostFormValue("_method")
if method == "PUT" || method == "PATCH" || method == "DELETE" {
c.Request().Method = method
}
}
return next(c)
}
}
func SignUp(c echo.Context) error {
return c.Render(http.StatusOK, "signup.html", map[string]interface{}{
"title": "Sign Up Form",
})
}
func GetUser(c echo.Context) error {
i, _ := strconv.Atoi(c.Param("id"))
id := uint(i)
user := model.User{}
user.FirstById(id)
return c.Render(http.StatusOK, "user.html", map[string]interface{}{
"title": "Edit Your Account",
"id": user.ID,
"name": user.Name,
"age": user.Age,
})
}
func CreateUser(c echo.Context) error {
name := c.FormValue("name")
a, _ := strconv.Atoi(c.FormValue("age"))
age := uint(a)
user := model.User{
Name: name,
Age: age,
}
user.Create()
return c.Render(http.StatusOK, "user.html", map[string]interface{}{
"title": "Edit Your Account",
"id": user.ID,
"name": user.Name,
"age": user.Age,
})
}
func UpdateUser(c echo.Context) error {
i, _ := strconv.Atoi(c.Param("id"))
id := uint(i)
name := c.FormValue("name")
a, _ := strconv.Atoi(c.FormValue("age"))
age := uint(a)
user := model.User{
ID: id,
Name: name,
Age: age,
}
user.Updates()
return c.Render(http.StatusOK, "user.html", map[string]interface{}{
"title": "Updated Your Account",
"id": user.ID,
"name": user.Name,
"age": user.Age,
})
}
func DeleteUser(c echo.Context) error {
i, _ := strconv.Atoi(c.Param("id"))
id := uint(i)
user := model.User{}
user.DeleteById(id)
return c.Render(http.StatusOK, "signup.html", map[string]interface{}{
"title": "Sign Up Form",
})
}
router/router.go
ルーティングに先ほどのmiddleWareを事前に読み込む記述、e.Pre(controller.MethodOverride)を追記します。
そのほかにもテンプレートを読み込む記述を追記します。
package router
import (
"io"
"sample/controller"
"text/template"
"github.com/labstack/echo/v4"
)
// TemplateRenderer is a custom html/template renderer for Echo framework
type TemplateRenderer struct {
templates *template.Template
}
// Render renders a template document
func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
// Add global methods if data is a map
if viewContext, isMap := data.(map[string]interface{}); isMap {
viewContext["reverse"] = c.Echo().Reverse
}
return t.templates.ExecuteTemplate(w, name, data)
}
func Init() {
t := &TemplateRenderer{
templates: template.Must(template.ParseGlob("public/views/*.html")),
}
e := echo.New()
e.Pre(controller.MethodOverride)
e.Renderer = t
e.GET("/", controller.SignUp)
e.GET("/user/:id", controller.GetUser)
e.POST("/user", controller.CreateUser)
e.PUT("/user/:id", controller.UpdateUser)
e.DELETE("/user/:id", controller.DeleteUser)
e.Logger.Fatal(e.Start(":8080"))
}
public/views/signup.html
データ登録に使用するテンプレートになります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{index . "title"}}</title>
</head>
<body>
<h1>{{index . "title"}}</h1>
<form method="POST" action="/user">
<dl>
<dt>Name:</dt>
<dd><input type="text" name="name"></dd>
<dt>Age:</dt>
<dd><input type="number" name="age"></dd>
</dl>
<p><input type="submit" name="submit" value="Sign Up"></p>
</form>
</body>
</html>
public/views/user.html
登録されたデータを表示するテンプレートになります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{index . "title"}}</title>
</head>
<body>
<h1>{{index . "title"}}</h1>
<h2>Your Account</h2>
<form method="POST" action="/user/{{index . "id"}}">
<input type="hidden" name="_method" value="PUT">
<dl>
<dt>ID:</dt>
<dd>{{index . "id"}}</dd>
<dt>Name:</dt>
<dd><input type="text" name="name" value="{{index . "name"}}"></dd>
<dt>Age:</dt>
<dd><input type="text" name="age" value="{{index . "age"}}"></dd>
</dl>
<p><input type="submit" name="submit" value="Submit"></p>
</form>
<h2>Delete</h2>
<form method="POST" action="/user/{{index . "id"}}">
<input type="hidden" name="_method" value="DELETE">
<p><input type="submit" name="submit" value="Delete"></p>
</form>
</body>
</html>
これでブラウザで http://localhost:8080/ にアクセスするとフォームが表示されます。
今回の記事は以上となります。
次回の記事では、JWT認証に挑戦します。