提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
gin框架的基本使用,把各个知识点拆分写成demo。
最简单的获取参数的方法。我还知道json映射,后续补充。
package main
import (
"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)
}
package main
import (
"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,
})
}
package main
import (
"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.Body
contentLength := resp.ContentLength
contentType := resp.Header.Get("Content-Type")
c.DataFromReader(http.StatusOK, contentLength, contentType, body, nil)
})
r.Run(":9090")
}
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/getJson", func(c *gin.Context) {
// JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003c
c.JSON(http.StatusOK, gin.H{
"html": "hi
",
})
})
r.GET("/getHTML", func(c *gin.Context) {
// 如果要正常返回HTML字符,用PureJSON
c.PureJSON(http.StatusOK, gin.H{
"html": "hi
",
})
})
r.GET("/getXML", func(c *gin.Context) {
type Message struct {
Name string
Msg string
Age 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")
}
package main
import (
"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 字符,例如 < 变为 \ u003c
c.File(fileName)
})
r.Run(":9090")
}
package main
import (
"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")
}
package main
import (
"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")
}
package main
import (
"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()
}
}
package main
import (
"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
}
package main
import (
"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("同步任务结束")
}
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
"net/http"
"time"
)
var g errgroup.Group
func 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
}
package main
import (
"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,
})
}
package main
import (
"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,
})
}
package main
import (
"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 TempData
json.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接口的client
client := &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
}
package main
import (
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"net/http"
"sync"
)
var (
Validate *validator.Validate
once 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,
})
}
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"net/http"
"sync"
)
var (
Validate *validator.Validate
once 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,
})
}
完成代码
package main
import (
_ "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 string
Host string
BasePath string
Schemes []string
Title string
Description string
InfoInstanceName string
SwaggerTemplate string
}
package main
import (
"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_" + name
val, _ := 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_" + name
cookieValue := 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,如图:
package main
import (
"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 string
type MyOption struct {
sessions.Options
}
func handleSession(c *gin.Context) {
name := c.Query("name")
if len(name) <= 0 {
c.JSON(http.StatusBadRequest, "数据错误")
return
}
sessionName = "session_" + name
sessionValue = "session_value_" + name
session := sessions.Default(c)
sessionData := session.Get(sessionName)
if sessionData != sessionValue {
session.Set(sessionName, sessionValue)
o := MyOption{}
o.Path = "/"
o.MaxAge = 10 // 有效期,s
session.Options(o.Options)
session.Save() // 保存session
c.JSON(http.StatusOK, "首次访问,保存session")
return
}
c.JSON(http.StatusOK, "session: "+sessionData.(string))
}
gin框架的基本使用