This is a creation in Article, where the information may have evolved or changed.
Write article's API ' s, Models
Defining interfaces
This section writes the logic of the article, we define the interface!
- Get a list of articles: Get ("/articles")
- Get the specified article: Post ("/articles/:id")
- New article: POST ("/articles")
- Update specified article: PUT ("/articles/:id")
- Delete the specified article: Delete ("/articles/:id")
Writing routing logic
routers
under the V1 version, create a new article.go
file, write the content:
package v1import ( "github.com/gin-gonic/gin")//获取单个文章func GetArticle(c *gin.Context) {}//获取多个文章func GetArticles(c *gin.Context) {}//新增文章func AddArticle(c *gin.Context) {}//修改文章func EditArticle(c *gin.Context) {}//删除文章func DeleteArticle(c *gin.Context) {}
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 { ... apiv1 := r.Group("/api/v1") { ... //获取文章列表 apiv1.GET("/articles", v1.GetArticles) //获取指定文章 apiv1.GET("/articles/:id", v1.GetArticle) //新建文章 apiv1.POST("/articles", v1.AddArticle) //更新指定文章 apiv1.PUT("/articles/:id", v1.EditArticle) //删除指定文章 apiv1.DELETE("/articles/:id", v1.DeleteArticle) } return r}
Current directory structure:
gin-blog/├── conf│ └── app.ini├── main.go├── middleware├── models│ ├── models.go│ └── tag.go├── pkg│ ├── e│ │ ├── code.go│ │ └── msg.go│ ├── setting│ │ └── setting.go│ └── util│ └── pagination.go├── routers│ ├── api│ │ └── v1│ │ ├── article.go│ │ └── tag.go│ └── router.go├── runtime
After the basic routing rule configuration is complete, let's start writing our interface !
Writing models Logic
Create models
a directory article.go
, write the contents of the file:
package modelsimport ( "github.com/jinzhu/gorm" "time")type Article struct { Model TagID int `json:"tag_id" gorm:"index"` Tag Tag `json:"tag"` Title string `json:"title"` Desc string `json:"title"` Content string `json:"content"` CreatedBy string `json:"created_by"` ModifiedBy string `json:"modified_by"` State int `json:"state"`}func (article *Article) BeforeCreate(scope *gorm.Scope) error { scope.SetColumn("CreatedOn", time.Now().Unix()) return nil}func (article *Article) BeforeUpdate(scope *gorm.Scope) error { scope.SetColumn("ModifiedOn", time.Now().Unix()) return nil}
We created one Article struct {}
, with the Tag
difference is that a Article
few more
gorm:index
, which declares that this field is indexed, and if you use the Auto-migration feature, it will have an effect if you do not use it.
Tag
field, which is actually a nested struct
, it is used to TagID
correlate with the model, Tag
when executing the query, can reach Article
, the Tag
function of correlation query
time.Now().Unix()
Returns the current timestamp
Next, make sure that you have read through and understand the contents of the previous chapter, because the logical deviations are not too far away, we write these five interfaces directly in this section
Open models
Directory article.go
, modify the contents of the file:
Package Modelsimport ("Time" "Github.com/jinzhu/gorm") type article struct {Model TagID int ' json: "tag_id" Go RM: "index" ' tag tag ' json: ' Tag ' ' title string ' JSON: ' title ' ' Desc string ' json: ' title ' ' Content string ' json : "Content" ' CreatedBy string ' JSON: "created_by" ' ModifiedBy string ' JSON: "modified_by" ' state int ' JSON: "State" '} Func Existarticlebyid (id int) BOOL {var article article db. Select ("id"). Where ("id =?", id). First (&article) if article.id > 0 {return true} return False}func Getarticletotal (Maps interface {}) (count int) {db. Model (&article{}). Where (MAPS). Count (&count) return}func getarticles (pagenum int, pageSize int, maps Interface {}) (articles []article) {db]. Preload ("Tag"). Where (MAPS). Offset (Pagenum). Limit (pageSize). Find (&articles) return}func GetArticle (id int) (article article) {db. Where ("id =?", id). First (&article) db. Model (&article). Related (&article. TAG) reTurn}func editarticle (id int, data Interface {}) bool {db. Model (&article{}). Where ("id =?", id). Updates (data) return True}func addarticle (data map[string]interface {}) bool {db. Create (&article {tagid:data["tag_id"]. ( int), title:data["Title"]. (string), desc:data["Desc"]. (string), content:data["Content". (string), createdby:data["created_by"]. (string), state:data["state"]. (int),}) return true}func deletearticle (id int) BOOL {db. Where ("id =?", id). Delete (article{}) return True}func (article *article) beforecreate (Scope *gorm. Scope) Error {scope. Setcolumn ("CreatedOn", time. Now (). Unix ()) return Nil}func (article *article) BeforeUpdate (Scope *gorm. Scope) Error {scope. Setcolumn ("ModifiedOn", time. Now (). Unix ()) return nil}
Here we come up with a three-point difference in terms of
1. Article
How do we relate to Tag
???
func GetArticle(id int) (article Article) { db.Where("id = ?", id).First(&article) db.Model(&article).Related(&article.Tag) return }
Be able to reach the association, first of all it gorm
has done a lot of conventional
Article
There is a struct member that is TagID
the foreign key. The gorm
association between these two classes is found through the +id of the class name.
Article
There is a struct member, which is the Tag
structure we nest in Article
Tag
, and we can do this by Related
correlating the query
2, Preload
What is the thing, why the query can draw the association of each item Tag
?
func GetArticles(pageNum int, pageSize int, maps interface {}) (articles []Article) { db.Preload("Tag").Where(maps).Offset(pageNum).Limit(pageSize).Find(&articles) return}
Preload
is a preloader, it will execute two SQL, respectively SELECT * FROM blog_articles;
SELECT * FROM blog_tag WHERE id IN (1,2,3,4);
, and, then after querying out the structure, the gorm
internal processing of the corresponding mapping logic, filling it into Article
the Tag
, will be particularly convenient, and avoid the circular query
So is there any other way, roughly two kinds of
Integrated, or Preload
better, if you have a better plan, welcome to say:)
3, v.(I)
what is it?
v
Represents an interface value that I
represents an interface type. This is actually the type assertion in Golang, which is used to determine whether the actual type of an interface value is a type, or whether the type of a non-interface value implements an interface type
Open routers
The Directory V1 version of the article.go
file, modify the contents of the file:
Package V1import ("Net/http" "Log" "Github.com/gin-gonic/gin" "Github.com/astaxie/beego/validation" "Githu B.com/unknwon/com "" Gin-blog/models "" gin-blog/pkg/e "" gin-blog/pkg/setting "" gin-blog/pkg/util ")//Get single article Func GetArticle (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 var data interface {} if! Valid. HasErrors () {if models. Existarticlebyid (ID) {data = models. GetArticle (id) code = e.success} else {code = e.error_not_exist_article}} 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": Data,})}//get multiple articles func Getarticles (c *gin. Context) {data: = Make (map[string]interface{}) Maps: = Make (Map[string]interface{}) Valid: = Validation. validation{} var state int. =-1 if arg: = C.query ("state"); Arg! = "" {state, _ = com. Strto (ARG). Int () maps["state"] = State valid. Range (state, 0, 1, "state"). Message ("state only allows 0 or 1")} var tagId int =-1 if arg: = C.query ("tag_id"); Arg! = "" {tagId, _ = com. Strto (ARG). Int () maps["tag_id"] = tagId valid. Min (tagId, 1, "tag_id"). Message ("tag ID must be greater than 0")} code: = E.invalid_params if! Valid. HasErrors () {code = e.success data["lists"] = models. Getarticles (util. GetPage (c), setting. PageSize, maps) data["Total" = models. Getarticletotal (MAPS)} 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": Data,})}//new article Func addarticle (c *gin. Context) {TagId, _: = com. Strto (C.query ("tag_id")). Int () Title: = C.query ("title") Desc: = c.query ("desc") Content: = C.query ("content") CreatedBy: = C.query ("created_by") state, _: = com. Strto (C.defaultquery ("state", "0")). Int () Valid: = Validation. validation{} valid. Min (tagId, 1, "tag_id"). Message ("label ID must be greater than 0") valid. Required (title, "title"). Message ("Title cannot be empty") valid. Required (DESC, "desc"). Message ("Brief description cannot be empty") valid. Required (Content, "content"). Message ("Content cannot be empty") valid. Required (CreatedBy, "created_by"). Message ("Creator cannot be empty") valid. Range (state, 0, 1, "state"). Message ("state only allows 0 or 1") Code: = E.invalid_params if! Valid. HasErrors () {if models. Existtagbyid (tagId) {data: = Make (Map[string]interface {}) data["tag_id"] = tagId data[" Title "] = title data[" desc "] = desc data[" Content "] = content data[" created_by "] = Create DBy data["state") = State models. Addarticle (data) code = e.success} 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]interface{}),}//Modify article func Edi Tarticle (c *gin. Context) {valid: = validation. validation{} ID, _: = com. Strto (C.param ("id")). Int () TagId, _: = com. Strto (C.query ("tag_id")). Int () Title: = C.query ("title") Desc: = C.query ("desc") Content: = C.query ("content") ModifiedBy: = C.query ("M Odified_by ") 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. Min (ID, 1, "id"). Message ("ID must be greater than 0") valid. MaxSize (title, "title"). Message ("title up to 100 characters") valid. MaxSize (DESC, 255, "desc"). Message ("A description of the maximum of 255 characters") valid. MaxSize (content, 65535, "content"). Message ("Maximum content of 65535 characters") valid. Required (ModifiedBy, "MOdified_by "). Message ("Modifier cannot be empty") valid. MaxSize (ModifiedBy, "modified_by"). Message ("modifier maximum of 100 characters") Code: = E.invalid_params if! Valid. HasErrors () {if models. Existarticlebyid (ID) {if models. Existtagbyid (tagId) {data: = Make (Map[string]interface {}) if TagId > 0 { data["tag_id"] = tagId} if title! = "" {data["title"] = Title} If desc = "" {data["desc"] = desc} If content! = "" {data["content"] = content} data[ "modified_by"] = ModifiedBy models. Editarticle (ID, data) code = e.success} else {code = E.error_not_exist_ta G}} else {code = E.error_not_exist_article}} 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 func deletear Ticle (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 () {if models. Existarticlebyid (ID) {models. Deletearticle (id) code = e.success} else {code = e.error_not_exist_article}} el Se {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),})}
Current directory structure:
gin-blog/├── conf│ └── app.ini├── main.go├── middleware├── models│ ├── article.go│ ├── models.go│ └── tag.go├── pkg│ ├── e│ │ ├── code.go│ │ └── msg.go│ ├── setting│ │ └── setting.go│ └── util│ └── pagination.go├── routers│ ├── api│ │ └── v1│ │ ├── article.go│ │ └── tag.go│ └── router.go├── runtime
Validation features
We restart the service, execute go run main.go
, check the console output results
$ 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) [Gin-debug] get/api/v1/articles--gin-blog/routers/api/v1. Getarticles (3 handlers) [Gin-debug] Get/api/v1/articles/:id--gin-blog/routers/api/v1. GetArticle (3 handlers) [Gin-debug] post/api/v1/articles--gin-blog/routers/api/v1. Addarticle (3 handlers) [Gin-debug] Put/api/v1/articles/:id--gin-blog/routers/api/v1. Editarticle (3 handlers) [Gin-debug] delete/api/v1/aRticles/:id--Gin-blog/routers/api/v1. Deletearticle (3 handlers)
The use Postman
of test interface is normal (we can choose the appropriate parameter transfer method, here in order to facilitate the display I chose the URL parameter),
- Post:http://127.0.0.1:8000/api/v1/articles?tag_id=1&title=test1&desc=test-desc&content= Test-content&created_by=test-created&state=1
- Get:http://127.0.0.1:8000/api/v1/articles
- Get:http://127.0.0.1:8000/api/v1/articles/1
- Put:http://127.0.0.1:8000/api/v1/articles/1?tag_id=1&title=test-edit1&desc=test-desc-edit&content= Test-content-edit&modified_by=test-created-edit&state=0
- Delete:http://127.0.0.1:8000/api/v1/articles/1
So far, our API's writing is here, and the next section we'll cover some other tricks!
Reference
Sample code for this series