はじめに
今回は golang の revel framework を使用して掲示板っぽいやつを作ってみる。
掲示板っぽいと言っても基本的には API で Json を返すことにする。
しかし html を返す場合もやることはほぼ変わらない。
今回作成する API は一般的な掲示板でいうスレッドは存在せず
レスのみを扱う。(今後スレッド対応やログイン対応等を行っていくかもしれない)
endpoint は下記のようにする
http method |
path |
説明 |
GET |
/comments |
一覧 |
GET |
/comments/:id |
詳細 |
POST |
/comments |
登録 |
DELETE |
/comments/:id |
削除 |
revel is 何 ?
revel は高機能な重量級の framework で Ruby で言うと sinatra というより
Rails に近い framework になります。
環境
$ go version
go version go1.4.2
$ mysql --version
mysql Ver 14.14 Distrib 5.6.13
revel を install する
下記で install できる
$ go get github.com/revel/revel # revel framework を取得
$ go get github.com/revel/cmd/revel # revel command を取得
プロジェクトを作成してみる
とりあえずハロワが表示されるとこまで進める。
$ revel new github.com/shiro16/golang-bbs # rails new みたいなやつ
~
~ revel! http://revel.github.io
~
Your application is ready:
${GOPATH}/src/github.com/shiro16/golang-bbs
You can run it with:
revel run github.com/shiro16/golang-bbs
$ cd ${GOPATH}/src/github.com/shiro16/golang-bbs
$ revel run # applicaton の起動
これで「http://localhost:9000/」にアクセスすると
「It works!」が表示される。
API の endpoint を定義する
routing を追加する
routing は「config/routes」で管理されている
下記のように編集する。
今回の Api の path は「/api/v1」をベースにする
GET / App.Index
+ GET /api/v1/comments ApiV1Comments.Index
+ GET /api/v1/comments/:id ApiV1Comments.Show
+ POST /api/v1/comments ApiV1Comments.Create
+ DELETE /api/v1/comments/:id ApiV1Comments.Delete
controller を作成する
今回は api で共通して使うであろう Error 処理等をまとめて記述する
ベースとなる controller も作成しておく
package controllers
import (
"github.com/revel/revel"
"github.com/shiro16/golang-bbs/app/utils"
"net/http"
)
type ApiV1Controller struct {
*revel.Controller
}
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
type Response struct {
Results interface{} `json:"results"`
}
func (c *ApiV1Controller) BindParams(s interface{}) error {
return utils.JsonDecode(c.Request.Body, s)
}
func (c *ApiV1Controller) HandleBadRequestError(s string) revel.Result {
c.Response.Status = http.StatusBadRequest
r := ErrorResponse{c.Response.Status, s}
return c.RenderJson(r)
}
func (c *ApiV1Controller) HandleNotFoundError(s string) revel.Result {
c.Response.Status = http.StatusNotFound
r := ErrorResponse{c.Response.Status, s}
return c.RenderJson(r)
}
func (c *ApiV1Controller) HandleInternalServerError(s string) revel.Result {
c.Response.Status = http.StatusInternalServerError
r := ErrorResponse{c.Response.Status, s}
return c.RenderJson(r)
}
これでベースとなる処理が完了したので comments controller を作成していく
package controllers
import (
"github.com/revel/revel"
"github.com/shiro16/golang-bbs/app/controllers"
)
type ApiV1Comments struct {
ApiV1Controller
}
func (c ApiV1Comments) Index() revel.Result {
r := Response{"index"}
return c.RenderJson(r)
}
func (c ApiV1Comments) Show(id int) revel.Result {
r := Response{"show"}
return c.RenderJson(r)
}
func (c ApiV1Comments) Create() revel.Result {
r := Response{"create"}
return c.RenderJson(r)
}
func (c ApiV1Comments) Delete(id int) revel.Result {
r := Response{"delete"}
return c.RenderJson(r)
}
これで controller の作成は完了。
DB へ接続してデータを取得する等は次で行います。
実際に起動してみると下記のようになるかと。
$ revel run github.com/shiro16/golang-bbs
# 別窓等で
$ curl http://localhost:9000/api/v1/comments
{
"results": "index"
}
$ curl http://localhost:9000/api/v1/comments/1
{
"results": "show"
}
$ curl -X POST http://localhost:9000/api/v1/comments
{
"results": "create"
}
$ curl -X DELETE http://localhost:9000/api/v1/comments/1
{
"results": "delete"
}
DB 周りの処理を作成する
現状 ORM が revel には無い為自分で好きなものを使う。
revel の samples では gorp が使われているが
今回は gorm を使用する。
validation には validator を使用する。
DB の接続情報を追加
「golang_bbs_development」という名前の DB 名にしています。
[dev]
mode.dev = true
results.pretty = true
watch = true
watcher.mode = "normal"
log.trace.output = off
log.info.output = stderr
log.warn.output = stderr
log.error.output = stderr
db.info = "root@/golang_bbs_development?charset=utf8&parseTime=True
model を作成する
gorm を使用するので取得する
validator も取得しておく
$ go get github.com/jinzhu/gorm
$ go get gopkg.in/validator.v2
comment model を作成
package models
import (
"time"
)
type Comment struct {
ID uint64 `gorm:"primary_key" json:"id"`
Nickname string `sql:"size:64" json:"nickname" validate:"max=64"`
Body string `sql:"size:255" json:"body" validate:"min=1,max=255"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at"`
}
DB への接続などの初期化処理を作成
package controllers
import (
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"github.com/revel/revel"
"github.com/shiro16/golang-bbs/app/models"
"log"
)
var DB *gorm.DB
func InitDB() {
db, err := gorm.Open("mysql", dbInfoString())
if err != nil {
log.Panicf("Failed to connect to database: %v\n", err)
}
db.DB()
db.AutoMigrate(&models.Comment{}) # ここで table の作成を行っている
DB = &db
}
func dbInfoString() string {
s, b := revel.Config.String("db.info")
if !b {
log.Panicf("database info not found")
}
return s
}
上記を呼び出す処理を追記
// app/init.go
- import "github.com/revel/revel"
+ import(
+ "github.com/revel/revel"
+ "github.com/shiro16/golang-bbs/app/controllers"
+ )
func init() {
....
+ revel.OnAppStart(controllers.InitDB) // 28行目くらいに
}
DB 周りの処理の作成が完了し、
残すは作成した model を controller で実際に使用する処理を残すのみ
controller で model を使用する
comments controller を編集していく
package controllers
import (
"github.com/revel/revel"
+ "github.com/shiro16/golang-bbs/app/controllers"
+ "github.com/shiro16/golang-bbs/app/models"
+ "gopkg.in/validator.v2"
)
type ApiV1Comments struct {
ApiV1Controller
}
func (c ApiV1Comments) Index() revel.Result {
+ comments := []models.Comment{}
+ if err := controllers.DB.Order("id desc").Find(&comments).Error; err != nil {
+ return c.HandleInternalServerError("Record Find Failure")
+ }
+ r := Response{comments}
- r := Response{"index"}
return c.RenderJson(r)
}
func (c ApiV1Comments) Show(id int) revel.Result {
+ comment := &models.Comment{}
+ if err := controllers.DB.First(&comment, id).Error; err != nil {
+ return c.HandleNotFoundError(err.Error())
+ }
+ r := Response{comment}
- r := Response{"show"}
return c.RenderJson(r)
}
func (c ApiV1Comments) Create() revel.Result {
+ comment := &models.Comment{}
+ if err := c.BindParams(comment); err != nil {
+ return c.HandleBadRequestError(err.Error())
+ }
+ if err := validator.Validate(comment); err != nil {
+ return c.HandleBadRequestError(err.Error())
+ }
+ if err := controllers.DB.Create(comment).Error; err != nil {
+ return c.HandleInternalServerError("Record Create Failure")
+ }
+ r := Response{comment}
- r := Response{"create"}
return c.RenderJson(r)
}
func (c ApiV1Comments) Delete(id int) revel.Result {
+ comment := models.Comment{}
+ if err := controllers.DB.First(&comment, id).Error; err != nil {
+ return c.HandleNotFoundError(err.Error())
+ }
+ if err := controllers.DB.Delete(&comment).Error; err != nil {
+ return c.HandleInternalServerError("Record Delete Failure")
+ }
+ r := Response{"success"}
- r := Response{"delete"}
return c.RenderJson(r)
}
これで各 endpoint にアクセスしてみる
$ revel run github.com/shiro16/golang-bbs
# 別窓等で
$ curl -H "Content-type: application/json" -X POST -d '{"nickname":"shiro16", "body":"test comment"}' http://localhost:9000/api/v1/comments
{
"results": {
"id": 1,
"nickname": "shiro16",
"body": "test comment",
"created_at": "2015-08-13T21:20:46.681910871+09:00",
"updated_at": "2015-08-13T21:20:46.681910871+09:00",
"deleted_at": null
}
}
# validation がちゃんと機能しているかチェック
$ curl -H "Content-type: application/json" -X POST -d '{"nickname":"shiro16", "body":""}' http://localhost:9000/api/v1/comments
{
"code": 400,
"message": "Body: less than min"
}
$ curl http://localhost:9000/api/v1/comments
{
"results": [
{
"id": 1,
"nickname": "shiro16",
"body": "test comment",
"created_at": "2015-08-13T12:20:47Z",
"updated_at": "2015-08-13T12:20:47Z",
"deleted_at": null
}
]
}
$ curl http://localhost:9000/api/v1/comments/1
{
"results": {
"id": 1,
"nickname": "shiro16",
"body": "test comment",
"created_at": "2015-08-13T12:20:47Z",
"updated_at": "2015-08-13T12:20:47Z",
"deleted_at": null
}
}
$ curl http://localhost:9000/api/v1/comments/2
{
"code": 404,
"message": "record not found"
}
$ curl -X DELETE http://localhost:9000/api/v1/comments/1
{
"results": "success"
}
まとめ
こんな感じで雑な部分もありますが revel を使って基本的な処理の作成が完了しました。
validation に関しては model にてチェックを行った方がいいかと思いますが、
今回は時間の関係で controller にて行っています。
今回作成したものはこちらで公開しています。
API 以外の処理も追加していますので参考にしてください。
今回の説明以外の詳しい内容は下記を参考にするといいと思います。
とくに sample を配布しているので「go get github.com/revel/samples」で取得して
見てみるといいかと思います。
Welcome to Revel, the Web Framework for Go!