BoyChai's Blog - Gin 2023-01-27T14:53:00+00:00 Typecho https://blog.boychai.xyz/index.php/feed/atom/tag/Gin/ <![CDATA[[Go]GIN框架-请求参数]]> https://blog.boychai.xyz/index.php/archives/49/ 2023-01-27T14:53:00+00:00 2023-01-27T14:53:00+00:00 BoyChai https://blog.boychai.xyz Get请求参数

普通参数

请求

http://localhost:8080/query?id=123&name=user

接收

第一种方式

r.GET("/query", func(context *gin.Context) {
    // 获取传参
    id := context.Query("id")
    name := context.Query("name")
    // 设置某个参数的默认值
    sex := context.DefaultQuery("sex", "unknown")
    // 检查address值是否传入
    address, ok := context.GetQuery("address")
    context.JSON(http.StatusOK, gin.H{
        "id":         id,
        "name":       name,
        "sex":        sex,
        "address":    address,
        "address-ok": ok,
    })
})

第二种方式

提前定义结构体

// 定义结构体需要表示form名称,如果不标识则传参时key必须和结构体的对应的值一样比如Id就必须传入Id,id是不行的
type User struct {
    Id      int64  `form:"id"'`
    Name    string `form:"name"`
    Sex     string `form:"sex"`
    // binding是指传参必须拥有此参数
    Address string `form:"address" binding:"required"'`
}
r.GET("/query", func(context *gin.Context) {
    var user User
    err := context.BindQuery(&user)
    if err != nil {
        log.Println(err)
    }
    context.JSON(http.StatusOK, gin.H{
        "id":      user.Id,
        "name":    user.Name,
        "sex":     user.Sex,
        "address": user.Address,
    })
})

第三种方式

提前定义结构体

// 定义结构体需要表示form名称,如果不标识则传参时key必须和结构体的对应的值一样比如Id就必须传入Id,id是不行的
type User struct {
    Id      int64  `form:"id"'`
    Name    string `form:"name"`
    Sex     string `form:"sex"`
    // binding是指传参必须拥有此参数
    Address string `form:"address" binding:"required"'`
}
r.GET("/query", func(context *gin.Context) {
    var user User
    err := context.ShouldBindQuery(&user)
    if err != nil {
        log.Println(err)
    }
    context.JSON(http.StatusOK, gin.H{
        "id":      user.Id,
        "name":    user.Name,
        "sex":     user.Sex,
        "address": user.Address,
    })
})

返回值

第一种方式

状态码200

{
    "address": "",
    "address-ok": false,
    "id": "123",
    "name": "user",
    "sex": "unknown"
}

第二种方式

状态码400

后台会报:

