This is a creation in Article, where the information may have evolved or changed.
Write tag's API ' s, Models
This large sections will involve the following knowledge points:
- Gin:golang a micro-frame, excellent performance
- Beego-validation: The Beego Form validation library used in this section, Chinese documents
- Gorm, developer-friendly ORM framework, English documentation
- COM, toolkit
- The writing of business logic
We started writing business code, and the blog post would have a label concept,
Defining interfaces
This section is to write the logic of the label, we think about, the general interface for adding and deleting is the basis of the change, then we define the interface bar!
- Get a list of tags: get ("/tags")
- New label: POST ("/tags")
- Update the specified label: PUT ("/tags/:id")
- Delete the specified tag: delete ("/tags/:id")
To write a routed empty shell
Start to write the routing file logic, routers
under the new api
directory, we are currently the first API large version, so api
under the new v1
directory, create new tag.go
files, write content:
package v1import ( "github.com/gin-gonic/gin")//获取多个文章标签func GetTags(c *gin.Context) {}//新增文章标签func AddTag(c *gin.Context) {}//修改文章标签func EditTag(c *gin.Context) {}//删除文章标签func DeleteTag(c *gin.Context) {}
Registering routes
We open routers
the following router.go
file, modify the contents of the file as:
package routersimport ( "github.com/gin-gonic/gin" "gin-blog/routers/api/v1" "gin-blog/pkg/setting")func InitRouter() *gin.Engine { r := gin.New() r.Use(gin.Logger()) r.Use(gin.Recovery()) gin.SetMode(setting.RunMode) apiv1 := r.Group("/api/v1") { //获取标签列表 apiv1.GET("/tags", v1.GetTags) //新建标签 apiv1.POST("/tags", v1.AddTag) //更新指定标签 apiv1.PUT("/tags/:id", v1.EditTag) //删除指定标签 apiv1.DELETE("/tags/:id", v1.DeleteTag) } return r}
Current directory structure:
gin-blog/├── conf│ └── app.ini├── main.go├── middleware├── models│ └── models.go├── pkg│ ├── e│ │ ├── code.go│ │ └── msg.go│ ├── setting│ │ └── setting.go│ └── util│ └── pagination.go├── routers│ ├── api│ │ └── v1│ │ └── tag.go│ └── router.go├── runtime
Verify that the route is registered successfully
Go back to the command line, execute go run main.go
, check whether the routing rule is registered successfully.
$ go run main.go [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode)[GIN-debug] GET /api/v1/tags --> gin-blog/routers/api/v1.GetTags (3 handlers)[GIN-debug] POST /api/v1/tags --> gin-blog/routers/api/v1.AddTag (3 handlers)[GIN-debug] PUT /api/v1/tags/:id --> gin-blog/routers/api/v1.EditTag (3 handlers)[GIN-debug] DELETE /api/v1/tags/:id --> gin-blog/routers/api/v1.DeleteTag (3 handlers)
Run successfully, then we happily begin to write our interface !
Download Dependent packages
First of all we want to pull validation
the dependency package, in the back of the interface will use to form validation
go get -u github.com/astaxie/beego/validation
Models logic for writing tag lists
Create models
a directory tag.go
, write the contents of the file:
package modelstype Tag struct { Model Name string `json:"name"` CreatedBy string `json:"created_by"` ModifiedBy string `json:"modified_by"` State int `json:"state"`}func GetTags(pageNum int, pageSize int, maps interface {}) (tags []Tag) { db.Where(maps).Offset(pageNum).Limit(pageSize).Find(&tags) return}func GetTagTotal(maps interface {}) (count int){ db.Model(&Tag{}).Where(maps).Count(&count) return}
- We created one
Tag struct{}
that was used for Gorm
the use. and gives a subordinate attribute json
, so that c.JSON
when the child will automatically convert the format, very convenient
- There may be some beginners see
return
, and the latter does not follow the variable, will not understand; in fact, you can see at the end of the function, we have shown that the return value is declared, this variable can be used directly in the body of the function, because he was declared at the outset
- Some people will wonder
db
where it comes from, because under the same models
package, so it db *gorm.DB
can be used directly
Routing logic for writing label lists
Open routers
The directory under the V1 version tag.go
, first we write the interface to get the label list
To modify the contents of a file:
package v1import ( "net/http" "github.com/gin-gonic/gin" //"github.com/astaxie/beego/validation" "github.com/Unknwon/com" "gin-blog/pkg/e" "gin-blog/models" "gin-blog/pkg/util" "gin-blog/pkg/setting")//获取多个文章标签func GetTags(c *gin.Context) { name := c.Query("name") maps := make(map[string]interface{}) data := make(map[string]interface{}) if name != "" { maps["name"] = name } var state int = -1 if arg := c.Query("state"); arg != "" { state, _ = com.StrTo(arg).Int() maps["state"] = state } code := e.SUCCESS data["lists"] = models.GetTags(util.GetPage(c), setting.PageSize, maps) data["total"] = models.GetTagTotal(maps) c.JSON(http.StatusOK, gin.H{ "code" : code, "msg" : e.GetMsg(code), "data" : data, })}//新增文章标签func AddTag(c *gin.Context) {}//修改文章标签func EditTag(c *gin.Context) {}//删除文章标签func DeleteTag(c *gin.Context) {}
c.Query
Can be used to get such ?name=test&state=1
URL parameters, while c.DefaultQuery
setting a default value is supported
code
The variable uses the e
module's error code, which is the previously planned error code to facilitate troubleshooting and identification of records
util.GetPage
Ensures that the processing of each interface page
is consistent
c *gin.Context
Is Gin
an important component that can be understood as context, which allows us to pass variables between middleware, manage flows, validate JSON of requests, and render JSON responses
In the local execution curl 127.0.0.1:8000/api/v1/tags
, the correct return value is {"code":200,"data":{"lists":[],"total":0},"msg":"ok"}
, if there is a problem, please combine the gin result to make a mistake.
In the Get tag list interface, we can filter the query criteria according to,,, the step of name
state
page
paging can be app.ini
configured, in lists
combination, to total
return to achieve the paging effect.
Models logic to write new tags
Next we write the interface for adding tags
Open models
the directory under V1 version tag.go
, modify the file (add 2 methods):
...func ExistTagByName(name string) bool { var tag Tag db.Select("id").Where("name = ?", name).First(&tag) if tag.ID > 0 { return true } return false}func AddTag(name string, state int, createdBy string) bool{ db.Create(&Tag { Name : name, State : state, CreatedBy : createdBy, }) return true}...
Routing logic to write new tags
Open routers
directory tag.go
, modify file (change Addtag method):
Package V1import ("Log" "Net/http" "Github.com/gin-gonic/gin" "Github.com/astaxie/beego/validation" "Githu B.com/unknwon/com "gin-blog/pkg/e" "Gin-blog/models" "Gin-blog/pkg/util" "gin-blog/pkg/setting") ...//Add article label Fu NC addtag (c *gin. Context) {Name: = C.query ("name") state, _: = com. Strto (C.defaultquery ("state", "0")). Int () CreatedBy: = C.query ("created_by") Valid: = Validation. validation{} valid. Required (name, "name"). Message ("Name cannot be empty") valid. Required (name, "Created_by"). Message ("Creator cannot be empty") valid. MaxSize (name, +, "created_by"). Message ("creator maximum of 100 characters") valid. MaxSize (name, +, "name"). Message ("Maximum name is 100 characters") valid. Range (state, 0, 1, "state"). Message ("state only allows 0 or 1") Code: = E.invalid_params if! Valid. HasErrors () {if! models. Existtagbyname (name) {code = E.success models. Addtag (name, State, CreatedBy)} else {code = E.error_exist_tag}} else {for _, err : = Range Valid.errors {log. Println (Err. Key, Err. Message)}} c.json (http. Statusok, Gin. h{"Code": Code, "MSG": E.getmsg (Code), "Data": Make (Map[string]string),})} ...
Use Postman
post access http://127.0.0.1:8000/api/v1/tags?name=1&state=1&created_by=test
to see code
if the return 200
and the blog_tag
table have values, and the values are correct.
Write Models Callbacks
But this time people will find, I clearly added a tag, but created_on
actually no value, then do change the label when modified_on
there is this problem?
To solve this problem, we need to open the models
file in the directory tag.go
, modify the contents of the file (modify the package reference and add 2 methods):
package modelsimport ( "time" "github.com/jinzhu/gorm")...func (tag *Tag) BeforeCreate(scope *gorm.Scope) error { scope.SetColumn("CreatedOn", time.Now().Unix()) return nil}func (tag *Tag) BeforeUpdate(scope *gorm.Scope) error { scope.SetColumn("ModifiedOn", time.Now().Unix()) return nil}
Restart the service, and then use the Postman
post access http://127.0.0.1:8000/api/v1/tags?name=2&state=1&created_by=test
, found that created_on
already has the value!
In these pieces of code, the knowledge points are involved:
This belongs to gorm
a Callbacks
callback method that can be defined as a pointer to a model structure that will be called when created, updated, queried, deleted, and if any callbacks return an error, Gorm will stop future operations and roll back all changes.
gorm
Supported Callback methods:
- Created: BeforeSave, Beforecreate, Aftercreate, Aftersave
- Updated: BeforeSave, BeforeUpdate, AfterUpdate, Aftersave
- Delete: BeforeDelete, AfterDelete
- Enquiry: Afterfind
Write routing logic for the rest of the interfaces
Next, let's take the remaining two interfaces (Edittag, Deletetag) in one gulp.
Open routers
The directory under the V1 version of the tag.go
file, modify the content:
...//modify article label Func Edittag (C *gin. Context) {id, _: = com. Strto (C.param ("id")). Int () Name: = C.query ("name") ModifiedBy: = C.query ("modified_by") Valid: = Validation. validation{} var state int. =-1 if arg: = C.query ("state"); Arg! = "" {state, _ = com. Strto (ARG). Int () valid. Range (state, 0, 1, "state"). Message ("state only allows 0 or 1")} valid. Required (ID, "id"). Message ("ID cannot be empty") valid. Required (ModifiedBy, "modified_by"). Message ("Modifier cannot be empty") valid. MaxSize (ModifiedBy, "modified_by"). Message ("modifier maximum is 100 characters") valid. MaxSize (name, +, "name"). Message ("Maximum name is 100 characters") Code: = E.invalid_params if! Valid. HasErrors () {code = E.success if models. Existtagbyid (ID) {Data: = Make (map[string]interface{}) data["modified_by"] = ModifiedBy If name! = "" {data["name"] = name} if state! =-1 {data["state"] = State} models. Edittag (ID, data) } else {code = E.error_not_exist_tag}} else {for _, err: = range valid. Errors {log. Println (Err. Key, Err. Message)}} c.json (http. Statusok, Gin. h{"Code": Code, "MSG": E.getmsg (Code), "Data": Make (Map[string]string),})}//delete Article tag func Dele Tetag (c *gin. Context) {id, _: = com. Strto (C.param ("id")). Int () Valid: = Validation. validation{} valid. Min (ID, 1, "id"). Message ("ID must be greater than 0") Code: = E.invalid_params if! Valid. HasErrors () {code = E.success if models. Existtagbyid (ID) {models. Deletetag (ID)} else {code = E.error_not_exist_tag}} else {for _, err: = Range Vali d.errors {log. Println (Err. Key, Err. Message)}} c.json (http. Statusok, Gin. h{"Code": Code, "MSG": E.getmsg (Code), "Data": Make (Map[string]string),})}
Write models logic for the rest of the interfaces
Open models
tag.go
, modify the contents of the file:
...func ExistTagByID(id int) bool { var tag Tag db.Select("id").Where("id = ?", id).First(&tag) if tag.ID > 0 { return true } return false}func DeleteTag(id int) bool { db.Where("id = ?", id).Delete(&Tag{}) return true}func EditTag(id int, data interface {}) bool { db.Model(&Tag{}).Where("id = ?", id).Updates(data) return true}...
Validation features
Restart the service with postman
- Put access http://127.0.0.1:8000/api/v1/tags/1?name=edit1&state=0&modified_by=edit1 to see if code returns 200
- Delete Access HTTP://127.0.0.1:8000/API/V1/TAGS/1 to see if code returns 200
At this point, the tag API's is complete, and the next section we will start article API's writing!
Reference
Sample code for this series