go语言作为c语言同父异母的兄弟在后端服务器开发方面表现出了其惊人的天赋,出生记为高并发、多核心而生。
beego是华人谢大神借鉴tornado、sinatra 和 flask 这三个框架的精髓而设计的一个框架,该框架可以帮助人们来快速开发API、web、以及后端服务应用。
站在巨人的肩膀上,也同时作为学习go的具体实战项目,从beego开始创建一个基于前后端分离的web项目。
[帮助]

  • beego官方文档
    beego案例
安装beego beego直接通过go get安装,bee 工具是一个为了协助快速开发 beego 项目而创建的项目,通过 bee 您可以很容易的进行 beego项目的创建、热编译、开发、测试、和部署。
本项目将基于bee创建一个api工程,开发IDE采用goland(支持正版,你懂得)
$ go get -u github.com/astaxie/beego
$ go get -u github.com/beego/bee

创建API工程 创建一个xxxx.com的工程:
$bee api xxxx.com

下面运行该工程,
-gendoc=true
表示每次自动化的 build 文档,
-downdoc=true
就会自动的下载 swagger 文档查看器:
$bee run -gendoc=true -downdoc=true

在浏览器中输入
http://localhost:8080/swagger/
可以看到工程预置的user和object接口:

基于beego开发RESTfulAPI项目


文章图片

设计接口API RESTful API的特点
  • 基于“资源”,数据也好、服务也好,在RESTFul设计里一切都是资源。
  • 无状态。一次调用一般就会返回结果,不存在类似于“打开连接-访问数据-关闭连接”这种依赖于上一次调用的情况。
  • URL中通常不出现动词,只有名词
  • URL语义清晰、明确
  • 使用HTTP的GET、POST、DELETE、PUT来表示对于资源的增删改查
  • 使用JSON不使用XML
RESTful风格的接口设计尽量遵循以下原则
参考RESTful API接口设计:
  • 使用HTTP动词表示增删改查资源, GET:查询,POST:新增,PUT:更新,DELETE:删除
  • 返回结果必须使用JSON
  • HTTP状态码,在REST中都有特定的意义:200,201,202,204,400,401,403,500。比如401表示用户身份认证失败,403表示你验证身份通过了,但这个资源你不能操作
  • 如果出现错误,返回一个错误码
  • API必须有版本的概念,v1,v2,v3
  • 使用Token令牌来做用户身份的校验与权限分级,而不是Cookie
  • url中大小写不敏感,不要出现大写字母
  • 使用 - 而不是使用 _ 做URL路径中字符串连接
  • 有一份漂亮的文档
撰写API文档
API文档撰写有很多开源的工具,我使用的是
showdoc
(支持api接口编写和数据字典编写),支持在线编写和自己独立部署。
设计接口示例:
/api/v1.0/banner
 {
"errno": 0,
"errmsg" "成功",
"data": {
"total": 1,
"list":
[
{
"image_url": "http://",
"title": "",
"content": "!"
"link": "http://"
}
]
}
 }
设计数据模型 如果说接口文档是是对项目中所有功能的的梳理,是前后端开发的纽带,那么数据模型则是整个项目开发过程中的基石,数据模型设计的好坏直接关系到项目的优劣,包括可维护性、架构合理性、多模块的协作性。
1. 创建数据字典
数据库设计规范
需注意的数据库设计原则可参考
  • 规则 1:弄清楚将要开发的应用程序是什么性质的(OLTP 还是 OPAP)?
  • 规则 2:将你的数据按照逻辑意义分成不同的块,让事情做起来更简单
  • 规则 3:不要过度使用 “规则 2”
  • 规则 4:把重复、不统一的数据当成你最大的敌人来对待
  • 规则 5:当心被分隔符分割的数据,它们违反了“字段不可再分”
  • 规则 6:当心那些仅仅部分依赖主键的列
  • 规则 7:仔细地选择派生列
  • 规则 8:如果性能是关键,不要固执地去避免冗余
  • 规则 9:多维数据是各种不同数据的聚合
  • 规则 10:将那些具有“名值表”特点的表统一起来设计
  • 规则 11:无限分级结构的数据,引用自己的主键作为外键
