GIN
GIN 是一个高性能,简单易用的轻量级 WEB 框架
快速尝试
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func pong ( c * gin. Context) {
c. JSON ( http. StatusOK, gin. H{
"message" : "pong" ,
} )
}
func main ( ) {
r := gin. Default ( )
r. GET ( "/ping" , pong)
r. Run ( ":8080" )
}
路由分组
对于 WEB 开发来讲,不同的接口需要被不同的模块调用,而我们一般把URL 路径的前缀作为区分模块的基础,例如:
localhost:8080/goods/list 和 localhost:8080/goods/detail 就同属于一个模块,而我们每次配置 GIN 时都需要写它的路径,这样无疑增加了我们的工作量,我们可以通过路由分组,来让相同模块的 URL 分到一起并设置一个共有前缀,进而解决这个问题
示例:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main ( ) {
router := gin. Default ( )
goodsGroup := router. Group ( "/goods" )
{
goodsGroup. GET ( "/list" , goodsList)
goodsGroup. GET ( "/1" , goodsDetail)
goodsGroup. POST ( "/add" )
}
router. Run ( ":8080" )
}
func goodsDetail ( context * gin. Context) {
}
func goodsList ( context * gin. Context) {
context. JSON ( http. StatusAccepted, gin. H{
"message" : "WIN" ,
} )
}
如果我们有动态的变量需要传入:
使用冒号进行区分
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main ( ) {
router := gin. Default ( )
goodsGroup := router. Group ( "/goods" )
{
goodsGroup. GET ( "/list" , goodsList)
goodsGroup. GET ( "/:id/:action" , goodsDetail)
goodsGroup. POST ( "/add" )
}
router. Run ( ":8080" )
}
func goodsDetail ( context * gin. Context) {
id := context. Param ( "id" )
action := context. Param ( "action" )
context. JSON ( http. StatusOK, gin. H{
"id" : id,
"action" : action,
"message" : "OK" ,
} )
}
func goodsList ( context * gin. Context) {
context. JSON ( http. StatusOK, gin. H{
"message" : "WIN" ,
} )
}
但是,我们还需要对传入的参数进行控制,对参数的类型进行约束
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
type Person struct {
ID int `uri:"id" binding:"required"`
Name string `uri:"name" binding:"required"`
}
func main ( ) {
router := gin. Default ( )
router. GET ( "/:name/:id" , func ( c * gin. Context) {
var person Person
if err := c. ShouldBindUri ( & person) ; err != nil {
c. Status ( 404 )
return
}
c. JSON ( http. StatusOK, gin. H{
"name" : person. Name,
"id" : person. ID,
} )
} )
router. Run ( ":8083" )
}
参数获取
我们很一般会分为从 GET 和 POST 类型的请求中获取参数:
另外我们也有同时从 url 路径中以及 body 中获取参数的情况,这个时候就需要我们使用 POST 请求
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main ( ) {
router := gin. Default ( )
router. GET ( "/getname" , GetName)
router. POST ( "/getname" , PostGetName)
router. POST ( "bothgetname" , GetPostname)
router. Run ( ":8083" )
}
func GetPostname ( context * gin. Context) {
id := context. Query ( "id" )
page := context. DefaultQuery ( "page" , "0" )
message := context. PostForm ( "message" )
nick := context. DefaultPostForm ( "nick" , "anonymous" )
context. JSON ( http. StatusOK, gin. H{
"id" : id,
"page" : page,
"message" : message,
"nick" : nick,
} )
}
func PostGetName ( context * gin. Context) {
message := context. PostForm ( "message" )
nick := context. DefaultPostForm ( "nick" , "anonymous" )
context. JSON ( http. StatusOK, gin. H{
"message" : message,
"nick" : nick,
} )
}
func GetName ( context * gin. Context) {
firstname := context. DefaultQuery ( "firstname" , "Guest" )
lastname := context. DefaultQuery ( "lastname" , "Guest" )
context. JSON ( http. StatusOK, gin. H{
"first_name" : firstname,
"last_name" : lastname,
} )
}
表单验证
GIN 引入了 validate 开源项目进行表单验证:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
type LoginForm struct {
User string `form:"user" binding:"required,min=3,max=10"`
Password string `form:"password" binding:"required"`
}
type SignForm struct {
Age uint8 `json:"age" binding:"required,gte=1,lte=130"`
Name string `json:"name" binding:"required,min=2"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6,max=20"`
RePassword string `json:"repassword" binding:"required,eqfield=Password"`
}
func main ( ) {
router := gin. Default ( )
router. POST ( "/loginJSON" , func ( context * gin. Context) {
var loginForm LoginForm
if err := context. ShouldBind ( & loginForm) ; err != nil {
fmt. Println ( err. Error ( ) )
context. JSON ( http. StatusBadRequest, gin. H{
"error" : err. Error ( ) ,
} )
return
}
context. JSON ( http. StatusOK, gin. H{
"status" : "验证成功" ,
} )
} )
router. POST ( "/signUp" , func ( context * gin. Context) {
var signForm SignForm
if err := context. ShouldBind ( & signForm) ; err != nil {
context. JSON ( http. StatusBadRequest, gin. H{
"error" : err. Error ( ) ,
} )
return
}
context. JSON ( http. StatusOK, gin. H{
"message" : "成功!!!!!" ,
} )
} )
router. Run ( ":8083" )
}
错误信息配置成中文:
package main
import (
"fmt"
"net/http"
"reflect"
"strings"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
)
type LoginForm struct {
User string `form:"user" binding:"required,min=3,max=10"`
Password string `form:"password" binding:"required"`
}
type SignForm struct {
Age uint8 `json:"age" binding:"required,gte=1,lte=130"`
Name string `json:"name" binding:"required,min=2"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6,max=20"`
RePassword string `json:"repassword" binding:"required,eqfield=Password"`
}
func removeTopStruct ( fields map [ string ] string ) map [ string ] string {
rsp := map [ string ] string { }
for field, err := range fields {
rsp[ field[ strings. Index ( field, "." ) + 1 : ] ] = err
}
return rsp
}
var trans ut. Translator
func InitTrans ( locale string ) ( err error ) {
if v, ok := binding. Validator. Engine ( ) . ( * validator. Validate) ; ok {
v. RegisterTagNameFunc ( func ( fld reflect. StructField) string {
name := strings. SplitN ( fld. Tag. Get ( "json" ) , "," , 2 ) [ 0 ]
if name == "-" {
return ""
}
return name
} )
zhT := zh. New ( )
enT := en. New ( )
uni := ut. New ( enT, zhT, enT)
trans, ok = uni. GetTranslator ( locale)
if ! ok {
return fmt. Errorf ( "uni.GetTranslator(%s)" , locale)
}
switch locale {
case "en" :
en_translations. RegisterDefaultTranslations ( v, trans)
case "zh" :
zh_translations. RegisterDefaultTranslations ( v, trans)
default :
en_translations. RegisterDefaultTranslations ( v, trans)
}
}
return
}
func main ( ) {
if err := InitTrans ( "zh" ) ; err != nil {
fmt. Println ( "初始化翻译器错误" )
fmt. Println ( err)
return
}
router := gin. Default ( )
router. POST ( "/loginJSON" , func ( context * gin. Context) {
var loginForm LoginForm
if err := context. ShouldBind ( & loginForm) ; err != nil {
errs, ok := err. ( validator. ValidationErrors)
if ! ok {
context. JSON ( http. StatusOK, gin. H{
"msg" : err. Error ( ) ,
} )
return
}
fmt. Println ( err. Error ( ) )
context. JSON ( http. StatusBadRequest, gin. H{
"error" : removeTopStruct ( errs. Translate ( trans) ) ,
} )
return
}
context. JSON ( http. StatusOK, gin. H{
"status" : "验证成功" ,
} )
} )
router. POST ( "/signUp" , func ( context * gin. Context) {
var signForm SignForm
if err := context. ShouldBind ( & signForm) ; err != nil {
context. JSON ( http. StatusBadRequest, gin. H{
"error" : err. Error ( ) ,
} )
return
}
context. JSON ( http. StatusOK, gin. H{
"message" : "成功!!!!!" ,
} )
} )
router. Run ( ":8083" )
}
将配置翻译成中文的具体步骤:
配置如下函数与全局变量:
func removeTopStruct ( fields map [ string ] string ) map [ string ] string {
rsp := map [ string ] string { }
for field, err := range fields {
rsp[ field[ strings. Index ( field, "." ) + 1 : ] ] = err
}
return rsp
}
0var trans ut. Translator
func InitTrans ( locale string ) ( err error ) {
if v, ok := binding. Validator. Engine ( ) . ( * validator. Validate) ; ok {
v. RegisterTagNameFunc ( func ( fld reflect. StructField) string {
name := strings. SplitN ( fld. Tag. Get ( "json" ) , "," , 2 ) [ 0 ]
if name == "-" {
return ""
}
return name
} )
zhT := zh. New ( )
enT := en. New ( )
uni := ut. New ( enT, zhT, enT)
trans, ok = uni. GetTranslator ( locale)
if ! ok {
return fmt. Errorf ( "uni.GetTranslator(%s)" , locale)
}
switch locale {
case "en" :
en_translations. RegisterDefaultTranslations ( v, trans)
case "zh" :
zh_translations. RegisterDefaultTranslations ( v, trans)
default :
en_translations. RegisterDefaultTranslations ( v, trans)
}
}
return
}
其中,要注意可能需要添加的包:
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
在 main 函数的最一开始配置 翻译器的初始化:
if err := InitTrans ( "zh" ) ; err != nil {
fmt. Println ( "初始化翻译器错误" )
fmt. Println ( err)
return
}
在发生错误的地方进行配置:
if err := context. ShouldBind ( & loginForm) ; err != nil {
errs, ok := err. ( validator. ValidationErrors)
if ! ok {
context. JSON ( http. StatusOK, gin. H{
"msg" : err. Error ( ) ,
} )
return
}
fmt. Println ( err. Error ( ) )
context. JSON ( http. StatusBadRequest, gin. H{
"error" : removeTo