手撸博客2 留言板

0 为什么是留言板?

需要给easy-blog开一个头,想来想去,留言板好像比较合适。涉及到了需要用到的大部分内容:

  • html模板
  • 数据库
  • gin

1 设计思路

1.1 设计数据库

数据库字段如下:

  • 留言ID
  • 留言人昵称
  • 留言人邮箱
  • 留言内容
  • 留言时间

1.2 设计页面

  • 页面顶部为留言人昵称和邮箱的输入框
  • 下面是留言框
  • 再下面是历史留言的展示

2 实现细节

2.1 创建数据库表

create TABLE message_board(
    messageId int primary key auto_increment,
    authorName varchar(20),
    authorEmail varchar(30),
    message text,
    messageTime datetime)

2.2 创建html模板

我们这里先使用一个简易的页面,我承认确实很丑。

2.2.1 留言提交区

使用一个表单来实现留言提交区,使用POST方式提交到/msgboard路径。稍后会在路由中定义页面展示和后台处理的路由。

    
留言

效果如下:


留言提交

2.2.2 历史留言展示区

使用表格来实现留言展示。表格使用html模板来填充,使用传入的结构中的各个字段对td标签中的内容进行替换。

    
{{if len .}} {{range .}} {{end}}
留言ID 留言人 邮箱 留言时间 留言内容
{{.MsgID}} {{.AuthorName}} {{.AuthorEmail}} {{.MsgTime}} {{.Msg}}
{{end}}

效果如下:


留言展示

2.3 创建路由

创建以下两个路由,分别用来响应/msgboard路径的GET和POST请求。其中GET路由用来响应界面,POST请求用来处理新增留言。

engine := gin.Default()
engine.GET("/msgboard", msgboard.GetHandler)
engine.POST("/msgboard", msgboard.PostMsgHandler)

msgboard是一个本地的包,其中有GetHandler和PostHandler两个对外的函数。
另外,因为我们的需要使用到html模板,并且模板中会引用css或js的静态文件,需要添加如下逻辑:

engine.LoadHTMLGlob("./template/*") //加载html模板,加载后才能使用
engine.Static("/static", "./static") //配置静态文件服务,配置后才可以引用本地的静态文件

2.4 创建路由处理函数

在msgboard包中创建路由函数。

2.4.1 GetHandler

func GetHandler(c *gin.Context) {
    //Get msgs from sql
    msgs, err := dao.SelectAllMessage(SqlxDB)
    if err != nil {
        fmt.Println("get msgs from db err", err)
        c.String(http.StatusInternalServerError, err)
    }

    c.HTML(http.StatusOK, "msgboard.html", msgs)
}

其中dao是我们封装的数据库处理包,这里先从数据库中读取所有的留言信息,然后将其传入msgboard.html模板,并返回给用户。
msgs的结构如下,字段和模板中的数据对应:

type Message struct {
    MsgID       int    `db:"messageId"`
    AuthorName  string `db:"authorName"`
    AuthorEmail string `db:"authorEmail"`
    Msg         string `db:"message"`
    MsgTime     string `db:"messageTime"`
}

2.4.2 PostHandler

unc PostHandler(c *gin.Context) {
    //Write new msg to sql
    var msg model.Message
    msg.AuthorName = c.DefaultPostForm("name", "nobody")
    msg.AuthorEmail = c.DefaultPostForm("mail", "无邮箱信息")
    msg.Msg = c.DefaultPostForm("msg", "")
    dao.InsertOneMessage(SqlxDB, &msg)
    GetHandler(c)
}

PostHandler先获取POST请求中携带的信息,DefaultPostForm在请求中未包含该字段未,使用第二个参数的默认值代替。

msg.AuthorName = c.DefaultPostForm("name", "nobody")
msg.AuthorEmail = c.DefaultPostForm("mail", "无邮箱信息")
msg.Msg = c.DefaultPostForm("msg", "")

使用dao包中封装的InsertOneMessage函数将一条留言插入到数据库中,并调用GetHandler将所有留言的页面返回给用户。这里后面可以优化为使用ajax的方式更新页面,而不是整个返回。

2.5 数据库处理

2.5.1 数据库连接

dbutil包的init函数中初始化数据库连接,保证后台运行起来时,数据库已就绪。
数据库处理使用了sqlx这个数据库包,其扩展了官方的sql包。注意在这里要匿名导入mysql的驱动包github.com/go-sql-driver/mysql,使用其中的init函数初始化mysql驱动,否则无法连接到mysql数据库。其他数据库也是同理。
所有服务使用SQLXDB作为服务器连接的handle,当数据库连接失败时,触发panic,服务无法启动。

package dbutil

import (
    "fmt"

    "github.com/finalone/easy-blog/app/config"
    _ "github.com/go-sql-driver/mysql"
    "github.com/jmoiron/sqlx"
)

var (
    // SQLXDB is sqlx instance
    SQLXDB *sqlx.DB
)

func init() {
    if SQLXDB != nil {
        return
    }
    SQLXDB = NewSQLXDB()
}

// NewSQLXDB get a *sqlx.DB
func NewSQLXDB() *sqlx.DB {
    cfg := &config.Cfg
    if cfg == nil {
        return nil
    }

    dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/easyblog?charset=utf8&parseTime=True", cfg.User, cfg.Passwd, cfg.Addr, cfg.Port)

    db, err := sqlx.Connect("mysql", dsn)
    if err != nil {
        fmt.Println("sql connect error", err)
        panic("sql connect error")
    }

    db.SetMaxOpenConns(20)
    db.SetMaxIdleConns(10)

    return db
}

2.5.2 数据库使用

前面提到,我们封装了dao包来实现对数据库的操作。目前dao包中只有两个函数,获取所有留言和插入一条留言,这个逻辑比较简单,就不再详述了。

// SelectAllMessage get all message from table message_board
func SelectAllMessage(sqlxDB *sqlx.DB) (*[]model.Message, error) {
    var msgs []model.Message
    sql := `SELECT * FROM message_board`
    err := sqlxDB.Select(&msgs, sql)
    if err != nil {
        return nil, err
    }

    return &msgs, nil
}

// InsertOneMessage insert one message to table message_board
func InsertOneMessage(sqlxDB *sqlx.DB, msg *model.Message) error {
    sql := `INSERT INTO message_board (authorName, authorEmail, message, messageTime) values (?, ?, ?, NOW())`
    _, err := sqlxDB.Exec(sql, msg.AuthorName, msg.AuthorEmail, msg.Msg)
    if err != nil {
        fmt.Println("insert msg err", err)
        return err
    }

    return nil
}

3 小结

本节介绍了留言板页面的几个关键点:

  • 数据库
  • 路由
  • GET和POST请求的不同处理
  • HTML模板

因为我目前还在思考如何布置整个系统的框架,这部分说的比较粗糙,待框架确定后,将各个模块放的正确的位置,再进行分享。

你可能感兴趣的:(手撸博客2 留言板)