2. 用Beego编写一个友情链接

beego框架和bee工具的安装非常简单,唯一需要注意的是Mac平台go1.8版本无法运行bee工具,说是官方包造成的bug,需要使用更高版本,比如我的Mac本地版本是:

zhgxun-pro:~ zhgxun$ go version
go version go1.8.3 darwin/amd64

现在来看如何用beego框架编写一个友情链接。

1、创建表

CREATE TABLE `link` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `type` tinyint(1) NOT NULL DEFAULT 1 COMMENT '类型1官网2手册3其它',
  `title` varchar(120) NOT NULL COMMENT '标题',
  `url` varchar(255) NOT NULL COMMENT '地址',
  `content` varchar(255) NOT NULL COMMENT '描述',
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态1可用2不可用',
  `ctime` int(11) DEFAULT '0' COMMENT '创建时间',
  `utime` int(11) NOT NULL DEFAULT '0' COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `type`(`type`),
  KEY `title`(`title`),
  KEY `status`(`status`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='友情链接';

2、编写模型

// 友情链接
package models

import (
    "time"

    "github.com/astaxie/beego/orm"
)

type Link struct {
    Id      int64
    Type    int
    Title   string
    Url     string
    Content string
    Status  int
    Ctime   int64
    Utime   int64
}

// LinkStatus 友情链接状态
func LinkStatus() map[int]string {
    return map[int]string{
        1: "可用",
        2: "不可用",
    }
}

// LinkStatusDesc 友情链接状态描述
func LinkStatusDesc(id int) string {
    if desc, ok := LinkStatus()[id]; ok {
        return desc
    }
    return "未知"
}

// LinkTypeList 类型
func LinkTypeList() map[int]string {
    return map[int]string{
        1: "官网",
        2: "手册",
        3: "其它",
    }
}

// LinkTypeDesc 类型描述
func LinkTypeDesc(id int) string {
    if desc, ok := LinkTypeList()[id]; ok {
        return desc
    }
    return "未知"
}

// LinkSave 添加友情链接
func LinkSave(l *Link) (int64, error) {
    l.Status = 1
    l.Ctime = time.Now().Unix()
    l.Utime = time.Now().Unix()
    return orm.NewOrm().Insert(l)
}

// LinkUpdate 更新友情链接
func LinkUpdate(l *Link) (int64, error) {
    l.Utime = time.Now().Unix()
    if _, err := orm.NewOrm().Update(l); err != nil {
        return l.Id, err
    }
    return l.Id, nil
}

// LinkList 友情链接列表
func LinkList() []*Link {
    var link Link
    var links []*Link
    orm.NewOrm().QueryTable(link).RelatedSel().Filter("Status", 1).All(&links)
    return links
}

// LinkInfo 友情链接详情
func LinkInfo(id int64) (Link, error) {
    var l Link
    err := orm.NewOrm().QueryTable(l).RelatedSel().Filter("Id", id).One(&l)
    return l, err
}

3、注册模型

package main

import (
    _ "github.com/go-sql-driver/mysql"

    "github.com/astaxie/beego"
    "github.com/astaxie/beego/orm"

    "step/models"
    _ "step/routers"
    _ "step/templates"
)

func init() {
    orm.RegisterDataBase("default", "mysql", "root:@/step")
    orm.RegisterModel(
        new(models.Link),
    )
}

func main() {
    beego.Run()
}

4、注册路由

package routers

import (
    "github.com/astaxie/beego"

    "step/controllers"
)

func init() {
    // 友情链接
    beego.Router("/link", &controllers.LinkController{}, "GET:Index")
    beego.Router("/link/create", &controllers.LinkController{}, "GET,POST:Create")
    beego.Router("/link/:id([0-9]+)", &controllers.LinkController{}, "GET,POST:View")
    beego.Router("/link/update/:id([0-9]+)", &controllers.LinkController{}, "GET,POST:Update")
    beego.Router("/link/delete/:id([0-9]+)", &controllers.LinkController{}, "GET:Delete")
}

5、控制器

// 友情链接
package controllers

import (
    "fmt"
    "strconv"
    "strings"

    "github.com/astaxie/beego"

    "step/models"
)

type LinkController struct {
    beego.Controller
}

// 检查用户是否登陆
func (l *LinkController) Prepare() {
    _, user := models.IsLogin(l.Ctx)
    l.Data["nickname"] = user.Nickname
}

// Index 友情链接列表
func (l *LinkController) Index() {
    l.Data["links"] = models.LinkList()
    l.Data["types"] = models.LinkTypeList()
    l.Data["status"] = models.LinkStatus()
    l.Layout = "base.html"
    l.TplName = "link/index.html"
}

// Create 添加友情链接
func (l *LinkController) Create() {
    if l.Ctx.Request.Method == "POST" {
        typeId, err := strconv.Atoi(strings.TrimSpace(l.Input().Get("type")))
        if err != nil {
            l.Redirect("/link/create", 302)
        }
        title := l.Input().Get("title")
        url := l.Input().Get("url")
        content := l.Input().Get("content")
        if typeId <= 0 {
            l.Redirect("/link/create", 302)
        }
        if strings.TrimSpace(title) == "" {
            l.Redirect("/link/create", 302)
        }
        if strings.TrimSpace(url) == "" {
            l.Redirect("/link/create", 302)
        }
        if strings.TrimSpace(content) == "" {
            l.Redirect("/link/create", 302)
        }
        var id int64
        if id, err = models.LinkSave(&models.Link{
            Type:    typeId,
            Title:   title,
            Url:     url,
            Content: content,
        }); err != nil {
            l.Redirect("/link/create", 302)
        }
        l.Redirect("/link/"+strconv.FormatInt(id, 10), 302)
    }

    l.Data["isNewRecord"] = true
    l.Data["link"] = models.Link{}
    l.Data["types"] = models.LinkTypeList()
    l.Layout = "base.html"
    l.TplName = "link/create.html"
}

// View 友情链接详情
func (l *LinkController) View() {
    id, err := strconv.Atoi(l.Ctx.Input.Param(":id"))
    if err != nil || id <= 0 {
        l.Redirect("/link", 302)
    }
    link, err := models.LinkInfo(int64(id))
    if err != nil {
        l.Redirect("/link ", 302)
    }
    l.Data["link"] = link
    l.Data["type"] = models.LinkTypeDesc(link.Type)
    l.Data["status"] = models.LinkStatusDesc(link.Status)
    l.Layout = "base.html"
    l.TplName = "link/view.html"
}

// Update 编辑友情链接
func (l *LinkController) Update() {
    id, err := strconv.Atoi(l.Ctx.Input.Param(":id"))
    if err != nil || id <= 0 {
        l.Redirect("/link", 302)
    }
    link, err := models.LinkInfo(int64(id))
    if err != nil {
        l.Redirect("/link ", 302)
    }

    if l.Ctx.Request.Method == "POST" {
        typeId, err := strconv.Atoi(strings.TrimSpace(l.Input().Get("type")))
        if err != nil {
            l.Redirect("/link/update/"+strconv.FormatInt(int64(id), 10), 302)
        }
        link.Type = typeId
        link.Title = strings.TrimSpace(l.Input().Get("title"))
        link.Url = strings.TrimSpace(l.Input().Get("url"))
        link.Content = strings.TrimSpace(l.Input().Get("content"))
        var newId int64
        if newId, err = models.LinkUpdate(&link); err != nil {
            l.Redirect("/link/update/"+strconv.FormatInt(newId, 10), 302)
        }
        l.Redirect("/link/"+strconv.FormatInt(newId, 10), 302)
    }

    l.Data["isNewRecord"] = false
    l.Data["link"] = link
    l.Data["types"] = models.LinkTypeList()
    l.Layout = "base.html"
    l.TplName = "link/update.html"
}

// Delete 删除友情链接
func (l *LinkController) Delete() {
    id, err := strconv.Atoi(l.Ctx.Input.Param(":id"))
    if err != nil || id <= 0 {
        l.Redirect("/link", 302)
    }
    link, err := models.LinkInfo(int64(id))
    if err != nil {
        l.Redirect("/link", 302)
    }
    link.Status = 2
    if _, err := models.LinkUpdate(&link); err != nil {
        fmt.Println(err)
    }
    l.Redirect("/link", 302)
}

6、模板

6.1 列表 index.html

<ol class="breadcrumb">
    <li><a href="#">首页</a></li>
    <li><a href="#">内容管理</a></li>
    <li class="active">友情链接</li>
</ol>

<p>
    <a class="btn btn-primary" href="/link/create">添加</a>
</p>

<p class="text-info">共搜索到 <a class="text-success">{{.links | len}}</a> 条符合条件的记录。</p>
<table class="table table-bordered table-striped">
    <thead>
        <tr>
            <th width="10%">ID</th>
            <th width="10%">类型</th>
            <th width="20%">名称</th>
            <th width="35%">描述</th>
            <th width="10%">状态</th>
            <th width="15%">操作</th>
        </tr>
    </thead>
    <tbody>
        {{range .links}}
        <tr>
            <td>{{.Id}}</td>
            <td>{{index $.types .Type}}</td>
            <td>{{.Title}}</td>
            <td>{{.Content}}</td>
            <td>{{index $.status .Status}}</td>
            <td>
                <a href="/link/{{.Id}}">查看</a>&nbsp;&nbsp;|
                <a href="/link/update/{{.Id}}">编辑</a>&nbsp;&nbsp;|
                <a href="javascript:if(confirm('确定删除吗?')) location.href='/link/delete/{{.Id}}'">删除</a>
            </td>
        </tr>
        {{end}}
    </tbody>
</table>

6.2 添加 create.html

<ol class="breadcrumb">
    <li><a href="#">首页</a></li>
    <li><a href="#">内容管理</a></li>
    <li><a href="/link">友情链接</a></li>
    <li class="active">添加</li>
</ol>

{{template "link/from.html" .}}

6.3 表单 from.html

<form method="post" action="/link/{{if .isNewRecord}}create{{else}}update/{{.link.Id}}{{end}}">
    <div class="form-group">
        <label for="type">类型</label>
        <select class="form-control" id="type" name="type">
            {{range $index, $type := .types}}
            <option id="type_{{$index}}"  value="{{$index}}">{{$type}}</option>
            {{end}}
        </select>
    </div>
    <div class="form-group">
        <label for="title">标题</label>
        <input type="text" class="form-control" id="title" name="title" value="{{.link.Title}}" placeholder="请输入标题">
    </div>
    <div class="form-group">
        <label for="url">地址</label>
        <input type="text" class="form-control" id="url" name="url" value="{{.link.Url}}" placeholder="请输入地址">
    </div>
    <div class="form-group">
        <label for="content">描述</label>
        <textarea class="form-control" id="content" name="content" cols="15" rows="4">{{.link.Content}}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">{{if .isNewRecord}}添加{{else}}编辑{{end}}</button>
    </div>
</form>
<script type="text/javascript">
    $(function () {
        $("#type_{{.link.Type}}").prop("selected", "selected");
    });
</script>

6.4 编辑 update.html

<ol class="breadcrumb">
    <li><a href="#">首页</a></li>
    <li><a href="#">内容管理</a></li>
    <li><a href="/link">友情链接</a></li>
    <li class="active">编辑</li>
</ol>

{{template "link/from.html" .}}

6.5 详情 view.html

<ol class="breadcrumb">
    <li><a href="#">首页</a></li>
    <li><a href="#">内容管理</a></li>
    <li><a href="/link">友情链接</a></li>
    <li class="active">详情</li>
</ol>

<p>
    <a class="btn btn-primary" href="/link/create">添加</a>
    <a class="btn btn-info" href="/link/update/{{.link.Id}}">修改</a>
    <a class="btn btn-danger" href="/link/delete/{{.link.Id}}">删除</a>
</p>

<table class="table table-bordered table-responsive">
    <thead>
    <tr>
        <th width="10%">属性</th>
        <th width="90%">名称</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>Id</td>
        <td>{{.link.Id}}</td>
    </tr>
    <tr>
        <td>类型</td>
        <td>{{.type}}</td>
    </tr>
    <tr>
        <td>名字</td>
        <td>{{.link.Title}}</td>
    </tr>
    <tr>
        <td>说明</td>
        <td>{{.link.Content}}</td>
    </tr>
    <tr>
        <td>状态</td>
        <td>{{.status}}</td>
    </tr>
    </tbody>
</table>

7、效果展示

列表

列表

添加

添加

修改

修改

详情

详情

8、说明

《Go语言程序设计》一书7.7节中提到:

“Go语言目前没有一个权威的web框架,就像Ruby语言有Rails和python有Django。这并不是说这样的框架不存在,而是Go语言标准库中的构建模块就已经非常灵活以至于这些框架都是不必要的。此外,尽管在一个项目早期使用框架是非常方便的,但是它们带来额外的复杂度会使长期的维护更加困难。”

和使用PHP成熟的WEB开发框架比如YII2比较起来,确实显得无比笨拙。当然还可以做更多的封装,在这里就不做这些处理,仅仅是体验一下用go来编写一个简单的友情链接,你就会知道对于一般功能的实现,有一个完善的框架是多么的便捷和美好。没有尝试过用bee的自动生成工具生成的代码有多棒,但至少应该比上面的代码看起来要聪明一些才好。