数据在进行网络传输的时候,需要进行序列化,序列化协议有很多种,比如xml、json、protobuf等。
gRPC默认应用使用protocol buffers,这是google开源的一套成熟的结构数据序列化机制。
序列化: 将数据结构或对象转换成二进制串的过程。
反序列化: 将在虚拟化过程中所生产的二进制串转换成数据结构或对象的过程。
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"
]
}
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请求一般是表单参数和json参数
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"
}
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保存到本地
]]>路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 函数(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。
RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路由的时候建议参考 RESTful API 指南。
在 RESTful 架构中,每个网址代表一种资源,不同的请求方式表示执行不同的操作:
请求方式 | 操作 |
---|---|
GET(SELECT) | 从服务器中获取资源 |
POST(CREATE) | 在服务器中创建资源 |
PUT(UPDATE) | 在服务器中更新更改资源 |
DELETE(DELETE) | 在服务器中删除资源 |
// GET
r.GET("/GET", func(c *gin.Context) {
c.String(http.StatusOK, "GET")
})
// POST
r.POST("/POST", func(c *gin.Context) {
c.String(http.StatusOK, "POST")
})
// PUT
r.PUT("/PUT", func(c *gin.Context) {
c.String(http.StatusOK,"PUT")
})
// 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)
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")
})
]]>INI文件是Initialization File的缩写,意思为"配置文件;初始化文件;",以节(section)和键(key)构成,常用于微软Windows操作系统中。这种配置文件的文件扩展名多为INI,故名。
在主程序文件目录下创建"my.ini"文件内容如下
app_mode = development
[paths]
data = /home/git/grafana
[server]
protocol = http
http_port = 9999
enforce_domain = true
package main
import (
"fmt"
"github.com/go-ini/ini"
)
func main() {
// 读取配置文件
cfg, err := ini.Load("my.ini")
if err != nil {
panic(err)
}
//读取
//读取app_mode
fmt.Println("app_mode:", cfg.Section("").Key("app_mode"))
//读取paths.data
fmt.Println("paths.data:", cfg.Section("paths").Key("data").String())
//读取server.protocol并限制值
//如果没有值或值错误则使用"http"
fmt.Println("server.protocol:", cfg.Section("server").Key("protocol").In("http", []string{"http", "https"}))
//读取时进行类型转换,MustInt()可以存放默认值,如果转换失败则使用里面的值
fmt.Printf("Port Number: (%[1]T) %[1]d\n", cfg.Section("server").Key("http_port").MustInt())
//修改
//修改app_mode的值为production
cfg.Section("").Key("app_mode").SetValue("production")
//修改完毕写入"my.ini.local"文件
cfg.SaveTo("my.ini.local")
}
]]>func main() {
// 发送请求
r, err := http.Get("https://www.baidu.com?name=boychai&age=18")
// 检查错误
if err != nil {
panic(err)
}
// 释放
defer func() { _ = r.Body.Close() }()
//读取请求内容
content, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
fmt.Println(string(content))
}
func main() {
// 发送请求
r, err := http.Post("https://www.baidu.com", "", nil)
// 检查错误
if err != nil {
panic(err)
}
// 释放
defer func() { _ = r.Body.Close() }()
//读取请求内容
content, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
fmt.Println(string(content))
}
func main() {
// from data 形式类似于 name=boychai&age=18
data := make(url.Values)
data.Add("name", "boychai")
data.Add("age", "18")
payload := data.Encode()
// 发送请求
r, err := http.Post("https://httpbin.org/post", "application/x-www-form-urlencoded", strings.NewReader(payload))
defer func() { _ = r.Body.Close() }()
if err != nil {
print(err)
}
//读取
content, err := ioutil.ReadAll(r.Body)
fmt.Println(string(content))
}
func main() {
u := struct {
Name string `json:"name"`
Age string `json:"age"`
}{
Name: "BoyChai",
Age: "18",
}
payload, _ := json.Marshal(u)
// 发送请求
r, err := http.Post("https://httpbin.org/post", "application/json", bytes.NewReader(payload))
defer func() { _ = r.Body.Close() }()
if err != nil {
print(err)
}
//读取
content, err := ioutil.ReadAll(r.Body)
fmt.Println(string(content))
}
func main() {
// 创建请求对象
request, err := http.NewRequest(http.MethodDelete, "https://www.baidu.com", nil)
if err != nil {
panic(err)
}
// 发送请求
r, err := http.DefaultClient.Do(request)
defer func() { _ = r.Body.Close() }()
if err != nil {
panic(err)
}
// 读取
content, err := ioutil.ReadAll(r.Body)
fmt.Println(string(content))
}
func main() {
// 创建请求对象
request, err := http.NewRequest(http.MethodPut, "https://www.baidu.com", nil)
if err != nil {
panic(err)
}
// 发送请求
r, err := http.DefaultClient.Do(request)
defer func() { _ = r.Body.Close() }()
if err != nil {
panic(err)
}
// 读取
content, err := ioutil.ReadAll(r.Body)
fmt.Println(string(content))
}
]]>