设计数据字典示例
  • banner表,存储banner信息
字段 类型 默认 注释
id bigint 主键
name varchar(50) 名称
image varchar(255) 内容
title varchar(255) NULL 标题
content varchar(255) NULL 内容
  • 备注:无
2. 设计beego数据结构模型
beego的MVC模型从工程结构上分为controllers、models、views;routers目录提供了接口的路由信息。
controllers:接口服务的具体实现,通常为业务的具体实现;
models:数据模型,往往和数据库的设计直接相关;
beego的数据结构设计时,通过传入初始值从而实现ORM框架的表结构创建及检查,具体可以参考下一小节
vim models/model.go

添加以下内容
/* table_name = banner */
type Banner struct {
	Id int64`json:"user_id"`//banner id
	Name string`orm:"size(50)"json:"name"`//banner name
	Image string`orm:"size(255)"json:"image"`//banner image url
	Title string`orm:"size(255)"json:"title"`//banner titile
	Content string `orm:"size(255)"json:"content"`//banner content
}

3. beegode的ORM模型定义详解
func (u *User) TableName() string {return "auth_user"}
AuthUser -> auth_user
Auth_User -> auth__user
DB_AuthUser -> d_b__auth_user
orm:"null;
rel(fk)";
,AnyField stringautoIdpknullnullindexuniqueuniquecolumnName string `orm:"column(user_name)"`sizeTitle string `orm:"size(60)"`digits / decimalsMoney float64 `orm:"digits(12);
decimals(4)"`
auto_now / auto_now_addauto_nowauto_now_addCreated time.Time >`orm:"auto_now_add;
type(datetime)"`
Updated time.Time >`orm:"auto_now;
type(datetime)"`
type设置为 date 时,time.Time 字段的对应 db >类型使用 date
Created time.Time >`orm:"auto_now_add;
type(date)"`
设置为 datetime 时,time.Time 字段的对应 db >类型使用 datetime
Created time.Time >`orm:"auto_now_add;
type(datetime)"`
defaultStatus int `orm:"default(1)"`type User struct {
...
Profile *Profile `orm:"null;
rel(one);
on_delete(set_null)"`
...
}
type Profile struct {
...
User *User `orm:"reverse(one)"`
...
}
type Post struct {
...
User *User `orm:"rel(fk)"` // RelForeignKey relation
...
}
type User struct {
...
Posts []*Post `orm:"reverse(many)"` // fk 的反向关系
...
}
type Post struct {
...
Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation
...
}
type Tag struct {
...
Posts []*Post `orm:"reverse(many)"`
...
}
orm:"rel(m2m)"rel_tablerel_throughpkg.path.ModelNameapp.models.PostTagRelPostTagrel_tablerel_throughorm:"rel(m2m);
rel_table(the_table_name)"orm:"rel(m2m);
rel_through(pkg.path.ModelName)"cascade级联删除(默认值)
set_null设置为 NULL,需要设置 null = true
set_default设置为默认值,需要设置 default 值
do_nothing什么也不做,忽略
type User struct {
...
Profile *Profile `orm:"null;
rel(one);
on_delete(set_null)"`
...
}
type Profile struct {
...
User *User `orm:"reverse(one)"`
...
}// 删除 Profile 时将设置 User.Profile 的数据库字段为 NULL
type User struct {
Id int
Name string
}type Post struct {
Id int
Title string
User *User `orm:"rel(fk)"`
}
type Post struct {
Id int
Title string
User *User `orm:"rel(fk);
null;
on_delete(set_null)"`
}
type Post struct {
Id int
Title string
User *User `orm:"rel(fk);
null;
on_delete(do_nothing)"`
}
go mysql
int, int32 - 设置 auto 或者名称为 Id 时 integer AUTO_INCREMENT
int64 - 设置 auto 或者名称为 Id 时 bigint AUTO_INCREMENT
uint, uint32 - 设置 auto 或者名称为 Id 时 integer unsigned AUTO_INCREMENT
uint64 - 设置 auto 或者名称为 Id 时 bigint unsigned AUTO_INCREMENT
bool bool
string - 默认为 size 255 varchar(size)
string - 设置 type(char) 时 char(size)
string - 设置 type(text) 时 longtext
time.Time - 设置 type 为 date 时 date
time.Time datetime
byte tinyint unsigned
rune integer
int integer
int8 tinyint
int16 smallint
int32 integer
int64 bigint
uint integer unsigned
uint8 tinyint unsigned
uint16 smallint unsigned
uint32 integer unsigned
uint64 bigint unsigned
float32 double precision
float64 double precision
float64 - 设置 digits, decimals 时 numeric(digits, decimals)
Mysql数据库连接 beego提供一个强大的ORM框架,支持关联查询和SQL查询。
这里借用官方文档说明下该ORM的强大。
已支持数据库驱动:
MySQL:github.com/go-sql-driver/mysql
PostgreSQL:github.com/lib/pq
Sqlite3:github.com/mattn/go-sqlite3

