1. 程式人生 > 程式設計 >[系列] - go-gin-api 規劃目錄和引數驗證(二)

[系列] - go-gin-api 規劃目錄和引數驗證(二)

概述

首先同步下專案概況:

上篇文章分享了,使用 go modules 初始化專案,這篇文章咱們分享:

  • 規劃目錄結構
  • 模型繫結和驗證
  • 自定義驗證器
  • 制定 API 返回結構

廢話不多說,咱們開始吧。

規劃目錄結構

├─ go-gin-api
│  ├─ app
│     ├─ config           //配置檔案
│        ├─ config.go
│     ├─ controller       //控制器層
│        ├─ param_bind
│        ├─ param_verify
│        ├─ ...
│     ├─ model            //資料庫ORM
│        ├─ proto
│        ├─ ...
│     ├─ repository       //資料庫操作層
│        ├─ ...
│     ├─ route            //路由
│        ├─ middleware
│        ├─ route.go
│     ├─ service          //業務層
│        ├─ ...
│     ├─ util             //工具包
│        ├─ ...
│  ├─ vendor  //依賴包
│     ├─ ...
│  ├─ go.mod
│  ├─ go.sum
│  ├─ main.go //入口檔案
複製程式碼

上面的目錄結構是我自定義的,大家也可以根據自己的習慣去定義。

controller 控制器層主要對提交過來的資料進行驗證,然後將驗證完成的資料傳遞給 service 處理。

在 gin 框架中,引數驗證有兩種:

1、模型繫結和驗證。

2、自定義驗證器。

其中目錄 param_bind,儲存的是引數繫結的資料,目錄param_verify 儲存的是自定義驗證器。

接下來,讓咱們進行簡單實現。

模型繫結和驗證

比如,有一個建立商品的介面,商品名稱不能為空。

配置路由(route.go):

ProductRouter := engine.Group("")
{
	// 新增產品
	ProductRouter.POST("/product"
,product.Add) // 更新產品 ProductRouter.PUT("/product/:id",product.Edit) // 刪除產品 ProductRouter.DELETE("/product/:id",product.Delete) // 獲取產品詳情 ProductRouter.GET("/product/:id",product.Detail) } 複製程式碼

引數繫結(param_bind/product.go):

type ProductAdd struct {
	Name string `form:"name" json:"name" binding:"required"
` } 複製程式碼

控制器呼叫(controller/product.go):

if err := c.ShouldBind(&param_bind.ProductAdd{}); err != nil {
	utilGin.Response(-1,err.Error(),nil)
	return
}
複製程式碼

咱們用 Postman 模擬 post 請求時,name 引數不傳或傳遞為空,會出現:

Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag

這就使用到了引數設定的 binding:"required"

那麼還能使用 binding 哪些引數,有檔案嗎?

有。Gin 使用 go-playground/validator.v8 進行驗證,相關檔案:

godoc.org/gopkg.in/go…

接下來,咱們實現一下自定義驗證器。

自定義驗證器

比如,有一個建立商品的介面,商品名稱不能為空並且引數名稱不能等於 admin。

類似於這種業務需求,無法 binding 現成的方法,需要我們自己寫驗證方法,才能實現。

自定義驗證方法(param_verify/product.go)

func NameValid (
	v *validator.Validate,topStruct reflect.Value,currentStructOrField reflect.Value,field reflect.Value,fieldType reflect.Type,fieldKind reflect.Kind,param string,) bool {
	if s,ok := field.Interface().(string); ok {
		if s == "admin" {
			return false
		}
	}
	return true
}
複製程式碼

引數繫結(param_bind/product.go):

type ProductAdd struct {
	Name string `form:"name" json:"name" binding:"required,NameValid"`
}
複製程式碼

同時還要繫結驗證器:

// 繫結驗證器
if v,ok := binding.Validator.Engine().(*validator.Validate); ok {
	v.RegisterValidation("NameValid",param_verify.NameValid)
}
複製程式碼

咱們用 Postman 模擬 post 請求時,name 引數不傳或傳遞為空,會出現:

Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag

name=admin 時:

Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'NameValid' tag

OK,上面兩個驗證都生效了!

上面的輸出都是在控制檯,能不能返回一個 Json 結構的資料呀?

能。接下來咱們制定 API 返回結構。

制定 API 返回結構

{
    "code": 1,"msg": "","data": null
}
複製程式碼

API 介面的返回的結構基本都是這三個欄位。

比如 code=1 表示成功,code=-1 表示失敗。

msg 表示提示資訊。

data 表示要返回的資料。

那麼,我們怎麼在 gin 框架中實現它,其實很簡單 基於 c.JSON() 方法進行封裝即可,直接看程式碼。

package util

import "github.com/gin-gonic/gin"

type Gin struct {
	Ctx *gin.Context
}

type response struct {
	Code     int         `json:"code"`
	Message  string      `json:"msg"`
	Data     interface{} `json:"data"`
}

func (g *Gin)Response(code int,msg string,data interface{}) {
	g.Ctx.JSON(200,response{
		Code    : code,Message : msg,Data    : data,})
	return
}
複製程式碼

控制器呼叫(controller/product.go):

utilGin := util.Gin{Ctx:c}
if err := c.ShouldBind(&param_bind.ProductAdd{}); err != nil {
	utilGin.Response(-1,nil)
	return
}
複製程式碼

咱們用 Postman 模擬 post 請求時,name 引數不傳或傳遞為空,會出現:

{
    "code": -1,"msg": "Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'required' tag","data": null
}
複製程式碼

name=admin 時:

{
    "code": -1,"msg": "Key: 'ProductAdd.Name' Error:Field validation for 'Name' failed on the 'NameValid' tag","data": null
}
複製程式碼

OK,上面兩個驗證都生效了!

原始碼地址

github.com/xinliangnot…

go-gin-api 系列文章