As the half-brother of C language, the go language has shown its amazing talent in the development of back-end servers. It was born for high concurrency and multi-core.
Beego is a framework designed by Chinese Xie Dashen by referring to the essence of the three frameworks of tornado, sinatra and flask. This framework can help people quickly develop API, web, and back-end service applications.
Standing on the shoulders of giants, it is also a specific practical project for learning go, starting from beego to create a web project based on the separation of front and back ends.
[help]
install beego
beego is installed directly through go get. The bee tool is a project created to assist in the rapid development of beego projects. Through bee, you can easily create, hot compile, develop, test, and deploy beego projects.
This project will create an api project based on bee, and the development IDE uses goland (support genuine, you know)
$ go get -u github.com/astaxie/beego
$ go get -u github.com/beego/bee
Create an API project
Create a xxxx.com project:
$bee api xxxx.com
Run the project below, -gendoc=true
which means that every time you automate the build document, -downdoc=true
you will automatically download the swagger document viewer:
$bee run -gendoc=true -downdoc=true
Enter in the browser http://localhost:8080/swagger/
to see the user and object interfaces preset by the project:
Design interface API
Features of RESTful API
- Based on "resources", whether it is data or services, everything is a resource in RESTFul design.
- no status. One call will generally return the result, and there is no such thing as "opening a connection - accessing data - closing a connection" that depends on the previous call.
- Verbs usually do not appear in URLs, only nouns
- URL semantics are clear and unambiguous
- Use HTTP's GET, POST, DELETE, and PUT to indicate the addition, deletion, modification, and query of resources
- Use JSON instead of XML
RESTful style interface design should follow the following principles as much as possible.
Refer to RESTful API interface design :
- Use HTTP verbs to indicate adding, deleting, modifying and querying resources, GET: query, POST: add, PUT: update, DELETE: delete
- The returned result must use JSON
- HTTP status codes have specific meanings in REST: 200, 201, 202, 204, 400, 401, 403, 500. For example, 401 means that the user identity authentication failed, and 403 means that your identity verification has passed, but you cannot operate this resource.
- If an error occurs, return an error code
- API must have the concept of version, v1, v2, v3
- Use Token tokens for user identity verification and permission grading instead of cookies
- Case insensitive in url, do not appear in uppercase letters
- Use - instead of _ for string concatenation in URL paths
- There is a beautiful document
Writing API documents
There are many open source tools for writing API documents. I use them showdoc
(supporting API interface writing and data dictionary writing), which support online writing and independent deployment.
Example design interface:
A brief description:
- banner
Request URL:
/api/v1.0/banner
Request method:
- GET
parameter:
parameter name required type illustrate name yes string banner name
banner name parameter front page homepage page two page2 page three page3
return example
{ "errno": 0, "errmsg" "成功", "data": { "total": 1, "list": [ { "image_url": "http://", "title": "", "content": "!" "link": "http://" } ] } }
Return parameter description
parameter name type illustrate total int The total number of banners image_url string The map's address title string banner title content string banner text Remark
- For more return error codes, please see the error code description on the home page
Design data model
If the interface document is to sort out all the functions in the project and is the link of front-end and back-end development, then the data model is the cornerstone of the entire project development process. The quality of the data model design is directly related to the pros and cons of the project, including Maintainability, rationality of architecture, and multi-module collaboration.
1. Create a data dictionary
Database Design Specifications
For the database design principles to be noted, please refer to
- Rule 1: Figure out what is the nature of the application to be developed (OLTP or OPAP)?
- Rule 2: Divide your data into logical chunks to make things easier
- Rule 3: Don't Overuse "Rule 2"
- Rule 4: Treat duplicate, inconsistent data as your worst enemy
- Rule 5: Beware of data separated by delimiters, they violate "fields cannot be subdivided"
- Rule 6: Beware of columns that are only partially dependent on the primary key
- Rule 7: Choose derived columns carefully
- Rule 8: If performance is critical, don't be stubborn about avoiding redundancy
- Rule 9: Multidimensional data is an aggregation of disparate data
- Rule 10: Unify the design of those tables that have the characteristics of "name-value table"
- Rule 11: For infinitely hierarchical data, refer to your own primary key as a foreign key
Design Data Dictionary Example
- banner table, storing banner information
field type null default note id bigint no primary key name varchar(50) no name image varchar(255) no content title varchar(255) yes NULL title content varchar(255) yes NULL content
- Remarks: None
2. Design the beego data structure model
Beego's MVC model is divided into controllers, models, and views from the engineering structure; the routers directory provides the routing information of the interface.
controllers: the specific implementation of interface services, usually the specific implementation of the business;
models: data models, often directly related to the design of the database;
When designing the data structure of beego, the table structure creation and inspection of the ORM framework is realized by passing in the initial value. For details, please refer to the next section
vim models/model.go
Add the following
/* 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. Detailed explanation of beegode's ORM model definition
- The default table name rules, use camelCase to snake. You can also
func (u *User) TableName() string {return "auth_user"}
customize the table name.
AuthUser -> auth_user Auth_User -> auth__user DB_AuthUser -> d_b__auth_user
- Custom index (refer to the official document for details )
- Custom engine (only MYSQL is supported, please refer to the official document for details )
- Setting parameters
orm:"null;rel(fk)"
Use to;
separate , if there are multiple settings, use,
to separate- ignore field
AnyField string
orm:"-"`` auto
,Id
the Field will be regarded as self-increasingpk
Set as the primary key, suitable for customizing other types of primary keysnull
The default is NOT NULL, settingnull
means ALLOW NULLindex
Add an index to a single fieldunique
Addunique
keyscolumn
Set the name of the db field for the field,Name string `orm:"column(user_name)"`
size
The string type field defaults to varchar(255),Title string `orm:"size(60)"`
digits / decimals
Set float32, float64 floating-point precision, total length 12 4 digits after the decimal point eg: 99999999.9999
Money float64 `orm:"digits(12);decimals(4)"`
auto_now / auto_now_add
auto_now
The time is automatically updated every time the model is saved, and
auto_now_add
the time is only set when the model is saved for the first time
Created 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)"`
default
Set a default value for the field, the type must conform to (currently only used for default values when cascading deletes),Status int `orm:"default(1)"`
- Table relationship set
foreign key always on child table-
rel / reverse
RelOneToOne:type User struct { ... Profile *Profile `orm:"null;rel(one);on_delete(set_null)"` ... }
The corresponding reverse relation RelReverseOne:
type Profile struct { ... User *User `orm:"reverse(one)"` ... }
RelForeignKey:
type Post struct { ... User *User `orm:"rel(fk)"` // RelForeignKey relation ... }
The corresponding reverse relationship RelReverseMany:
type User struct { ... Posts []*Post `orm:"reverse(many)"` // fk 的反向关系 ... }
RelManyToMany:
type Post struct { ... Tags []*Tag `orm:"rel(m2m)"` // ManyToMany relation ... }
The corresponding reverse relationship RelReverseMany:
type Tag struct { ... Posts []*Post `orm:"reverse(many)"` ... }
-
rel_table / rel_through
This setting targets relational fieldsorm:"rel(m2m)"
ofrel_table
Set the name of the automatically generated m2m relationship table.
rel_through
If you want to use a custom m2m relationship table in the m2m relationship,
set its name through this, the format ispkg.path.ModelName
eg:app.models.PostTagRel
PostTagRel table needs to havePost
aTag
relationship with and When
it is set, it will be ignoredrel_table
rel_through
Setting method:
orm:"rel(m2m);rel_table(the_table_name)"
orm:"rel(m2m);rel_through(pkg.path.ModelName)"
-
on_delete sets how to deal with the relationship field when the corresponding rel relationship is deleted.
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
Related examples about on_delete
type User struct { Id int Name string } type Post struct { Id int Title string User *User `orm:"rel(fk)"` }
Suppose Post -> User is a ManyToOne relationship, that is, a foreign key.
o.Filter(“Id”, 1).Delete()
will delete the User whose Id is 1, and also delete the Post published by itIf you don't want to delete, you need to set set_null
type Post struct { Id int Title string User *User `orm:"rel(fk);null;on_delete(set_null)"` }
At this time, deleting User will only set the corresponding Post.user_id to NULL
Of course, sometimes for the sake of high performance, it doesn't matter if you save more data, but the problem of batch deletion > is the problem.
type Post struct { Id int Title string User *User `orm:"rel(fk);null;on_delete(do_nothing)"` }
Then as long as you delete it, you don't need to operate Post.
-
- Correspondence between model fields and database types
MySQL (Sqlite3 and PostgreSQL, please refer to )
go | mysql |
---|---|
int, int32 - when auto is set or the name is Id | integer AUTO_INCREMENT |
int64 - when auto is set or the name is Id | bigint AUTO_INCREMENT |
uint, uint32 - when auto is set or the name is Id | integer unsigned AUTO_INCREMENT |
uint64 - when auto is set or the name is Id | bigint unsigned AUTO_INCREMENT |
bool | bool |
string - defaults to size 255 | varchar(size) |
string - when type(char) is set | char(size) |
string - when type(text) is set | longtext |
time.Time - when the type is set to date | date |
time.Time | datetime |
byte | tinyint unsigned |
rune | integer |
int | integer |
you8 | 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_addr string //mysql ip 地址
G_mysql_port string //mysql 端口
G_mysql_dbname string //mysql db name
G_mysql_uname string //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 [email protected]
package routers
以上是常用的参数,通个bee api project_name
会自动生成,更多参数参考。
2)路由解析
vim routers/router.go
支持的API swagger文档自动生成,对路由设置有一定的要求,其他的方式不会自动解析,即
- namespace+Include 的写法,对于
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 // @Param name query string true "banner position" // @Failure 400 BAD REQUEST // @Failure 500 INTERNAL SERVER ERROR // @router / [get]
@Title
接口名,空格之后的内容全部解析为 title@Description
接口描述,空格之后的内容全部解析为 Description@Param
传入参数,共五列,使用空格或者 tab 分割- 参数名
- 参数类型,可以有的值是
formData、query、path、body、header。formData
表示是post
请求的数据;query
表示带在 url 之后的参数;path
表示请求路径上得参数,比如/user/:id
中的id
;body
表示是一个 raw 数据请求;header
表示带在 header 信息中得参数,比如"Content-Type":"application/json"
。 - 参数类型,
int,object
(swagger会表示为json
对象) - 该参数是否必须,
true,false
- 参数注释
@Success
成功返回给客户端的信息,共三个参数,之间用空格分开- status code
- 返回的类型,必须使用 {} 包含,比如 {int},{object}
- 返回的对象或者字符串信息,例如{object} 类型, models.ZDTProduct.ProductList 就表示 /models/ZDTProduct 目录下的 ProductList 对象
@Failure
失败返回的信息,包含两个参数,使用空格分隔- status code
- 错误信息
@router
两个参数,使用空格分隔- 请求的路由地址,支持正则和自定义路由
- 请求方法,放在
[]
之中,如果有多个方法,那么使用,
分隔
4)生成swagger文档
bee run -gendoc=true -downdoc=true
,让我们的 API 应用跑起来,-gendoc=true
表示每次自动化的 build
文档,-downdoc=true
就会自动的下载 swagger 文档查看器。
在浏览器中输入下面的URL,就可以看到该文档一开始的API文档了:
http://localhost:8080/swagger/
5) Test request
interface The test interface can be tested by writing a small test program in go, or by inviting front-end engineers to help with the test. Of course, there are also some tools that can assist in the test.
- Recommend an interface testing tool postman
[GET] /api/v1.0/banner?name=homepage
You can see the returned result, indicating that the interface has been debugged successfully:
{
"errno": "0",
"errmsg": "成功",
"data": {
"list": [
{
"content": "呵护你的每一天",
"image_url":
"static/img/banner2.jpg",
"link": "",
"title": "医佰康"
}
],
"total": 1
}
}
6) Possible problems
- API adds CORS support
ctx.Output.Header("Access-Control-Allow-Origin", "*")
problems encountered
-
packets.go:36: unexpected EOF
Use DB.SetConnMaxLifetime(time.Second) to set the maximum connection reuse time, 3~10 seconds is enough. orm basically has related settings.
Reference address: https://github.com/go-sql-driver/mysql/issues/674 -
API adds CORS support
ctx.Output.Header("Access-Control-Allow-Origin", "*")
Add the following information to the response stream header of the http request
rw.Header().Set(“Access-Control-Allow-Origin”, “*”)
rw is the http.ResponseWriter object
2. Add routing filter to 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,
}))