ORM 特性
  • 支持 Go 的所有类型存储
  • 轻松上手,采用简单的 CRUD 风格
  • 自动 Join 关联表
  • 跨数据库兼容查询
  • 允许直接使用 SQL 查询/映射
  • 严格完整的测试保证 ORM 的稳定与健壮
安装 ORM
go get github.com/astaxie/beego/orm

安装mysql驱动
go get github.com/go-sql-driver/mysql

数据库连接
1. 配置数据库连接参数
vim conf/app.conf

添加以下内容
#配置数据库连接参数
mysqladdr = "127.0.0.1"
mysqlport = 3306
mysqldbname = "database_name"
mysqlusername = "root"
mysqlpassword = "123456"
# 设置最大空闲连接
mysqlmaxIdle = 30
# 设置最大数据库连接 (go >= 1.2)
mysqlmaxConn = 30

2. 解析配置文件参数
vim utils/config.go

添加以下内容
var (
	G_mysql_addrstring //mysql ip 地址
	G_mysql_portstring //mysql 端口
	G_mysql_dbname string //mysql db name
	G_mysql_unamestring //mysql username
	G_mysql_passwd string //mysql password
	G_mysql_maxidle int//mysql maxidle
	G_mysql_maxconn int// mysql maxconn
)func InitConfig() {
	//从配置文件读取配置信息
	appconf, err := config.NewConfig("ini", "./conf/app.conf")
	if err != nil {
beego.Debug(err)
return
	}
	G_mysql_addr = appconf.String("mysqladdr")
	G_mysql_port = appconf.String("mysqlport")
	G_mysql_dbname = appconf.String("mysqldbname")
	G_mysql_uname = appconf.String("mysqlusername")
	G_mysql_passwd = appconf.String("mysqlpassword")
	G_mysql_maxidle = appconf.Int("mysqlmaxIdle")
	G_mysql_maxconn = appconf.Int("mysqlmaxConn")	return
}func init() {
	InitConfig()
}

3. 加载Mysql驱动,初始化数据库
vim models/model.go

添加以下内容
import (
	"github.com/astaxie/beego/orm"
	"backend/utils"	_ "github.com/go-sql-driver/mysql"
)func init() {
	orm.RegisterDriver("mysql", orm.DRMySQL)	// set default database
	orm.RegisterDataBase("default", "mysql",
utils.G_mysql_uname+":"+utils.G_mysql_passwd+"@tcp("+utils.G_mysql_addr+":"+utils.G_mysql_port+")/"+utils.G_mysql_dbname+"?charset=utf8mb4",
utils.G_mysql_maxidle, utils.G_mysql_maxconn)	//注册model
	orm.RegisterModel(new(Banner))	// create table
	//第二个参数是强制更新数据库
	//第三个参数是如果没有则同步
	orm.RunSyncdb("default", false, true)
}

