BoyChai's Blog - Go https://blog.boychai.xyz/index.php/category/Go/ [Go]gRPC-介绍 https://blog.boychai.xyz/index.php/archives/55/ 2023-05-09T14:54:00+00:00 gRPC是一款语言中立、平台中立、开源的远程过程调用系统,gRPC客户端和服务端可以在多种环境中运行和交互。,例如用java写一个服务端,可以用go语言写客户端调用。数据在进行网络传输的时候,需要进行序列化,序列化协议有很多种,比如xml、json、protobuf等。gRPC默认应用使用protocol buffers,这是google开源的一套成熟的结构数据序列化机制。序列化: 将数据结构或对象转换成二进制串的过程。反序列化: 将在虚拟化过程中所生产的二进制串转换成数据结构或对象的过程。参考资料码神之路-Go语言gRPChttps://www.bilibili.com/video/BV16Z4y117yz/ [Go]GIN框架-请求参数 https://blog.boychai.xyz/index.php/archives/49/ 2023-01-27T14:53:00+00:00 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/queryform-dataid=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/queryraw 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保存到本地 [Go]GIN框架-路由 https://blog.boychai.xyz/index.php/archives/48/ 2023-01-19T14:55:00+00:00 路由概述路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 函数(GET、POST 等)组成的,涉及到应用如何响应客户端对某个网站节点的访问。RESTful APIRESTful 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静态URIr.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") }) [Go]读取INI配置文件 https://blog.boychai.xyz/index.php/archives/43/ 2022-12-12T15:03:00+00:00 INIINI文件是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") } [Go]发送GET、POST、DELETE、PUT请求 https://blog.boychai.xyz/index.php/archives/42/ 2022-12-07T14:45:00+00:00 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)) }