用Golang创建简易论坛
请参考
尝试使用Golang的revel框架创建一个类似论坛的东西。
检查Go和MySQL的版本
$ go version
go version go1.7.1 darwin/amd64
$ mysql --version
mysql Ver 14.14 Distrib 5.7.15, for osx10.11 (x86_64) using EditLine wrapper
设定环境变量
请将以下内容添加到.bash_profile文件中。
#golang
export GOROOT=/usr/local/opt/go/libexec
export GOPATH=$HOME/.go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
安装 Revel。
$ go get github.com/revel/revel
$ go get github.com/revel/cmd/revel
确认已安装的revel
$ revel version
~
~ revel! http://revel.github.io
~
Version(s):
Revel v0.13.1 (2016-06-06)
go1.7.1 darwin/amd64
使用全新的应用程式名称(revel new app_name)创建项目。
借用了shiro16先生的golang-bbs代码
$ revel new github.com/shiro16/golang-bbs
~
~ revel! http://revel.github.io
~
Your application is ready:
/Users/xxxxxx/.go/src/github.com/shiro16/golang-bbs
You can run it with:
revel run github.com/shiro16/golang-bbs
使用revel run命令启动Web服务器。
$ revel run github.com/shiro16/golang-bbs
~
~ revel! http://revel.github.io
~
INFO 2016/10/07 14:28:23 revel.go:365: Loaded module testrunner
INFO 2016/10/07 14:28:23 revel.go:365: Loaded module static
INFO 2016/10/07 14:28:23 revel.go:230: Initialized Revel v0.13.1 (2016-06-06) for >= go1.4
INFO 2016/10/07 14:28:23 run.go:57: Running golang-bbs (github.com/shiro16/golang-bbs) in dev mode
INFO 2016/10/07 14:28:23 harness.go:170: Listening on :9000
INFO 2016/10/07 14:28:30 build.go:179: Cleaning dir tmp
INFO 2016/10/07 14:28:30 build.go:179: Cleaning dir routes
INFO 2016/10/07 14:28:30 build.go:179: Cleaning dir tmp
INFO 2016/10/07 14:28:30 build.go:179: Cleaning dir routes
INFO 2016/10/07 14:28:31 revel.go:365: Loaded module static
INFO 2016/10/07 14:28:31 revel.go:365: Loaded module testrunner
INFO 2016/10/07 14:28:31 revel.go:230: Initialized Revel v0.13.1 (2016-06-06) for >= go1.4
INFO 2016/10/07 14:28:31 main.go:30: Running revel server
Go to /@tests to run the tests.
使用Ctrl+C停止服务器。
创建API
编辑routes文件
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
创建控制器
创建一个通用控制器
// app/controllers/api/v1/v1.go
package controllers
import (
"github.com/revel/revel"
"github.com/shiro16/golang-bbs/app/utils"
"net/http"
)
// 埋め込みによって revel.Controller をラップした ApiV1Controller を定義する
type ApiV1Controller struct {
*revel.Controller
}
// エラーの際に返す Json 用の構造体
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
// 正常な際に返す Json 用の構造体(今回は1種類で統一する)
type Response struct {
Results interface{} `json:"results"`
}
// 引数として渡されて interface にリクエストの Json の値を格納する
func (c *ApiV1Controller) BindParams(s interface{}) error {
return utils.JsonDecode(c.Request.Body, s)
}
// Bad Request Error を返すやつ
func (c *ApiV1Controller) HandleBadRequestError(s string) revel.Result {
c.Response.Status = http.StatusBadRequest
r := ErrorResponse{c.Response.Status, s}
return c.RenderJson(r)
}
// Not Found Error を返すやつ
func (c *ApiV1Controller) HandleNotFoundError(s string) revel.Result {
c.Response.Status = http.StatusNotFound
r := ErrorResponse{c.Response.Status, s}
return c.RenderJson(r)
}
// Internal Server Error を返すやつ
func (c *ApiV1Controller) HandleInternalServerError(s string) revel.Result {
c.Response.Status = http.StatusInternalServerError
r := ErrorResponse{c.Response.Status, s}
return c.RenderJson(r)
}
创建评论控制器
// app/controllers/api/v1/comments.go
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)
}
API的操作验证
启动服务器:
$ 使用 revel run 命令启动github.com/shiro16/golang-bbs。
请不要打开终端并尝试使用curl命令。
请用中文将以下内容重述,只需要一种选项:$ 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
请以本地9000端口发送一个DELETE请求到http://localhost:9000/api/v1/comments/1。
{
"results": "delete"
}
在启动服务器的终端上显示了控制台日志。
2016/10/07 15:31:00.070 ::1 200 4.097022ms GET /api/v1/comments
2016/10/07 15:31:26.809 ::1 200 146.966µs GET /api/v1/comments/1
2016/10/07 15:31:39.625 ::1 200 108.271µs POST /api/v1/comments
2016/10/07 15:31:50.521 ::1 200 105.353µs DELETE /api/v1/comments/1
数据库管理
-
- 使用するORM: github.com/jinzhu/gorm
使用するvalidation: github.com/go-validator/validator
创建数据库
- mysqlにログインして、create databaseを実行
$ 使用者 root 登入 mysql
mysql> 创建数据库 golang_bbs_development;
mysql> 退出
添加DB的连接信息
在终端输入:$ vi conf/app.conf
在[dev]部分添加一行
+ db.info = "root@/golang_bbs_development?charset=utf8&parseTime=True
我們需要建立一個模型。
安装要使用的ORM(gorm)和验证器(validator)。
$ go get github.com/jinzhu/gorm
$ go get gopkg.in/validator.v2
创建评论模型
// app/models/comment.go
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"`
}
创建gorm控制器
// app/controllers/gorm.go
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
}
在代码中添加调用上述控制器的处理逻辑。
请用中文重新表达如下内容: $ vi app/init.go
请编辑 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行目くらいに
}
在控制器中使用模型
编辑评论控制器
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)
}
创建app/controllers/comments.go。
package controllers
import (
"github.com/revel/revel"
"github.com/shiro16/golang-bbs/app/models"
"net/http"
)
type Comments struct {
*revel.Controller
}
func (c Comments) Index() revel.Result {
comments := []models.Comment{}
if err := DB.Order("id desc").Find(&comments).Error; err != nil {
c.Response.Status = http.StatusNotFound
// TODO: error rendering
}
c.RenderArgs["comments"] = comments
return c.Render()
}
检查API的运作
启动服务器
$ revel run github.com/shiro16/golang-bbs
在命令行中打开终端并尝试使用curl命令→出现错误!
用中文对以下内容进行改述:
$ curl -H “Content-type: application/json” -X POST -d ‘{“nickname”:”shiro16″, “body”:”test comment”}’ http://localhost:9000/api/v1/comments
curl命令:-H表示设置请求头部,”Content-type: application/json”表示请求头部的内容类型为JSON;-X POST表示使用POST方法发送请求;-d后面的内容是请求体,即要发送的JSON数据;http://localhost:9000/api/v1/comments是要发送请求的URL。
Go Compilation Error
The Go code src/github.com/shiro16/golang-bbs/app/controllers/gorm.go does not compile: cannot use &db (type **gorm.DB) as type *gorm.DB in assignment
In src/github.com/shiro16/golang-bbs/app/controllers/gorm.go (around line 22)
17:
log.Panicf("Failed to connect to database: %v\n", err)
18:
}
19:
20:
db.DB()
21:
db.AutoMigrate(&models.Comment{})
22:
DB = &db
23:
}
24:
25:
func dbInfoString() string {
26:
s, b := revel.Config.String("db.info")
对API进行功能验证
启动服务器
请运行 `revel run github.com/shiro16/golang-bbs`。
打开一个终端并尝试使用curl命令。
$ 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": "2016-10-11T17:23:40.257591728+09:00",
"updated_at": "2016-10-11T17:23:40.257591728+09:00",
"deleted_at": null
}
}
确认验证功能是否正常运作。
$ 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": "2016-10-11T08:23:40Z",
"updated_at": "2016-10-11T08:23:40Z",
"deleted_at": null
}
]
}
请参考评论的具体内容。
$ curl http://localhost:9000/api/v1/comments/1
{
"results": {
"id": 1,
"nickname": "shiro16",
"body": "test comment",
"created_at": "2016-10-11T08:23:40Z",
"updated_at": "2016-10-11T08:23:40Z",
"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"
}
总结
我已经成功将消息存储到了MySQL,并将其输出为JSON。下一步,我希望能够对输出结果进行装饰,以及在登录和发布方面增强功能。还有人说”验证最好是在模型中执行”,我希望能够在研究这方面信息的同时进行报告。
revelのsampleはこちらにあるので是非参考になさってください。