4. ORM框架下的数据库操作
API接口功能实现 1. 设计基于接口的Request和Response请求数据结构
示例
type BannerResp struct {
	Errno string `json:"errno"`
	Errmsg string `json:"errmsg"`
	Data interface{} `json:"data"`
}
Request 为get参数
2. 根据流程图实现每个接口的业务逻辑
graph LR
start[获取参数] --> conditionA{检查参数}
conditionA -- 参数OK --> conditionB{读取数据库}
conditionA -- 参数错误 --> returnB[返回参数错误]
conditionB -- 成功 --> returnA[返回json data]
conditionB -- 失败 --> returnC[返回数据库查询失败]
returnA --> stop[结束]
returnB --> stop[结束]
returnC --> stop[结束]

3. ORM框架下的数据库操作
4. 创建路由及自动生成API注解
beego支持通过注解自动生成基于swagger的API。
1)全局设置
  • 配置文档开关
vim conf/app.conf

添加一下内容
EnableDocs = true

  • 设置全局路由doc信息
vim routers/router.go

在最顶部添加:
// @APIVersion 1.0.0
// @Title mobile API
// @Description mobile has every tool to get any job done, so codename for the new mobile APIs.
// @Contact astaxie@gmail.com
package routers

以上是常用的参数,通个
bee api project_name
会自动生成,更多参数参考。
2)路由解析
vim routers/router.go

支持的API swagger文档自动生成,对路由设置有一定的要求,其他的方式不会自动解析,即
beego.NSRouter()
示例:
func init() {
ns :=
beego.NewNamespace("/api/v1.0",
beego.NSNamespace("/banner",
beego.NSInclude(
&controllers.BannerController{},
),
),
)
beego.AddNamespace(ns)
}
3)接口注解
示例:
vim controllers/banner.go
// @Title Get the Banners
// @Description get the banners of given name
// @Success 200 {object} controllers.BannerResp
// @Paramnamequerystringtrue"banner position"
// @Failure 400 BAD REQUEST
// @Failure 500 INTERNAL SERVER ERROR
// @router / [get]
注解详解
@Title@Description@ParamformData、query、path、body、header。formDatapostquerypath/user/:ididbodyheader"Content-Type":"application/json"int,objectjsontrue,false@Success@Failure@router[],
4)生成swagger文档
bee run -gendoc=true -downdoc=true
,让我们的 API 应用跑起来,
-gendoc=true
表示每次自动化的
build
文档,
-downdoc=true
就会自动的下载 swagger 文档查看器。
在浏览器中输入下面的URL,就可以看到该文档一开始的API文档了:
http://localhost:8080/swagger/

5)测试请求接口
测试接口可以用go写一个小的测试程序进行测试,或者邀请前端的工程师帮忙测试,当然也有一些工具可以辅助测试。
  • 推荐一款接口测试工具postman
[GET] /api/v1.0/banner?name=homepage

可以看到返回结果,说明接口已经调试成功了:
{
"errno": "0",
"errmsg": "成功",
"data": {
"list": [
{
"content": "呵护你的每一天",
"image_url": 
"static/img/banner2.jpg",
"link": "",
"title": "医佰康"
}
],
"total": 1
}
}

6)可能遇到的问题
ctx.Output.Header("Access-Control-Allow-Origin", "*")
遇到的问题
ctx.Output.Header("Access-Control-Allow-Origin", "*")
在http请求的响应流头部加上如下信息
rw.Header().Set(“Access-Control-Allow-Origin”, “*”)
rw是http.ResponseWriter对象
2、Beego中添加路由过滤器
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
AllowAllOrigins:true,
AllowMethods:[]string{"*"},
AllowHeaders:[]string{"Origin", "Authorization", "Access-Control-Allow-Origin"},
ExposeHeaders:[]string{"Content-Length", "Access-Control-Allow-Origin"},
AllowCredentials: true,
}))