快速入门
第一个demo
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"msg": "pong",
})
})
r.Run(":8777")
}
使用get、post、put等http方法
func main() {
// 使用默认中间件创建一个gin路由器
// logger and recovery (crash-free) 中间件
router := gin.Default()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
// 默认启动的是 8080端口,也可以自己定义启动端口
router.Run()
// router.Run(":3000") for a hard coded port
}
URL和路由分组
路由分组
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8777")
}
带参数的url
func main() {
r := gin.Default()
goodsGroup := r.Group("/goods")
{
goodsGroup.GET("", goodsList)
goodsGroup.GET("/:id/:action", goodsDetail)
//goodsGroup.GET("/:id/*action", goodsDetail)
goodsGroup.POST("", createGoods)
}
r.Run(":8777")
}
func goodsList(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"name": "list",
})
}
func goodsDetail(c *gin.Context) {
id := c.Param("id")
action := c.Param("action")
c.JSON(http.StatusOK, gin.H{
"id": id,
"action": action,
})
}
func createGoods(c *gin.Context) {
}
获取路由分组的参数
type Person struct {
ID int `uri:"id" binding:"required"`
Name string `uri:"name" binding:"required"`
}
func main() {
r := gin.Default()
r.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,
})
})
r.Run(":8777")
}
获取参数
获取get /post参数
func main() {
r := gin.Default()
r.GET("/welcome", welcome)
r.POST("/formPost", formPost)
r.Run(":8777")
}
func welcome(c *gin.Context) {
firstname := c.DefaultQuery("first", "lcd")
lastname := c.DefaultQuery("last", "qq")
c.JSON(http.StatusOK, gin.H{
"f": firstname,
"l": lastname,
})
}
func formPost(c *gin.Context) {
msg := c.PostForm("msg")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(http.StatusOK, gin.H{
"msg": msg,
"nick": nick,
})
}
JSON、ProtoBuf 渲染
输出json和protobuf
syntax = "proto3";
option go_package = ".;proto";
message Teacher {
string name = 1;
repeated string course = 2;
}
func main() {
r := gin.Default()
// gin.H is a shortcut for map[string]interface{}
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/moreJSON", func(c *gin.Context) {
// You also can use a struct
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// Note that msg.Name becomes "user" in the JSON
// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, msg)
})
r.GET("/someProtoBuf", func(c *gin.Context) {
courses := []string{"python", "django", "go"}
// The specific definition of protobuf is written in the testdata/protoexample file.
data := &proto.Teacher{
Name: "lll",
Course: courses,
}
// Note that data becomes binary data in the response
// Will output protoexample.Test protobuf serialized data
c.ProtoBuf(http.StatusOK, data)
})
r.Run(":8777")
}
pureJSON
通常情况下,JSON会将特殊的HTML字符替换为对应的unicode字符,比如<替换为\u003c,如果想原样输出html,则使用PureJSON
func main() {
r := gin.Default()
// Serves unicode entities
r.GET("/json", func(c *gin.Context) {
c.JSON(200, gin.H{
"html": "<b>Hello, world!</b>",
})
})
// Serves literal characters
r.GET("/purejson", func(c *gin.Context) {
c.PureJSON(200, gin.H{
"html": "<b>Hello, world!</b>",
})
})
r.Run(":8777")
}
表单验证
表单的基本验证(登录注册)
Gin使用 go-playground/validator 验证参数,查看完整文档。测试
package main
import (
"fmt"
"net/http"
"reflect"
"strings"
"github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator"
en_translations "github.com/go-playground/validator/v10/translations/en"
zh_translations "github.com/go-playground/validator/v10/translations/zh"
"github.com/go-playground/locales/zh"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
"github.com/gin-gonic/gin"
)
var trans ut.Translator
type Login struct {
User string ` json:"user" binding:"required,min=3" `
Password string ` json:"password" binding:"required"`
}
type SignUp struct {
Age uint8 `json:"age" binding:"gte=1,lte=130"`
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
RePassword string `json:"re_password" 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
}
func InitTrans(locale string) (err error) {
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterTagNameFunc(func(field reflect.StructField) string {
name := strings.SplitN(field.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
}
return
}
func main() {
if err := InitTrans("zh"); err != nil {
fmt.Println("初始化翻译器错误")
return
}
r := gin.Default()
r.POST("/loginJSON", func(c *gin.Context) {
var login Login
if err := c.ShouldBind(&login); err != nil {
fmt.Println(err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "login success",
})
})
r.POST("/signUp", func(c *gin.Context) {
var signUp SignUp
if err := c.ShouldBind(&signUp); err != nil {
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
return
}
c.JSON(http.StatusBadRequest, gin.H{
"error": RemoveTopStruct(errs.Translate(trans)),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "signUp success",
})
})
r.Run(":8777")
}
import requests
#login
resp = requests.post("http://127.0.0.1:8777/loginJSON", json={
"user": "xdd",
"password": "1233"
})
print(resp.text)
import requests
#signUp
resp = requests.post("http://127.0.0.1:8777/signUp", json={
"age": 18,
"name": "xdd",
"email": "12@qq.com",
"password": "aaa",
"re_password": "aaa",
})
print(resp.text)
中间件和next函数
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。默认启动
// 默认启动方式,包含 Logger、Recovery 中间件
r := gin.Default()
使用中间件
func main() {
// 创建一个不包含中间件的路由器
r := gin.New()
// 全局中间件
// 使用 Logger 中间件
r.Use(gin.Logger())
// 使用 Recovery 中间件
r.Use(gin.Recovery())
// 路由添加中间件,可以添加任意多个
r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
// 路由组中添加中间件
// authorized := r.Group("/", AuthRequired())
// exactly the same as:
authorized := r.Group("/")
// per group middleware! in this case we use the custom created
// AuthRequired() middleware just in the "authorized" group.
authorized.Use(AuthRequired())
{
authorized.POST("/login", loginEndpoint)
authorized.POST("/submit", submitEndpoint)
authorized.POST("/read", readEndpoint)
// nested group
testing := authorized.Group("testing")
testing.GET("/analytics", analyticsEndpoint)
}
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
自定义组件
func main() {
r := gin.New()
r.Use(Logger())
r.GET("/test", func(c *gin.Context) {
example := c.MustGet("example").(string)
log.Println(example)
})
r.Run(":8777")
}
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
c.Set("example", "123")
c.Next()
end := time.Since(t)
log.Println(end)
status := c.Writer.Status()
log.Println(status)
}
}
通过c.abort()终止中间件后续逻辑的执行