Golang 入門 : Web (簡易掲示板) を作成 part3-コントローラとルーティング

この記事は約11分で読めます。

今回の Golang は、

Web (簡易掲示板)を作成->コントローラとルーティングについて解説します!

前回は、

Golang 入門 : Web(簡易掲示板 ) を作成 part2-テーブル定義とモデル定義

に続いてやっていきます。

復習

前回の

Golang 入門 : Web(簡易掲示板 ) を作成 part2-テーブル定義とモデル定義
前回は Golang でした。 今回は,データベースのテーブル定義を行い、そのデータベースのデータを...

では、データベースに格納されているデータをアプリで利用できるようにモデル化し、取り出す処理まで記述しました。

今回は、取り出したデータを元に、ロジックを書いていきます。

ディレクトリ

.
├── controller
│   └── article_handler.go <- ここ
├── go.mod
├── go.sum
├── model
│   └── article.go
├── script
│   ├── reset_db.sh
│   └── reset_db.sql
├── util
│   └── db.go
├── view
│   ├── index.tmpl
│   ├── layout.tmpl
│   └── new.tmpl
└── web.go <-ーーーーーーーーー ここ

実践:ルーティング

まずルーティングとは、ユーザ(ブラウザ)からのリクエストによって、処理を振り分ける処理を作成します。

例えば、

Example Domain

にアクセスすると、user情報が得られ、

Example Domain

にアクセスすると、記事情報が得られます。

これをアプリ側で振り分ける設定をします。

[ golang ]Go言語入門~ WebAPI ~

ここでも少し説明しています。

仕様確認

ここで一度ルーティングの前に、仕様を再確認します。

仕様を確認して、ユーザ(ブラウザ)が行う処理だけ、サイトに処理を追加する必要があります。

ユーザ(ブラウザ)が行う処理は以下が挙げられます

  • 投稿記事一覧のページを表示する
  • 記事を投稿するページを表示する
  • 記事を投稿する
  • 記事を削除する

今回は、この4つをルーティングで設定します。

コード

package main

import (
	"web-go/controller"
	"web-go/util"

	"github.com/gin-gonic/gin"
)

func main() {

	server := controller.Server{
		DB: util.InitDB(),
	}
	router := gin.Default()

	router.LoadHTMLGlob("view/*")
	//template(view) ページ
	router.GET("/", server.GetArticlePage)
	router.GET("/new", server.NewArticlePage)
	//form 処理
	router.POST("/new", server.PostArticleHandler)
	router.POST("/delete/:id", server.DeleteArticleHandler)

	router.Run(":8080")
}

解説

router.GET("/", server.GetArticlePage)
	router.GET("/new", server.NewArticlePage)
	//form 処理
	router.POST("/new", server.PostArticleHandler)
	router.POST("/delete/:id", server.DeleteArticleHandler)

まず、ここがルーティングの設定です。

上から、記事一覧表示ページ、投稿ページ、投稿処理、削除処理です。

第一引数が、受け付けるリクエストのURL(URI)です。

第二引数で、URL(URI)に対するアプリの処理です。(以下、処理をハンドラーと呼びます)

特筆事項としては、ginというwebフレームワークでは,

ルーティングに”:{変数}”をつけておくと、変数に入る値をアプリの処理で利用できます。

 

今回は、削除処理で”/delete/:id”と書いてあるので、

/delete/1というリクエストが来ると、idの部分が1なので、

ハンドラーの中でidを呼び出すと、”1″という文字を参照することができます。

 

router := gin.Default()
        router.LoadHTMLGlob("view/*") 
	router.Run(":8080")

ここは、ginのアプリを呼び出して、サーバを起動す処理です。

LoadHTMLGlob()は、View層のファイル(見た目を作成するためのファイル)をアプリケーションで利用できるように設定しています。
“:8080″で

でアクセスできるようになります。

残りの、

server := controller.Server{
		DB: util.InitDB(),
	}

 

ここに関しては,次にcontroller層にあるハンドラーで利用するための設定です。

データベースにアクセスするDBオブジェクトをハンドラーに渡すことで、ハンドラーで、前回の記事で説明したデータベースの処理を利用できるようにします。

 

実践:コントローラ

コントローラ層では、リクエストに対しての処理を記述します。

コード

package controller

