go get -u github.com/gin-gonic/gin
func main() {
// 使用默认中间件创建一个gin路由器
// Default默认开启logger and recovery (crash-free) 中间件
实例化一个gin的server对象
router := gin.Default()
//r := gin.New() New()就不会新建中间件
//restful开发中使用
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
}
post创建数据,非幂等
put 更新数据,幂等
patch 局部更新数据,非幂等
此外,还有一个可以匹配所有请求方法的Any
方法如下:
r.Any("/test", func(c *gin.Context) {...})
为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码,下面的代码为没有匹配到路由的请求都返回views/404.html
页面。
r.NoRoute(func(c *gin.Context) {
c.HTML(http.StatusNotFound, "views/404.html", nil)
})
package main
import "github.com/gin-gonic/gin"
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(":8082")
}
func readEndpoint(context *gin.Context) {
}
func submitEndpoint(context *gin.Context) {
}
func loginEndpoint(context *gin.Context) {
}
也称为path参数获取
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
//通过:来传递
//通过c.Param来获取
r.GET("/user/:name/:action/", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.JSON(http.StatusOK,gin.H{
"id":id,
"action":action,
})
})
//*会把/user/:name后包括/的所有字符都接收,慎用
r.GET("/user/:name/*action", func(c *gin.Context) {
name := c.Param("name")
action := c.Param("action")
c.String(http.StatusOK, "%s is %s", name, action)
})
r.Run(":8082")
}
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")
}
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
router.GET("/welcome", welcome)
router.POST("/form_post", formPost)
router.POST("/post", getPost)
router.Run(":8083")
}
//get请求
//data := c.DefaultQuery()会设置默认值
//data := c.Query()取不到返回空
//data,err := c.GetQuery()取不到会报错
func welcome(c *gin.Context) {
//使用带默认值的获取参数
firstName := c.DefaultQuery("firstname", "james")
//不使用默认值的取值
lastName := c.Query("lastname")
c.JSON(http.StatusOK, gin.H{
"first_name": firstName,
"last_name": lastName,
})
}
/*
http://localhost:8083/welcome
{
"first_name": "james",
"last_name": ""
}
http://localhost:8083/welcome?firstname=tom&lastname=jerry
{
"first_name": "tom",
"last_name": "jerry"
}
*/
//post请求
//c.DefaultPostForm()取不到值时会返回指定的默认值
//c.PostForm()取不到值时会返回空
func formPost(c *gin.Context) {
message := c.PostForm("message")
nick := c.DefaultPostForm("nick", "anonymous")
c.JSON(http.StatusOK, gin.H{
"message": message,
"nick": nick,
})
}
/*
localhost:8083/form_post
{
"message": "",
"nick": "anonymous"
}
localhost:8083/form_post
message nihao
nick james
{
"message": "nihao",
"nick": "james"
}
*/
//get,post混合获取 通过post来获取
func getPost(c *gin.Context) {
id := c.Query("id")
page := c.DefaultQuery("page", "0")
name := c.PostForm("name")
message := c.DefaultPostForm("message", "mesg")
c.JSON(http.StatusOK, gin.H{
"id": id,
"page": page,
"name": name,
"message": message,
})
}
/*
localhost:8083/post?id=1&page=2
message nihao
name james
{
"id": "1",
"message": "nihao",
"name": "james",
"page": "2"
}
*/
package main
import (
"net/http"
"github.com/gin-gonic/gin"
"GoStart/gin_start/ch06/proto"
)
func main() {
router := gin.Default()
router.GET("/moreJSON", moreJSON)
router.GET("/someProtoBuf", returnProto)
router.Run(":8083")
}
//返回json格式
func moreJSON(c *gin.Context) {
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "james"
msg.Message = "this is a test"
msg.Number = 20
c.JSON(http.StatusOK, msg)
}
/*
localhost:8083/moreJSON
{
"user": "james",
"Message": "this is a test",
"Number": 20
}
*/
//返回ProtoBuf
func returnProto(c *gin.Context) {
course := []string{"python", "go", "weifuwu"}
user := &proto.Teacher{
Name: "james",
Course: course,
}
c.ProtoBuf(http.StatusOK, user)
}
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/mapjson", func(c *gin.Context) {
//方法1:使用map
data := map[string]interface{}{
"name": "james",
"age": 7,
"message": "mapjson",
}
c.JSON(http.StatusOK, data)
})
r.GET("/structjson", func(c *gin.Context) {
//方法2:使用结构体
//注意结构体字段首字母要大写,如果要小写可以通过tag来实现
type msg struct {
Name string `json:"name"`
Age int `json:"age"`
Id string
Message string
}
data := msg{
"james",
18,
"007",
"structjson",
}
c.JSON(http.StatusOK, data)
//返回值
//{
// "name": "james",
// "age": 18,
// "Id": "007",
// "Message": "structjson"
//}
})
r.GET("/ginHjson", func(c *gin.Context) {
//方法3:gin.H约等于map的用法
data := gin.H{
"name": "james",
"age": 7,
"message": "ginHjson",
}
c.JSON(http.StatusOK, data)
})
r.Run(":9090")
}
r.POST("/json", func(c *gin.Context) {
// 注意:下面为了举例子方便,暂时忽略了错误处理
b, _ := c.GetRawData() // 从c.Request.Body读取请求数据
// 定义map或结构体
var m map[string]interface{}
// 反序列化
_ = json.Unmarshal(b, &m)
c.JSON(http.StatusOK, m)
})
ShouldBind
会按照下面的顺序解析请求中的数据完成绑定:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
//登录表单验证
type LoginForm struct {
//required表示必填字段,
//binding:"required,min=3,max=10"`如果有多个绑定信息,用,隔开
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
//注册表单验证
type SignUpForm struct {
Age uint8 `json:"age" binding:"gte=1,lte=130"`
Name string `json:"name" binding:"required,min=3"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
RePassword string `json:"repassword" binding:"required,eqfield=Password"` //跨字段验证
}
func main() {
router := gin.Default()
router.POST("/loginJSON", func(c *gin.Context) {
var loginForm LoginForm
if err := c.ShouldBind(&loginForm); 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",
})
})
/*
{
"error": "Key: 'LoginForm.Password' Error:Field validation for 'Password' failed on the 'required' tag"
}
*/
router.POST("/signup", func(c *gin.Context) {
var signUpForm SignUpForm
if err := c.ShouldBind(&signUpForm); err != nil {
fmt.Println(err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "sign up success",
})
})
/*
{
"age":18,
"name":"james",
"email":"1111@qq.com",
"password":"111",
"repassword":"111"
}
{
"msg": "sign up success"
}
*/
router.Run(":8083")
}
package main
import (
"fmt"
"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"
enTranslations "github.com/go-playground/validator/v10/translations/en"
zhTranslations "github.com/go-playground/validator/v10/translations/zh"
"net/http"
)
var trans ut.Translator
//登录表单验证
type LoginForm struct {
//required表示必填字段,
//binding:"required,min=3,max=10"`如果有多个绑定信息,用,隔开
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
//注册表单验证
type SignUpForm struct {
Age uint8 `json:"age" binding:"gte=1,lte=130"`
Name string `json:"name" binding:"required,min=3"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required"`
RePassword string `json:"repassword" binding:"required,eqfield=Password"` //跨字段验证
}
//修改中文翻译器
func InitTrans(local string) (err error) {
//修改gin框架中的validator引擎属性,实现定制
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
zhT := zh.New() // 中文翻译器
enT := en.New() // 英文翻译器
// 第一个参数是备用参数,后面的参数是需要支持的语言环境
uni := ut.New(enT, zhT)
trans, ok = uni.GetTranslator(local)
if !ok {
return fmt.Errorf("uni.GetTranslator(%s)", local)
}
// 注册翻译器
switch local {
case "en":
err = enTranslations.RegisterDefaultTranslations(v, trans)
case "zh":
err = zhTranslations.RegisterDefaultTranslations(v, trans)
default:
err = enTranslations.RegisterDefaultTranslations(v, trans)
}
return
}
return
}
func main() {
if err := InitTrans("zh"); err != nil {
fmt.Println("init translator error")
return
}
router := gin.Default()
router.POST("/loginJSON", func(c *gin.Context) {
var loginForm LoginForm
if err := c.ShouldBind(&loginForm); err != nil {
errs, ok := err.(validator.ValidationErrors)
if !ok {
c.JSON(http.StatusOK, gin.H{
"msg": err.Error(),
})
}
c.JSON(http.StatusBadRequest, gin.H{
"error": errs.Translate(trans),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "login success",
})
})
/*
{
"error": "Key: 'LoginForm.Password' Error:Field validation for 'Password' failed on the 'required' tag"
}
*/
/*
翻译为中文后
{
"error": {
"LoginForm.User": "User为必填字段"
}
}
*/
router.POST("/signup", func(c *gin.Context) {
var signUpForm SignUpForm
if err := c.ShouldBind(&signUpForm); err != nil {
fmt.Println(err.Error())
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"msg": "sign up success",
})
})
/*
{
"age":18,
"name":"james",
"email":"1111@qq.com",
"password":"111",
"repassword":"111"
}
{
"msg": "sign up success"
}
*/
router.Run(":8083")
}
Gin中的中间件必须是一个gin.HandlerFunc
类型。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
//自定义中间件
func MyLogger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
c.Set("example", "123456")
//让原本该执行的逻辑继续执行
c.Next()
end := time.Since(t)
fmt.Printf("use time:%V\n", end)
status := c.Writer.Status()
fmt.Println("status:", status)
}
}
func main() {
gin.Default默认使用Logger()和Recovery()中间件
定义一个默认的无中间件的路由
//router := gin.New()
配置全局的中间件
使用logger中间件,打印日志的作用
//router.Use(gin.Logger())
使用recovery中间件,出现panic的时候,会自动recover掉
//router.Use(gin.Recovery())
也可以写在一起
router.Use(gin.Logger(),gin.Recovery())
//
只有Group的/goods可以使用这个中间件
//authrized := router.Group("/goods")
//authrized.Use(AuthRequired)
router := gin.Default()
router.Use(MyLogger())
router.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
router.Run(":8083")
}
func AuthRequired(context *gin.Context) {
}
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
"time"
)
func indexHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "index",
})
fmt.Println("index")
}
//中间件可以直接用参数为c *gin.Context的函数实现
func baseFunc(c *gin.Context) {
fmt.Println("baseFunc in")
name, _ := c.Get("name")
fmt.Printf("get name:%s\n", name)
c.Next()
fmt.Println("baseFunc out")
}
//通过闭包实现中间件,实际开发中这样用
func countTime() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("countTime in")
start := time.Now()
c.Set("name", "james") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
// 调用该请求的剩余处理程序
c.Next()
// 不调用该请求的剩余处理程序,直接返回,相当于函数中的return
// c.Abort()
// 计算耗时
cost := time.Since(start)
log.Printf("cost time:%d\n", cost)
fmt.Println("countTime out")
}
}
func main() {
r := gin.Default()
//全局注册中间件
r.Use(countTime())
//r.GET("/index", indexHandler)
//给单独的router注册中间件
r.GET("/index", baseFunc, countTime(), indexHandler)
r.Run(":9090")
}
为路由组注册中间件有以下两种写法。
写法1:
shopGroup := r.Group("/shop", countTime())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
写法2:
shopGroup := r.Group("/shop")
shopGroup.Use(countTime())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
当在中间件或handler
中启动新的goroutine
时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy()
)。
c.Next()会调用一个index++
中间件会全部加入一个队列,前面的中间件函数执行结束后,返回自己,而队列后面的中间件还会继续执行
所以如果想要中间件函数执行就结束,需要使用c.Abort(),这个函数会将index指向最后,使整个流程终止.
中间件中的return无法阻止后续逻辑的执行,而c.Abort()可以
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"os"
"os/signal"
"syscall"
)
func main() {
//优雅退出
//微服务,在启动前后会将当前服务的ip地址和端口号注册到注册中心
//当前服务停止后,并没有告诉注册中心下线服务,
router := gin.Default()
router.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"msg": "pong",
})
})
go func() {
router.Run(":8083")
}()
//接收到关闭信号 kill -9 强杀命令
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
//处理后续逻辑
fmt.Println("closing server")
fmt.Println("logout server")
}
go-micro
kratos
rpcx
HTTP 重定向很容易。 内部、外部重定向均支持。
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})
路由重定向,使用HandleContext
:
r.GET("/test", func(c *gin.Context) {
// 指定重定向的URL
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"hello": "world"})
})