提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 1、get和post方法
- 2、重定向
- 3、获取第三方数据
- 4、多形式渲染
- 5、文件服务器
- 6、单文件上传
- 7、多文件上传
- 8、自定义中间件
- 9、登录中间件
- 10、同步异步
- 11、多服务器程序运行
- 12、路由组
- 13、Gin框架bind
- 14、调用restful接口
- 15、参数校验1-tag验证
- 16、参数校验2-嵌套结构体验证
- 17、swagger
- 18、Gin框架cookie
- 19、Gin框架Session
- 总结
前言
gin框架的基本使用,把各个知识点拆分写成demo。
1、get和post方法
最简单的获取参数的方法。我还知道json映射,后续补充。
package mainimport ("fmt""github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/hi", getMsg)r.POST("/hi", postMsg)r.Run(":9090")
}func getMsg(c *gin.Context) {name := c.Query("name")// c.String(http.StatusOK, "欢迎:%s",name)c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + name,})
}func postMsg(c *gin.Context) {name := c.DefaultPostForm("name", "lisi")fmt.Println(name)form, b := c.GetPostForm("name")fmt.Println(form, b)
}
2、重定向
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()// 第一种应该是在某个版本被废弃了/*r.GET("/hi", func(c *gin.Context) {c.Redirect(http.StatpusMovedPermanently, "http://www.baidu.com")})*/r.GET("/hi", getMsg)r.GET("/hello", func(c *gin.Context) {c.Request.URL.Path = "/hi"r.HandleContext(c)})r.Run(":9090")
}func getMsg(c *gin.Context) {name := c.Query("name")// c.String(http.StatusOK, "欢迎:%s",name)c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + name,})
}
3、获取第三方数据
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/getThird", func(c *gin.Context) {url := "http://www.baidu.com"resp, err := http.Get(url)if err != nil || resp.StatusCode != http.StatusOK {c.Status(http.StatusServiceUnavailable)return}body := resp.BodycontentLength := resp.ContentLengthcontentType := resp.Header.Get("Content-Type")c.DataFromReader(http.StatusOK, contentLength, contentType, body, nil)})r.Run(":9090")
}
4、多形式渲染
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/getJson", func(c *gin.Context) {// JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003cc.JSON(http.StatusOK, gin.H{"html": "<H1>hi</H1>",})})r.GET("/getHTML", func(c *gin.Context) {// 如果要正常返回HTML字符,用PureJSONc.PureJSON(http.StatusOK, gin.H{"html": "<H1>hi</H1>",})})r.GET("/getXML", func(c *gin.Context) {type Message struct {Name stringMsg stringAge int}info := Message{Name: "zhangsan",Msg: "hello",Age: 20,}c.XML(http.StatusOK, info)})r.GET("/getYAML", func(c *gin.Context) {// 会下在一个yaml文件c.YAML(http.StatusOK, gin.H{"msg": "hello",})})r.Run(":9090")
}
5、文件服务器
package mainimport ("github.com/gin-gonic/gin"
)func main() {r := gin.Default()r.GET("/file", func(c *gin.Context) {path := "/home/ld/workspace/study/gin-learn/2.4_file/" // 最后一个/一定要加上,否则拼接路径就不对了fileName := path + c.Query("name")// JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003cc.File(fileName)})r.Run(":9090")
}
6、单文件上传
package mainimport ("fmt""github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.POST("/uploadOne", func(c *gin.Context) {file, err := c.FormFile("fileName")if err != nil {fmt.Println(err.Error())c.String(http.StatusBadRequest, "文件上传错误")}dst := "/home/ld/workspace/study/gin-learn/2.5_file_upload_one/"c.SaveUploadedFile(file, dst+file.Filename)c.String(http.StatusOK, "保存成功:%s", file.Filename)})r.Run(":9090")
}
7、多文件上传
package mainimport ("fmt""github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.POST("/uploadMoreThanOne", func(c *gin.Context) {form, err := c.MultipartForm()if err != nil {fmt.Println(err.Error())c.String(http.StatusBadRequest, "文件上传错误")}files := form.File["file_key"]dst := "/home/ld/workspace/study/gin-learn/2.6_file_upload_more_than_one/"for _, file := range files {c.SaveUploadedFile(file, dst+file.Filename)}c.String(http.StatusOK, "%d个文件保存成功", len(files))})r.Run(":9090")
}
8、自定义中间件
package mainimport ("github.com/gin-gonic/gin""net/http""strconv"
)func main() {r := gin.Default()r.Use(Middleware())r.GET("/middle", func(c *gin.Context) {c.String(http.StatusOK, "年龄验证成功,年龄为:%s", c.Query("age"))})r.Run(":9090")
}func Middleware() gin.HandlerFunc {return func(c *gin.Context) {ageStr := c.Query("age")age, err := strconv.Atoi(ageStr)if err != nil || age < 18 {c.AbortWithStatusJSON(http.StatusBadRequest, "年龄异常")return}c.Next()}
}
9、登录中间件
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.Use(AuthMiddleware())r.GET("/middle", func(c *gin.Context) {c.String(http.StatusOK, "身份验证成功,用户为:%s", c.MustGet(gin.AuthUserKey))})r.Run(":9090")
}func AuthMiddleware() gin.HandlerFunc {// 静态添加accounts := gin.Accounts{"admin": "admin","user": "user",}// 动态添加accounts["boss"] = "boss"auth := gin.BasicAuth(accounts)return auth
}
10、同步异步
package mainimport ("github.com/gin-gonic/gin""net/http""time"
)func main() {r := gin.Default()r.GET("/sync", func(c *gin.Context) {sync(c)c.String(http.StatusOK, "同步任务结束")})r.GET("/async", func(c *gin.Context) {for i := 0; i < 6; i++ {go async(c)}c.String(http.StatusOK, "异步任务结束")})r.Run(":9090")
}func async(c *gin.Context) {println("开始同步任务")time.Sleep(3 * time.Second)println("同步任务结束")
}func sync(c *gin.Context) {println("开始同步任务")time.Sleep(3 * time.Second)println("同步任务结束")
}
11、多服务器程序运行
package mainimport ("fmt""github.com/gin-gonic/gin""golang.org/x/sync/errgroup""net/http""time"
)var g errgroup.Groupfunc main() {server01 := &http.Server{Addr: ":9091",Handler: router01(),ReadTimeout: 5 * time.Second,WriteTimeout: 10 * time.Second,}server02 := &http.Server{Addr: ":9092",Handler: router02(),ReadTimeout: 5 * time.Second,WriteTimeout: 10 * time.Second,}g.Go(func() error {return server01.ListenAndServe()})g.Go(func() error {return server02.ListenAndServe()})if err := g.Wait(); err != nil {fmt.Println("Error: ", err)}
}
func router01() http.Handler {r := gin.Default()r.GET("/router01", func(c *gin.Context) {c.String(http.StatusOK, "router01")})return r
}
func router02() http.Handler {r := gin.Default()r.GET("/router02", func(c *gin.Context) {c.String(http.StatusOK, "router02")})return r
}
12、路由组
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()group1 := r.Group("/group1"){group1.GET("/hi", getMsg)group1.GET("/hello", getMsg)}group2 := r.Group("/group2")group2.GET("/hi", getMsg)r.Run(":9090")
}func getMsg(c *gin.Context) {name := c.Query("name")// c.String(http.StatusOK, "欢迎:%s",name)c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + name,})
}
13、Gin框架bind
package mainimport ("github.com/gin-gonic/gin""net/http"
)// User 大写
type User struct {Name string `json:"name" binding:"required"`Password string `json:"password"`
}func main() {r := gin.Default()r.POST("/hi", getMsg)r.Run(":9090")
}func getMsg(c *gin.Context) {var user User// 此处传指针err := c.Bind(&user)if err != nil {c.JSON(http.StatusBadRequest, err.Error())return}c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + user.Name,})
}
14、调用restful接口
package mainimport ("bytes""encoding/json""fmt""io/ioutil""net/http""time"
)// User 大写
type User struct {Name string `json:"name" binding:"required"`Password string `json:"password"`
}type TempData struct {// Code string `json:"code"` // 不会给这个值反序列化Msg string `json:"msg"`
}func main() {testApi()// 应用中就是一样的道理了,解析请求体,然后调用,最后把响应体封装返回给请求端就好了
}func testApi() {url := "http://127.0.0.1:9090/hi"user := User{Name: "zs",Password: "123",}data, err := getRestfulApi(url, user, "application/json")var tmp TempDatajson.Unmarshal(data, &tmp)fmt.Println(tmp, err)
}// getRestfulApi send post request
// url: 请求地址
// data: post请求提交的数据
// contentType: 请求体格式,如application/json
// res: 请求返回的内容
func getRestfulApi(url string, data interface{}, contentType string) ([]byte, error) {// 创建调用api接口的clientclient := &http.Client{Timeout: time.Second * 5}jsonStr, _ := json.Marshal(data)resp, err := client.Post(url, contentType, bytes.NewBuffer(jsonStr))if err != nil {fmt.Println("Error marshalling")return nil, err}res, err := ioutil.ReadAll(resp.Body)return res, err
}
15、参数校验1-tag验证
package mainimport ("github.com/gin-gonic/gin""github.com/go-playground/validator/v10""net/http""sync"
)var (Validate *validator.Validateonce sync.Once
)// User 大写
type User struct {Name string `json:"name" validate:"required"`Age int `json:"age" validate:"checkAge"` // 这里是为了演示,对于数字的校验,可以直接使用最大值最小值规则Password string `json:"password"`
}// 项目里可以这样实现
func init() {once.Do(func() {Validate = validator.New()Validate.RegisterValidation("checkAge", checkAgeFunc)})
}func checkAgeFunc(fl validator.FieldLevel) bool {if fl.Field().Int() > 18 {return true}return false
}func main() {r := gin.Default()r.POST("/hi", getMsg)r.Run(":9090")
}func getMsg(c *gin.Context) {var user User// 此处传指针err := c.Bind(&user)if err != nil {c.JSON(http.StatusBadRequest, err.Error())return}err = Validate.Struct(user)if err != nil {c.JSON(http.StatusBadRequest, err.Error())return}c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + user.Name,})
}
16、参数校验2-嵌套结构体验证
package mainimport ("fmt""github.com/gin-gonic/gin""github.com/go-playground/validator/v10""net/http""sync"
)var (Validate *validator.Validateonce sync.Once
)// User 大写
type User struct {Name string `json:"name" validate:"required"`Addr []Address `json:"addresses" validate:"required,dive"`
}type Address struct {Country string `json:"country" validate:"required"`City string `json:"city" validate:"required"`
}// 项目里可以这样实现
func init() {once.Do(func() {Validate = validator.New()})
}func main() {r := gin.Default()r.POST("/hi", getMsg)r.Run(":9090")
}func getMsg(c *gin.Context) {var user User// 此处传指针err := c.Bind(&user)if err != nil {c.JSON(http.StatusBadRequest, err.Error())return}err = Validate.Struct(user)if err != nil {c.JSON(http.StatusBadRequest, err.Error())return}fmt.Print("你的地址包括:")for _, v := range user.Addr {fmt.Print(v.Country + "-" + v.City + " ")}fmt.Println()c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + user.Name,})
}
17、swagger
完成代码
package mainimport (_ "gin-learn/2.16_swagger/docs""github.com/gin-gonic/gin"swaggerfiles "github.com/swaggo/files"ginSwagger "github.com/swaggo/gin-swagger""net/http"
)// User 大写
type User struct {Name string `json:"name" binding:"required"`Password string `json:"password"`
}func main() {r := gin.Default()r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))r.POST("/hi", getMsg)r.Run(":9090")
}// getMsg godoc
// @Tags 获取信息
// @Summary ping example
// @Description do ping
// @Accept json
// @Produce json
// @Param user body string true "用户"
// @Success 200 {string} json "{"code":"200", "msg": "欢迎:username"}"
// @Router /hi [post]
func getMsg(c *gin.Context) {var user User// 此处传指针err := c.Bind(&user)if err != nil {c.JSON(http.StatusBadRequest, err.Error())return}c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + user.Name,})
}
安装:
go install github.com/swaggo/swag/cmd/swag@latest
生成文档文件
swag init
运行,然后打开
http://localhost:9090/swagger/index.html
在我操作过程中碰到一个问题,我生成的docs.go中有这样一段代码
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = &swag.Spec{Version: "",Host: "",BasePath: "",Schemes: []string{},Title: "",Description: "",InfoInstanceName: "swagger",SwaggerTemplate: docTemplate,LeftDelim: "{{",RightDelim: "}}",
}
而我的swag是v1.8.12,Spec的定义中没有LeftDelim、RightDelim,如下,导致了报错,我只能手动删除这部分,不知道哪一步出错了。
// Spec holds exported Swagger Info so clients can modify it.
type Spec struct {Version stringHost stringBasePath stringSchemes []stringTitle stringDescription stringInfoInstanceName stringSwaggerTemplate string
}
18、Gin框架cookie
package mainimport ("encoding/hex""fmt""github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()// r.POST("/hi", getMsg)r.Use(CookieMiddleware())r.GET("/cookie", handleCookie)r.Run(":9090")
}func handleCookie(c *gin.Context) {name := c.Query("name")cookieName := "cookie_" + nameval, _ := c.Cookie(cookieName)if val == "" {c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "cookie : " + cookieName,})return}// c.String(http.StatusOK, "欢迎:%s",name)c.JSON(http.StatusOK, gin.H{"code": http.StatusOK,"msg": "欢迎:" + cookieName + "-" + val,})
}func CookieMiddleware() gin.HandlerFunc {return func(c *gin.Context) {name := c.Query("name")if len(name) <= 0 {c.JSON(http.StatusBadRequest, gin.H{"code": http.StatusBadRequest,"msg": "数据错误",})return}cookieName := "cookie_" + namecookieValue := hex.EncodeToString([]byte(cookieName + "value"))val, _ := c.Cookie(cookieName)if val == "" {c.SetCookie(cookieName, cookieValue, 3600, "/", "localhost", true, true)fmt.Println("cookie already set")}c.Next()}
}
验证的时候需要手动输入cookie,如图:
19、Gin框架Session
package mainimport ("github.com/gin-contrib/sessions""github.com/gin-contrib/sessions/cookie""github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()store := cookie.NewStore([]byte("session_secret"))r.Use(sessions.Sessions("mySession", store))r.GET("/session", handleSession)r.Run(":9090")
}var sessionName string
var sessionValue stringtype MyOption struct {sessions.Options
}func handleSession(c *gin.Context) {name := c.Query("name")if len(name) <= 0 {c.JSON(http.StatusBadRequest, "数据错误")return}sessionName = "session_" + namesessionValue = "session_value_" + namesession := sessions.Default(c)sessionData := session.Get(sessionName)if sessionData != sessionValue {session.Set(sessionName, sessionValue)o := MyOption{}o.Path = "/"o.MaxAge = 10 // 有效期,ssession.Options(o.Options)session.Save() // 保存sessionc.JSON(http.StatusOK, "首次访问,保存session")return}c.JSON(http.StatusOK, "session: "+sessionData.(string))
}
总结
gin框架的基本使用