import (
	"net/http"
	"strconv"
	"web-go/model"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

type Server struct {
	DB *gorm.DB
}

func (s *Server) GetArticlePage(c *gin.Context) {
	errMsg := ""
	articles, err := model.GetAllArticles(s.DB)
	if err != nil {
		errMsg = "エラー発生"
		articles = []model.Article{}
	}
	c.HTML(http.StatusOK, "index.tmpl", gin.H{
		"articles": articles,
		"errMsg":   &errMsg,
	})
}

func (s *Server) NewArticlePage(c *gin.Context) {
	c.HTML(http.StatusOK, "new.tmpl", gin.H{})
}

func (s *Server) PostArticleHandler(c *gin.Context) {
	c.Request.ParseForm()
	article := new(model.Article)
	article.Text = c.Request.Form["text"][0]
  if article.Text ==""{
    c.HTML(http.StatusBadRequest, "new.tmpl", gin.H{
			"errMsg": "テキストが空です",
		})
		return
  }
	if err := model.PostArticle(s.DB, article); err != nil {
		c.HTML(http.StatusBadRequest, "new.tmpl", gin.H{
			"errMsg": "登録できませんでした",
		})
		return
	}
	c.Redirect(http.StatusMovedPermanently, "/")
}

func (s *Server) DeleteArticleHandler(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.Redirect(http.StatusMovedPermanently, "/")
		return
	}
	if err := model.DeleteArticleByID(s.DB, uint(id)); err != nil {
		articles, _ := model.GetAllArticles(s.DB)
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"articles": articles,
			"errMsg":   "エラー発生",
		})
		return
	}
	c.Redirect(http.StatusMovedPermanently, "/")
}

解説

type Server struct { DB *gorm.DB }

ここが、先ほど、ルーティングであった、ハンドラーの構造体です。
DBオブジェクトをハンドラーで使うので、DBパラメータを保持しています。

func (s *Server) GetArticlePage(c *gin.Context) {
	errMsg := ""
	articles, err := model.GetAllArticles(s.DB)
	if err != nil {
		errMsg = "エラー発生"
		articles = []model.Article{}
	}
	c.HTML(http.StatusOK, "index.tmpl", gin.H{
		"articles": articles,
		"errMsg":   &errMsg,
	})
}

func (s *Server) NewArticlePage(c *gin.Context) {
	c.HTML(http.StatusOK, "new.tmpl", gin.H{})
}

まず、ここが記事一覧表示と記事投稿ページのハンドラーです。
model.GetAllArticles(s.DB)で、記事一覧を取ってきています。
c.HTML()はginのメソッドで、viewで読み込んでいるテンプレートファイル(index.tmpl)に、記事一覧と、エラーメッセージを埋め込んでいます。
viewについては、次回説明します。

NewArticlePage()は、テンプレートファイル(new.tmpl)をそのまま、HTMLとして返却します。

次から、ユーザ処理に対するハンドラーです。

func (s *Server) PostArticleHandler(c *gin.Context) {
	article := new(model.Article)
	c.Request.ParseForm()
	article.Text = c.Request.Form["text"][0]
      if article.Text ==""{
            c.HTML(http.StatusBadRequest, "new.tmpl", gin.H{
		"errMsg": "テキストが空です",
	    })
        	return
        }
	if err := model.PostArticle(s.DB, article); err != nil {
		c.HTML(http.StatusBadRequest, "new.tmpl", gin.H{
			"errMsg": "登録できませんでした",
		})
		return
	}
	c.Redirect(http.StatusMovedPermanently, "/")
}

記事を投稿する処理です。
c.Request.ParseForm()で、formから送られたデータを取得します。
c.Request.Form[“text”][0]では、textという名前がついたformDataを取得しています。
model.PostArticle()でデータベースに保存しています。

c.Redirect()では、データベース保存が上手くいったので、リダイレクトで、記事一覧ページに飛ばしています。

func (s *Server) DeleteArticleHandler(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.Redirect(http.StatusMovedPermanently, "/")
		return
	}
	model.DeleteArticleByID(s.DB, uint(id))
	c.Redirect(http.StatusMovedPermanently, "/")
}

このハンドラーは記事削除の処理です。
ルーティングで設定した”:id”を取得するために、c.Param(“id”)を利用します。
Atoiを使って,int型にしているのは、c.Param(“id”)が数字以外の場合をエラーにするためです。エラーの場合は記事一覧にリダイレクトしています。

model.DeleteArticleByID()で記事を削除します。その後に、記事一覧ページにリダイレクトしています。

これで、コントローラの設定は終わりです。

まとめ

今回はルーティングとコントローラに関して説明しました。

Viewについては説明していないので、次回説明していきます。

コントローラは処理が多くなりがちになるので、複数の層に分けてみるのもいいですね!

 

ほとんどできてきているはずです。もう少しです。

ryomak/example-go
for h type programming blog. Contribute to ryomak/example-go development by creating an account on GitHub.

 

完成したコードを貼っているのでここから改造するもよし、自分なりに作り上げるのも良いと思います。

それでは

タイトルとURLをコピーしました