2023/01/27 22:18:55 Key: 'User.Address' Error:Field validation for 'Address' failed on the 'required' tag
[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 200
{
    "address": "",
    "id": 123,
    "name": "user",
    "sex": ""
}

第三种方式

状态码200

后台会报:

2023/01/27 22:19:33 Key: 'User.Address' Error:Field validation for 'Address' failed on the 'required' tag
{
    "address": "",
    "id": 123,
    "name": "user",
    "sex": ""
}

数组参数

请求

http://localhost:8080/query?address=beijing&address=qingdao

接收

第一种方式

r.GET("/query", func(context *gin.Context) {
    address := context.QueryArray("address")
    context.JSON(http.StatusOK, address)
})

第二种方式

提前定义结构体

type Address struct {
    Address []string `form:"address"`
}
r.GET("/query", func(context *gin.Context) {
    var address Address
    err := context.BindQuery(&address)
    if err != nil {
        log.Println(err)
    }
    context.JSON(http.StatusOK, address)
})

第三种方式

提前定义结构体

type Address struct {
    Address []string `form:"address"`
}
r.GET("/query", func(context *gin.Context) {
    var address Address
    err := context.ShouldBindQuery(&address)
    if err != nil {
        log.Println(err)
    }
    context.JSON(http.StatusOK, address)
})

返回值

第一种方式

状态码200

[
    "beijing",
    "qingdao"
]

第二种方式

状态码200

{
    "Address": [
        "beijing",
        "qingdao"
    ]
}

第三种方式

状态码200

{
    "Address": [
        "beijing",
        "qingdao"
    ]
}

map参数

请求

http://localhost:8080/query?addressMap[home]=beijing&addressMap[company]=qingdao

接收

r.GET("/query", func(context *gin.Context) {
    addressMap := context.QueryMap("addressMap")
    context.JSON(http.StatusOK, addressMap)
})

返回值

状态码200

{
    "company": "qingdao",
    "home": "beijing"
}

POST请求

post请求一般是表单参数和json参数

表单请求

请求

http://localhost:8080/query

form-data

id=123
name=user
address=beijing
address=qingdao
addressMap[home]=beijing
addressMap[company]=qingdao

接收

r.POST("/query", func(context *gin.Context) {
    id := context.PostForm("id")
    name := context.PostForm("name")
    address := context.PostFormArray("address")
    addressMap := context.PostFormMap("addressMap")
    context.JSON(http.StatusOK, gin.H{
        "id":         id,
        "name":       name,
        "address":    address,
        "addressMap": addressMap,
    })
})

PS:

接收也可以使用func (c *gin.Context) ShouldBind(obj any) error {}函数进行接收和上面get讲的差不多一样这里不再讲解

返回值

状态码200

{
    "address": [
        "beijing",
        "qingdao"
    ],
    "addressMap": {
        "company": "qingdao",
        "home": "beijing"
    },
    "id": "123",
    "name": "user"
}

JSON请求

请求

http://localhost:8080/query

raw or json

{
    "address": [
        "beijing",
        "qingdao"
    ],
    "addressMap": {
        "company": "qingdao",
        "home": "beijing"
    },
    "id": 123,
    "name": "user",
    "sex":"Male"
}

接收

定义结构体,接收json数据结构体不需要加form表示,接收时会自动把Name转换为name进行匹配接收或者添加json的标识进行匹配

type User struct {
    Id         int64              `json:"id"`
    Name       string            
    Sex        string            
    Address    []string          
    AddressMap map[string]string 
}
    r.POST("/query", func(context *gin.Context) {
        var user User
        err := context.ShouldBindJSON(&user)
        if err != nil {
            log.Println(err)
        }
        context.JSON(http.StatusOK, user)
    })

返回值

状态码200

{
    "Id": 123,
    "Name": "user",
    "Sex": "Male",
    "Address": [
        "beijing",
        "qingdao"
    ],
    "AddressMap": {
        "company": "qingdao",
        "home": "beijing"
    }
}

文件请求

代码实现

r.POST("/save", func(context *gin.Context) {
   form, err := context.MultipartForm()
   if err != nil {
      log.Println(err)
   }
   for _, fileArray := range form.File {
      for _, v := range fileArray {
         context.SaveUploadedFile(v, "./"+v.Filename)
      }
   }
   context.JSON(http.StatusOK, form.Value)
})

context.MultipartForm()返回的from里面有俩值,一个是文件数组,还有一个是提交的参数。上传好的文件会被context.SaveUploadedFile保存到本地

]]>
<![CDATA[[Go]GIN框架-路由]]> https://blog.boychai.xyz/index.php/archives/48/ 2023-01-19T14:55:00+00:00 2023-01-19T14:55:00+00:00 BoyChai https://blog.boychai.xyz 路由概述

路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 函数(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。

RESTful API

RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路由的时候建议参考 RESTful API 指南。

在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作:

请求方式操作
GET(SELECT)从服务器中获取资源
POST(CREATE)在服务器中创建资源
PUT(UPDATE)在服务器中更新更改资源
DELETE(DELETE)在服务器中删除资源

创建路由

GET

// GET
r.GET("/GET", func(c *gin.Context) {
    c.String(http.StatusOK, "GET")
})

POST

// POST
r.POST("/POST", func(c *gin.Context) {
    c.String(http.StatusOK, "POST")
})

PUT

// PUT
r.PUT("/PUT", func(c *gin.Context) {
    c.String(http.StatusOK,"PUT")
})

DELETE

// DELETE
r.DELETE("/DELETE", func(c *gin.Context) {
    c.String(http.StatusOK, "DELETE")
})

其他创建方法

所有类型

如果同一个地址想要所有类型的请求都支持使用那么可以通过下面代码进行创建

// 接收所有类型请求
r.Any("/Any", func(c *gin.Context) {
    c.String(http.StatusOK, "Any")
})

两个或多个

// 先创建一个方法
func requestReturn(c *gin.Context) {
    c.String(http.StatusOK, "OTHER")
}
// 在创建请求URL
r.GET("/OTHER", requestReturn)
r.POST("/OTHER", requestReturn)

URI

静态URI

r.GET("/user/find", func(c *gin.Context) {
    c.String(http.StatusOK, "any")
})

路径参数

r.GET("/user/find/:id", func(c *gin.Context) {
    c.String(http.StatusOK, c.Param("id"))
})

模糊参数

// 使用path来接收值
r.GET("/user/*path", func(c *gin.Context) {
    c.String(http.StatusOK, c.Param("path"))
})

路由分组

在实际开发中,路由可能会有很多的版本,为了方便开发管理可以使用以下方法进行分组创建

// 路由分组
v1 := r.Group("v1")
v2 := r.Group("v2")
v1.GET("/user/find", func(c *gin.Context) {
    c.String(http.StatusOK, "/v1/user/find")
})
v2.GET("/user/find", func(c *gin.Context) {
    c.String(http.StatusOK, "/v2/user/find")
})
]]>