The introduction
- Today we continue to refine the previous section
- Using github.com/go-playground/validator/v10 for data verification, gin is very convenient
- Pull business logic out of the Server layer (service layer)
- Add interface signature verification (JWT custom middleware)
- Github code address
1, add check
type userId struct {
ID int `uri:"id" binding:"required"`
}
type user struct {
Name string `json:"name" xml:"name" form:"name" binding:"required"`
CreatedBy string `json:"created_by" xml:"created_by" form:"created_by" binding:"lowercase"`
}
func GetUser(c *gin.Context) {
user := new(userId)
iferr := c.ShouldBindUri(user); err ! =nil {
tool.JSONP(c, 40001, err.Error(), nil)
return
}
res, err := models.GetUser(user.ID)
iferr ! =nil {
tool.JSONP(c, 40001."No data at present".nil)
return
}
tool.JSONP(c, 0."Query successful", res)
}
Copy the code
Verify prompt Chinese culture
En. go was added to app/ Request
package request
import (
"errors"
"github.com/gin-gonic/gin/binding"
zh "github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
var v *validator.Validate
var trans ut.Translator
func init(a) {
zh_ch := zh.New()
uni := ut.New(zh_ch)
trans, _ = uni.GetTranslator("zh")
v, ok := binding.Validator.Engine().(*validator.Validate)
if ok {
// Validator registers translator
zh_translations.RegisterDefaultTranslations(v, trans)
}
}
func Translate(errs validator.ValidationErrors) string {
var errList []string
for _, e := range errs {
// can translate each error one at a time.
errList = append(errList, e.Translate(trans))
}
return errList[0] //strings.Join(errList, "|")
}
func GetError(err error) string {
switch err.(type) {
case validator.ValidationErrors:
return Translate(err.(validator.ValidationErrors))
default:
return errors.New("unknown error.").Error()
}
}
Copy the code
Custom error messages
Some validators defined by github.com/go-playground/validator are not translated (we can define ourselves)
V. registerTranslation ("lowercase", trans, func(ut ut.translator) error {return ut.Add("lowercase", "{0} must be lowercase ", true) // see universal-translator for details}, func(ut ut.translator, fe validator.FieldError) string { t, _ := ut.T("lowercase", fe.Field()) return t })Copy the code
Custom validators
Good interface extension, you can define some verification methods
v.RegisterValidation("checkMobile", checkMobile)
func checkMobile(fl validator.FieldLevel) bool {
mobile := fl.Field().String()
if len(mobile) ! =11 {
return false
}
return true
}
Copy the code
2. Pull away from the service layer
By adding files under APP/Services, we defined interfaces at the service layer, defined standards, and ensured that if we replaced the service, it would not affect the controller layer
package services
import (
"go-api/app/models"
"go-api/tool"
)
type UserContract interface {
UserGetsContract //GET users
UserGetContract //GET user
UserGetByIDContract //GETByID user
UserAddContract //Add user
UserExistContract //exist user
UserDeleteContract //delete user
UserEditContract //edit user
}
type UserGetsContract interface {
GetUserList(maps interface{}, offset int, limit int) tool.M
}
type UserGetContract interface {
GetUser(maps interface{}) tool.M
}
type UserGetByIDContract interface {
GetUserByID(id int) tool.M
}
type UserAddContract interface {
AddUser(maps map[string]interface{}, data map[string]interface{}) tool.M
}
type UserExistContract interface {
ExistUser(maps map[string]interface{}) bool
}
type UserDeleteContract interface {
DeleteUser(maps map[string]interface{}) tool.M
}
type UserEditContract interface {
EditUser(id int, data interface{}) tool.M
}
type UserService struct{}func (T UserService) GetUserList(maps interface{}, offet int, limit int) tool.M {
data := make(map[string]interface{})
data["lists"] = models.GetUsers(offet, limit, maps)
data["total"] = models.GetUserTotal(maps)
return tool.DataReturn(true."Query successful", data)
}
func (T UserService) GetUser(maps interface{}) tool.M {
data, err := models.GetUser(maps)
iferr ! =nil {
return tool.DataReturn(false."No data at present", err.Error())
}
return tool.DataReturn(true."Query successful", data)
}
func (T UserService) GetUserByID(id int) tool.M {
data, err := models.GetUserByID(id)
iferr ! =nil {
return tool.DataReturn(false."No data at present", err.Error())
}
return tool.DataReturn(true."Query successful", data)
}
func (T UserService) AddUser(maps map[string]interface{}, data map[string]interface{}) tool.M {
// Use MAPS to check if the data exists
if T.ExistUser(maps) {
return tool.DataReturn(false."The name already exists".nil)}else {
ifisbool := models.AddUser(data); ! isbool {return tool.DataReturn(false."Create failed".nil)}return tool.DataReturn(true."Created successfully".nil)}}func (T UserService) ExistUser(maps map[string]interface{}) bool {
return models.ExistUserByMaps(maps)
}
func (T UserService) DeleteUser(maps map[string]interface{}) tool.M {
if T.ExistUser(maps) {
isbool, err := models.DeleteUser(maps)
iferr ! =nil {
return tool.DataReturn(false."Delete failed", err.Error())
}
return tool.DataReturn(isbool, "Deleted successfully".nil)}else {
return tool.DataReturn(false."Records do not exist.".nil)}}func (T UserService) EditUser(id int, data interface{}) tool.M {
if models.ExistTagByID(id) {
_, err := models.EditUser(id, data)
iferr ! =nil {
return tool.DataReturn(false."Edit failed", err.Error())
}
return tool.DataReturn(true."Edit successful".nil)}else {
return tool.DataReturn(false."ID record does not exist".nil)}}Copy the code
2.1 Added the unified Service Layer return format
type M map[string]interface{}
type RetData struct{
Status bool `json:"status"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
func DataReturn(status bool,msg string,data interface{}) M{
result :=M{
"status" : status,
"msg" : msg,
"data" : data,
}
return result
}
func (m M) GetStatus(a) bool{
return m["status"]. (bool)}func (m M) GetMsg(a) string{
return m["msg"]. (string)}Copy the code
2.2 Modifying the Interface Controller Layer
var UserService services.UserContract
func init(a) {
UserService = &services.UserService{}
}
func GetUser(c *gin.Context) {
user := new(userId)
iferr := c.ShouldBindUri(user); err ! =nil {
tool.JSONP(c, 40001, request.GetError(err), nil)
return
}
ret := UserService.GetUserByID(user.ID)
if! ret.GetStatus() { tool.JSONP(c,40001, ret.GetMsg(), ret["data"])
return
}
tool.JSONP(c, 0, ret.GetMsg(), ret["data"])}Copy the code
So far we have layered the interface (controller -> parameter verification -> service layer -> Model layer) and then added the interface signature verification
3. Interface signature verification
We use JWT to do interface verification and customize a middleware
3.1. Create the jwt.go tool
package tool
import (
"time"
jwt "github.com/dgrijalva/jwt-go"
"go-api/config"
)
var (
JwtSecret = []byte(config.AppSetting.JwtSecret)
JwtExpiresAt = config.AppSetting.JwtExpiresAt
SigningMethod = config.AppSetting.SigningMethod
)
type Claims struct {
Appkey string `json:"app_key"`
AppSecret string `json:"app_secret"`
jwt.StandardClaims
}
func GenerateToken(appkey, app_secret string) (string, error) {
nowTime := time.Now()
expireTime := nowTime.Add(JwtExpiresAt)
claims := Claims{
appkey, // Consider encryption
app_secret, // Consider encryption
jwt.StandardClaims{
ExpiresAt: expireTime.Unix(),
Issuer: "go-api",
},
}
tokenClaims := jwt.NewWithClaims(jwt.GetSigningMethod(SigningMethod), claims)
token, err := tokenClaims.SignedString(JwtSecret)
return token, err
}
func ParseToken(token string) (*Claims, error) {
tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return JwtSecret, nil
})
iftokenClaims ! =nil {
if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
return claims, nil}}return nil, err
}
Copy the code
3.2. Custom middleware
In the app/middleware
package middleware
import (
"net/http"
jwt "github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"go-api/tool"
)
func JWT(a) gin.HandlerFunc {
return func(c *gin.Context) {
var (
code = tool.SUCCESS
token string
)
// Supports header and GET
if s, ok := c.GetQuery("token"); ok {
token = s
} else {
token = c.GetHeader("token")}if token == "" {
code = tool.ERROR_AUTH_CHECK_TOKEN_EMPTY
} else {
_, err := tool.ParseToken(token)
iferr ! =nil {
switch err.(*jwt.ValidationError).Errors {
case jwt.ValidationErrorExpired:
code = tool.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
default:
code = tool.ERROR_AUTH_CHECK_TOKEN_FAIL
}
}
}
ifcode ! = tool.SUCCESS { c.JSON(http.StatusUnauthorized, gin.H{"code": code,
"msg": tool.GetMsg(code),
"data": nil,
})
c.Abort()
return} c.ext ()}} ·· ###3.2Routing groups apply JWT middleware` ``golang package routes import ( "go-api/app/controller" "go-api/app/middleware" "go-api/tool" "github.com/gin-gonic/gin" ) func apiRoute(r *gin.Engine) { r.GET("/auth/:key", func(c *gin.Context) { appkey := c.Param("key") token, err := tool.GenerateToken(appkey, "12312323") if err ! = nil {tool.JSONP(c, tool.ERROR_AUTH_TOKEN, "", nil) return} tool.JSONP(c, 0, "success ", Token)}) apiv1 := r.group ("/ API /v1", middleware.jwt ()) {// GET("/users", Controller.getusers) // GET user apiv1.GET("/user/: ID ", controller.getuser) // Add user apiv1.POST("/users", Controller.adduser) // Update user apiv1.PUT("/users/:id", controller.edituser) // DELETE user apiv1.DELETE("/users/:id", controller.DeleteUser) } }Copy the code
Finally, compile the code for validation or unit testing
4. Series of articles
- Serialized a Golang environment build
- Serial II installation Gin
- Serial three defines the directory structure
- Serial four builds case API1
- Serial five builds case API2
- Serial six access Swagger interface document
- Serial seven Log components
- Serial eight gracefully restarts and stops
- Serial Makefile build
- Serial Cron timing mission
- Serial build command line tools
- Create a dedicated Cache(First Day)
- Create your own Cache in 3 days (Second Day)
- Create a dedicated Cache(Third Day)