BoyChai's Blog - Go https://blog.boychai.xyz/index.php/category/Go/ zh-CN Tue, 09 May 2023 14:54:00 +0000 Tue, 09 May 2023 14:54:00 +0000 [Go]gRPC-介绍 https://blog.boychai.xyz/index.php/archives/55/ https://blog.boychai.xyz/index.php/archives/55/ Tue, 09 May 2023 14:54:00 +0000 BoyChai gRPC是一款语言中立、平台中立、开源的远程过程调用系统,gRPC客户端和服务端可以在多种环境中运行和交互。,例如用java写一个服务端,可以用go语言写客户端调用。

数据在进行网络传输的时候,需要进行序列化,序列化协议有很多种,比如xml、json、protobuf等。

gRPC默认应用使用protocol buffers,这是google开源的一套成熟的结构数据序列化机制。

序列化: 将数据结构或对象转换成二进制串的过程。

反序列化: 将在虚拟化过程中所生产的二进制串转换成数据结构或对象的过程。

参考资料

]]>
0 https://blog.boychai.xyz/index.php/archives/55/#comments https://blog.boychai.xyz/index.php/feed/category/Go/
[Go]GIN框架-请求参数 https://blog.boychai.xyz/index.php/archives/49/ https://blog.boychai.xyz/index.php/archives/49/ Fri, 27 Jan 2023 14:53:00 +0000 BoyChai 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保存到本地

]]>
0 https://blog.boychai.xyz/index.php/archives/49/#comments https://blog.boychai.xyz/index.php/feed/category/Go/
[Go]GIN框架-路由 https://blog.boychai.xyz/index.php/archives/48/ https://blog.boychai.xyz/index.php/archives/48/ Thu, 19 Jan 2023 14:55:00 +0000 BoyChai 路由概述

路由(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")
})
]]>
0 https://blog.boychai.xyz/index.php/archives/48/#comments https://blog.boychai.xyz/index.php/feed/category/Go/
[Go]读取INI配置文件 https://blog.boychai.xyz/index.php/archives/43/ https://blog.boychai.xyz/index.php/archives/43/ Mon, 12 Dec 2022 15:03:00 +0000 BoyChai INI

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")
}
]]>
0 https://blog.boychai.xyz/index.php/archives/43/#comments https://blog.boychai.xyz/index.php/feed/category/Go/
[Go]发送GET、POST、DELETE、PUT请求 https://blog.boychai.xyz/index.php/archives/42/ https://blog.boychai.xyz/index.php/archives/42/ Wed, 07 Dec 2022 14:45:00 +0000 BoyChai GET请求
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))
}

POST请求

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))
}

提交FROM表单

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))
}

提交JSON数据

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))
}

DELETE请求

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))
}

PUT请求

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))
}
]]>
0 https://blog.boychai.xyz/index.php/archives/42/#comments https://blog.boychai.xyz/index.php/feed/category/Go/