Gin框架示例
Gin框架示例
1、AsciiJSON
使用 AsciiJSON 生成具有转义的非 ASCII 字符的 ASCII-only JSON。
(1)完整代码
1 | |
(2)知识点
gin.Default(): 这是 Gin 框架的一个方法,用于创建一个带有默认中间件的路由引擎,包括了logger和recovery中间件,用于记录请求日志和在出现 panic 时进行恢复,防止程序崩溃。r.GET("/someJSON", ...):路由注册方法,指定一个路径为”/someJSON”的GET请求,当客户端请求”some/JSON”路径时,会调用后面的处理函数来处理该请求。fun(c *gin.Context) {...}:这是一个匿名函数,作为路由处理函数,接收一个gin.Context参数。gin.Context:是Gin框架的一个上下文对象,用于在处理HTTP请求和生成HTTP响应时传递和管理数据。每当 Gin 框架处理一个HTTP请求时,都会创建一个新的gin.Context对象,并将其传递给注册的路由处理函数。它包含了很多有用的方法和属性,用于访问HTTP请求的各种信息,如请求路径、HTTP方法、球请求参数、请求头、请求体等。data := map[string]interface{}{...}:创建了一个map[string]interface{}类型的变量data,用于构造要返回的JSON数据。这里使用了一个简单的map,包含了连个键值对,分别是”lang”和”tag”。c.AsciiJSON(http.StatusOK, data):这是gin.Context的方法,用于将data变量的内容作为ASCII JSON格式的响应返回给客户端。http.StatusOK是HTTP状态码,表示请求成功,这里将其作为响应的状态码。data 变量作为响应的主体内容,通过ASCII JSON格式进行序列化,并设置了响应的Content-Type为 “application/json; charset=utf-8”。最终,这个处理函数会将生成的响应发送给客户端。
(3)示例大意
- 导入了
gin和net/http包,引入了 Gin 框架和标准库中处理 HTTP 请求和响应的功能。- 创建了一个默认的 Gin 路由引擎
r,作为 HTTP 请求的入口。- 注册了一个 GET 路由,路径为 “/someJSON”,并定义了一个处理函数,处理函数使用
c.AsciiJSON()方法将一个定义好的 map 数据转换为 ASCII JSON 格式,并作为响应发送给客户端。- 使用
r.Run()方法启动了一个 Gin web 服务器,并监听在 8080 端口,等待客户端的 HTTP 请求。- 当客户端通过浏览器或其他 HTTP 客户端访问 “/someJSON” 路径时,服务器会返回一个 JSON 格式的响应,包含了定义好的
data数据,其中 “lang” 键的值为 “GO语言”,”tag” 键的值为 “
“。这个响应的内容类型为 ASCII JSON。
2、绑定表单数据至自定义结构体
下述代码实现了一个简单的Web应用程序,通过Gin框架处理HTTP请求,将从请求中获取的数据绑定到结构体变量,并生成JSON格式的HTTP响应体。
(1)完整代码
1 | |
(2)知识点
c.Bind(&b):使用了Gin框架的Bind方法,将从HTTP请求中获取的数据绑定到b变量。&b表示传递b的指针,以便将数据绑定到b变量的字段。c.JSON(200, gin.H{"a": b.NestedStruct, "b": b.FieldB}):使用Gin框架的JSON方法,生成HTTP响应的JSON格式。200表示HTTP响应的状态码为200,gin.H是Gin框架提供的一种快捷方式,表示一个map[string]interface{}类型的数据结构,用于构造JSON响应体。在这里,构造了一个包含两个键值对的JSON响应体,其中键为a和b,值分别为b.NestedStruct和b.FieldB,这些值来自之前绑定的b变量的字段。- JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,通常用于在不同应用程序之间传输和存储数据。JSON采用键值对的形式表示数据,使用文本格式进行序列化,具有简洁、易读、易解析的特点。
(3)示例大意
- 定义了四个结构体类型:
StructA、StructB、StructC、StructD,分别包含了不同的字段和标签。- 在
GetDataB处理函数中,从HTTP请求中绑定数据到StructB类型的变量b,然后生成JSON响应体,包含了b.NestedStruct和b.FieldB字段的值。- 在
GetDataC处理函数中,从HTTP请求中绑定数据到StructC类型的变量b,然后生成JSON响应体,包含了b.NestedStructPointer和b.FieldC字段的值。- 在
GetDataD处理函数中,从HTTP请求中绑定数据到StructD类型的变量b,然后生成JSON响应体,包含了b.NestedAnonyStruct和b.FieldD字段的值。- 在
main函数中,创建了一个Gin的默认路由器,并注册了三个路由(/getb、/getc、/getd),并将对应的处理函数(GetDataB、GetDataC、GetDataD)作为处理函数。最后调用r.Run()启动Gin的web服务器,监听并处理HTTP请求。
(4)使用 curl 命令结果:
1 | |
3、绑定 HTML 复选框
(1)前置条件
在main.go文件的同目录下,存在
views/form.html目录,且form.html文件里存放了HTML模板文件,如下:
1 | |
(2)完整代码
1 | |
(3)知识点
r.LoadHTMLGlob("views/*"): 加载views目录下的HTML模板文件,用于后续的HTML模板渲染。LoadHTMLGlob()方法接受一个模板文件路径的参数,使用通配符*可以匹配指定目录下的所有文件。r.GET("/", indexHandler): 注册一个处理根路径的GET请求的路由,当客户端发送GET请求到根路径”/“时,Gin会调用indexHandler函数进行处理。indexHandler函数是自定义的处理函数,用于处理根路径的GET请求。r.POST("/", formHandler): 注册一个处理根路径的POST请求的路由,当客户端发送POST请求到根路径”/“时,Gin会调用formHandler函数进行处理。formHandler函数是自定义的处理函数,用于处理根路径的POST请求。
(4)示例大意
- 定义了一个名为
myForm的结构体,其中包含一个Colors字段,用于接收表单提交的colors[]参数。Colors字段使用了form:"colors[]"标签,表示在表单中使用colors[]作为参数名。- 在
main()函数中创建了一个Gin引擎实例,并设置了默认的中间件。- 使用
r.LoadHTMLGlob("views/*")加载了views目录下的HTML模板文件,用于后续的HTML模板渲染。- 使用
r.GET("/", indexHandler)注册了一个处理根路径的GET请求的路由,当客户端发送GET请求到根路径”/“时,Gin会调用indexHandler函数进行处理。- 使用
r.POST("/", formHandler)注册了一个处理根路径的POST请求的路由,当客户端发送POST请求到根路径”/“时,Gin会调用formHandler函数进行处理。- 定义了
indexHandler函数,用于处理根路径的GET请求。该函数通过调用c.HTML()方法,在响应中渲染名为form.html的HTML模板,并返回200状态码。- 定义了
formHandler函数,用于处理根路径的POST请求。该函数首先定义了一个名为fakeForm的myForm类型的变量,然后通过调用c.Bind()方法,将请求参数绑定到fakeForm结构体中。最后,通过调用c.JSON()方法,返回一个JSON格式的响应,包含fakeForm结构体中的Colors字段值。
4、绑定查询字符串或表单数据
(1)完整代码
1 | |
(2)知识点
-
ShouldBind是 Gin 框架提供的一个方法,用于将 HTTP 请求中的参数绑定到一个结构体变量上。ShouldBind方法会根据请求中的参数名称和目标结构体的字段名称进行自动匹配,并将对应的参数值绑定到结构体字段上。在示例代码中的Person结构体定义中使用了form标签来指定参数的来源和绑定规则,如form:"name"、form:"address"和form:"birthday" time_format:"2006-01-02" time_utc:"1"。这样,在调用ShouldBind方法时,Gin 会根据请求中的表单参数名称和结构体字段的标签进行自动匹配和绑定。需要注意的是,ShouldBind方法在绑定参数时会根据目标结构体字段的类型和标签进行自动转换和验证,如果绑定失败会返回错误信息。因此,在使用ShouldBind方法时,需要进行错误处理,可以通过判断返回的错误是否为nil来判断参数绑定是否成功。如果绑定失败,可以通过错误信息来获取详细的错误原因,并做相应的处理。
(3)代码大意
- 定义了一个
Person结构体,用于存储请求参数。Person结构体包含了三个字段:Name、Address和Birthday,分别对应请求中的表单参数 “name”、”address” 和 “birthday”。- 创建了一个 Gin 路由引擎
route,使用了默认的中间件。- 注册了一个 HTTP GET 请求的处理函数
startPage,该处理函数通过调用 Gin 的ShouldBind方法将请求参数绑定到Person结构体变量person上。ShouldBind方法会根据请求的类型(GET 或 POST)和请求的 content-type 自动选择合适的绑定引擎(Form 或 JSON/XML 绑定引擎)进行参数绑定。- 在
startPage函数中,通过打印person结构体的字段值来验证参数绑定是否成功。- 最后,返回了一个 HTTP 200 OK 响应,内容为 “Success” 字符串。
总的来说,这段代码实现了一个简单的 HTTP GET 请求处理函数,使用 Gin 框架来绑定请求参数并处理请求。当请求到达
/testing路径时,会触发startPage函数,将请求参数绑定到Person结构体变量,并输出字段值。如果参数绑定失败,则输出错误信息。最后返回一个 “Success” 字符串作为响应。
(4)测试
1 | |
若响应成功,可以看到页面中会显示Success;
若响应成功,且信息匹配成功,终端中会打印出信息。
5、绑定 Uri
(1)完整代码
1 | |
(2)知识点
c.ShouldBindUri(&person): 这是*gin.Context上下文对象的方法,用于将 URI 参数绑定到结构体变量person上。ShouldBindUri方法会自动根据结构体字段的uri标签和 URI 路径中的参数名称进行匹配和绑定。在示例代码中,ID字段的uri标签指定了参数名称为id,Name字段的uri标签指定了参数名称为name。同时,结构体字段的binding标签还指定了参数的验证规则,例如required表示参数必填,uuid表示参数的值必须是 UUID 格式。c.JSON(400, gin.H{"msg": err.Error()}): 这是*gin.Context上下文对象的方法,用于构建一个 JSON 格式的 HTTP 响应。其中,400表示 HTTP 状态码为 400(Bad Request),gin.H{"msg": err.Error()}表示响应的 JSON 数据,包含一个名为msg的字段,值为错误信息的字符串。c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID}): 这是*gin.Context上下文对象的方法,用于构建一个 JSON 格式的 HTTP 响应。其中,200表示 HTTP 状态码为 200(OK),gin.H{"name": person.Name, "uuid": person.ID}表示响应的 JSON 数据,包含两个字段,分别为name和uuid,值分别为person.Name和person.ID的值,即绑定后的 URI 参数值。
(3)示例大意
上述代码使用了 Gin 框架来创建一个 HTTP 服务器,并定义了一个 GET 请求处理函数,该处理函数绑定了两个 URI 参数(
:name和:id)到一个自定义的结构体Person的字段上。具体而言,代码实现了以下几个功能:
- 导入了
github.com/gin-gonic/gin包,该包是 Gin 框架的核心库,提供了用于构建 Web 应用的 HTTP 路由和中间件功能。- 定义了一个名为
Person的结构体,该结构体包含了两个字段ID和Name,分别用于绑定 URI 参数id和name。- 创建了一个默认的 Gin 路由引擎实例
route,用于处理 HTTP 请求和路由。- 定义了一个路由处理函数,处理 HTTP GET 请求,该处理函数绑定了路由路径中的
:name和:id参数到Person结构体的字段上,并在绑定成功后返回绑定后的值,否则返回错误信息。- 使用
c.ShouldBindUri(&person)方法将 URI 参数绑定到Person结构体的字段上。ShouldBindUri方法会根据结构体字段的标签信息来解析和验证 URI 参数,并将解析后的值赋给结构体字段。- 如果 URI 参数绑定失败(例如,缺少必填参数或参数格式不符合预期),则通过
c.JSON方法返回 HTTP 状态码 400(Bad Request)和错误信息。- 如果 URI 参数绑定成功,则通过
c.JSON方法返回 HTTP 状态码 200(OK)和绑定后的值,包括Person结构体中的Name和ID字段的值。- 最后,通过
route.Run(":8088")启动 HTTP 服务器,监听在本地的 8088 端口上。一旦有请求到达该端口,将会由 Gin 路由引擎处理并调用相应的路由处理函数。
(4)测试
网页:
1 | |
终端:
1 | |
6、控制Log高亮输出
默认是会高亮(当然这基于你使用的 TTY)。
(1)完整代码
如果你不想使用日志高亮:
1 | |
手动设置高亮:
1 | |
(2)知识点
gin.ForceConsoleColor(): 这是 Gin 框架的一个方法,用于强制开启日志的高亮显示。它可以让 Gin 框架在控制台输出的日志信息更加醒目和易读。gin.DisableConsoleColor(): 这是 Gin 框架的一个方法,用于关闭日志的高亮显示。当调用了这个方法后,Gin 框架在控制台输出的日志信息将不再具有高亮效果,变为普通的文本显示。
7、自定义HTTP配置
(1)完整代码
直接使用
1 | |
或
1 | |
(2)知识点
http.ListenAndServe(":8080", router): 这是 Go 标准库net/http提供的一个方法,用于启动一个 HTTP 服务器并监听指定的地址和端口,并使用传入的路由引擎处理请求。在这里,我们指定监听地址为空字符串,表示监听所有可用的网卡,端口为 8080。router是之前创建的 Gin 路由引擎实例,用于处理请求。s := &http.Server{...}这段代码创建了一个http.Server实例,并设置了以下配置项:Addr:指定服务器监听的地址和端口,这里设置为:8080,表示监听在本地的 8080 端口。Handler:设置处理请求的路由引擎,这里使用了之前创建的router,即 Gin 路由引擎。ReadTimeout:设置读取请求的超时时间,这里设置为 10 秒,表示如果在 10 秒内没有读取到完整的请求,服务器会关闭连接。WriteTimeout:设置写入响应的超时时间,这里设置为 10 秒,表示如果在 10 秒内没有完成响应的写入操作,服务器会关闭连接。MaxHeaderBytes:设置最大请求头的大小,这里设置为 1 << 20,即 1MB,表示请求头的大小不超过 1MB。
这些配置项可以根据具体的需求进行调整,用于自定义 HTTP 服务器的行为,例如设置超时时间、限制请求头大小等。在创建完
http.Server实例后,可以通过调用ListenAndServe()方法来启动服务器,开始监听指定的地址和端口,并使用设置的路由引擎来处理请求。s.ListenAndServe(): 这是 http.Server 的一个方法,用于启动 HTTP 服务器,并监听在指定的地址和端口,使用创建的路由引擎处理请求。在这里,s是之前创建的自定义 http.Server 实例,通过调用ListenAndServe()方法来启动服务器。
(3)示例大意
- 创建一个默认的 Gin 路由引擎实例,使用了默认的中间件包括日志和恢复中间件。
- 创建一个自定义的
http.Server实例,设置了监听的地址和端口为:8080,使用之前创建的路由引擎router来处理请求,设置了读取请求的超时时间为 10 秒,写入响应的超时时间为 10 秒,最大请求头大小为 1MB。- 调用
ListenAndServe()方法启动 HTTP 服务器,开始监听指定的地址和端口,并使用创建的路由引擎来处理请求。这段代码的目的是创建一个自定义的 HTTP 服务器,使用 Gin 框架来处理请求,并设置了一些自定义的配置项,如超时时间和最大请求头大小。最后通过调用
ListenAndServe()方法启动服务器,使其开始监听客户端请求。
8、 自定义 Log 文件
(1)完整代码
1 | |
(2)知识点
router.Use(): 此方法用于向 Gin 路由引擎添加中间件。中间件是处理 HTTP 请求的函数,在请求到达实际的路由处理器之前被执行。在这段代码中,使用router.Use()添加了两个中间件函数:router.Use(): 此方法用于向 Gin 路由引擎添加中间件。中间件是处理 HTTP 请求的函数,在请求到达实际的路由处理器之前被执行。在这段代码中,使用router.Use()添加了两个中间件函数:gin.LoggerWithFormatter(): 这个中间件函数用于以自定义格式记录 HTTP 请求日志。param参数是gin.LogFormatterParams类型的实例,包含了有关 HTTP 请求和响应的信息。在给定的代码中,param被用于格式化一个自定义的日志字符串,其中包括了客户端 IP 地址、时间戳、HTTP 方法、请求路径、协议版本、响应状态码、请求耗时、客户端 UserAgent 信息和错误消息(如果有的话)。gin.Recovery(): 这个中间件函数用于从可能在处理 HTTP 请求时发生的 panic 中恢复。如果发生 panic,这个中间件会从中恢复,并向客户端返回一个 500 内部服务器错误的响应。
(3)示例大意
导入了
fmt、github.com/gin-gonic/gin和time包。在
main()函数中,创建了一个新的 Gin 路由引擎实例router,并使用gin.New()方法创建了一个不使用默认中间件的路由引擎。使用
router.Use()方法添加了两个自定义中间件:a.
gin.LoggerWithFormatter()中间件,用于将日志格式化为自定义格式。在回调函数中,通过gin.LogFormatterParams参数获取了请求和响应的信息,然后使用自定义的格式将这些信息组合成一个日志字符串。b.
gin.Recovery()中间件,用于从 panic 中恢复。这将确保在处理请求时发生 panic 时,服务器不会崩溃,而是能够从中恢复,并返回一个 500 内部服务器错误的响应。使用
router.GET()方法定义了一个处理 HTTP GET 请求的路由,路径为 “/ping”。在回调函数中,使用c.String()方法向客户端发送一个状态码为 200 的 “pong” 字符串响应。最后,使用
router.Run()方法启动了 HTTP 服务器,监听在地址 “:8080” 上,并使用创建的路由引擎处理传入的请求。这段代码的功能是创建一个简单的 HTTP 服务器,监听在端口 8080,当访问 “/ping” 路径时,返回一个 “pong” 的响应。同时,还添加了自定义的日志中间件和恢复中间件,以增强服务器的健壮性和可靠性。
9、自定义中间件
(1)完整代码
1 | |
(2)知识点
time.Now(): 这是 Go 语言中的时间包time中的函数,用于获取当前时间。在这段代码中,用于记录请求处理开始的时间t,以便后续计算请求耗时。c.Set(key string, value interface{}): 这是 Gin 框架中Context对象的方法,用于在 Gin 上下文中设置一个自定义的键值对。在这段代码中,使用该方法将一个名为 “example” 的自定义变量设置到 Gin 上下文中,值为字符串 “12345”。c.Next(): 这是 Gin 框架中Context对象的方法,用于执行下一个处理函数。在这段代码中,用于将请求传递给下一个处理函数,通常在中间件中使用,确保请求能够继续向后处理。time.Since(t): 这是 Go 语言中的时间包time中的函数,用于计算当前时间与给定时间t的时间差。在这段代码中,用于计算请求耗时,即当前时间与请求处理开始时间t的时间差。log.Print(v ...interface{}): 这是 Go 语言中的日志包log中的函数,用于将日志消息打印到标准输出。在这段代码中,用于打印请求耗时latency和用于打印响应状态码。c.Writer.Status(): 这是 Gin 框架中Context对象的方法,用于获取响应状态码。在这段代码中,用于获取响应状态码,以便后续打印到日志中。
(3)示例执行过程
- 在
main()函数中,首先创建了一个新的 Gin 路由引擎实例r。- 使用
r.Use(Logger())将自定义的Logger中间件注册到路由引擎r中,以便在处理请求时自动调用。- 定义了一个处理 HTTP GET 请求的路由处理函数,路径为 “/test”。在这个处理函数中,使用
c.MustGet("example")从 Gin 上下文中获取之前在Logger中间件中设置的名为 “example” 的自定义变量,并将其转换为字符串类型。- 在路由处理函数中,使用日志包
log将获取到的 “example” 变量打印到日志中。- 使用
r.Run(":8080")启动 HTTP 服务器,监听在地址 “:8080” 并使用创建的路由引擎r处理请求。- 当有请求到达时,Gin 框架会依次执行注册到路由引擎的中间件和路由处理函数。
Logger()中间件会在请求前记录当前时间,并设置一个名为 “example” 的自定义变量到 Gin 上下文中。然后,它会调用c.Next()执行下一个处理函数,即路由处理函数。- 路由处理函数会从 Gin 上下文中获取 “example” 变量,并将其打印到日志中。
- 当路由处理函数执行完毕后,控制权会返回到
Logger()中间件,它会计算请求耗时,并将请求耗时和响应状态码打印到日志中。- 最后,HTTP 服务器会根据定义的路由引擎
r处理请求,并在指定的地址和端口上监听。
10、自定义验证器
可能有问题。略
(1)完整代码
1 | |
(2)知识点
type Booking struct: 定义了一个结构体类型Booking,用于存储从请求中绑定和验证后的数据。
var bookableDate validator.Func: 定义了一个自定义验证器函数bookableDate,用于验证日期是否可预订。该函数会在后续的验证中使用。
binding.Validator.Engine(): 获取 Gin 框架中的验证器引擎实例,用于注册自定义验证器函数。
v.RegisterValidation("bookabledate", bookableDate): 使用v.RegisterValidation()方法将自定义验证器函数bookableDate注册到 Gin 的验证器引擎中,并指定了验证器的名称为 “bookabledate”。这样,在后续的验证中就可以通过该名称来使用这个自定义验证器函数。
var bookableDate validator.Func: 定义了一个自定义验证器函数bookableDate,用于验证日期是否可预订。该函数会在后续的验证中使用。
c.ShouldBindWith(&b, binding.Query): 在getBookable处理函数中,使用c.ShouldBindWith()方法将请求中的数据绑定到b变量,并使用binding.Query标签指定了从查询参数中获取数据。这样,请求中的数据就会被绑定到Booking结构体的字段,并且会进行后续的验证。
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}): 在验证失败时,使用c.JSON()方法将一个 JSON 格式的错误响应发送给客户端,其中包含了一个键值对,键为 “error”,值为验证失败的错误信息。HTTP 状态码被设置为http.StatusBadRequest,表示请求参数错误。自定义验证器函数,用于验证日期是否可预订,具体实现如下:
func(fl validator.FieldLevel) bool: 这是一个函数类型,参数为validator.FieldLevel,返回值为bool。validator.FieldLevel是一个接口,用于在验证器中获取字段的相关信息,如字段的值、字段的标签等。fl.Field().Interface().(time.Time): 使用fl.Field()方法获取字段的值,并使用Interface()方法将其转换为interface{}类型。然后使用类型断言将其转换为time.Time类型的值。这样,我们可以在函数中使用字段的实际值进行验证。today := time.Now(): 使用time.Now()函数获取当前的时间,并将其赋值给today变量,表示当前的日期时间。if today.After(date) { return false }: 使用today.After()方法比较当前时间和字段值的时间,如果当前时间晚于字段值的时间,则返回false,表示验证失败,日期不可预订。return true: 如果验证通过,即当前时间不晚于字段值的时间,返回true,表示日期可预订。总的来说,这个函数的实现逻辑是通过比较当前时间和字段值的时间来判断日期是否可预订,如果当前时间晚于字段值的时间,则认为日期不可预订,返回
false,否则返回true。
(3)示例大意
- 定义了一个
Booking结构体,包含了两个字段CheckIn和CheckOut,分别表示入住日期和离店日期。这两个字段使用了 Gin 的form标签指定了它们在请求参数中的名称,并使用了binding标签来指定了验证规则。- 定义了一个名为
bookableDate的自定义验证器函数,用于验证日期是否可预订。这个函数在验证器中注册为bookabledate,并在验证过程中获取字段的实际值,比较当前时间和字段值的时间来判断日期是否可预订。- 在
main函数中,创建了一个 Gin 的默认引擎,用于处理 HTTP 请求。然后使用binding.Validator.Engine()方法获取到验证器引擎,并使用v.RegisterValidation()方法注册了自定义验证器函数bookableDate。- 定义了一个名为
getBookable的处理函数,用于处理/bookable路由的 GET 请求。在这个处理函数中,首先创建了一个Booking结构体实例b,然后使用c.ShouldBindWith()方法将请求参数绑定到b上,并进行验证。如果验证通过,则返回 HTTP 状态码为 200 的 JSON 响应,表示预订日期有效;否则,返回 HTTP 状态码为 400 的 JSON 响应,表示验证失败并返回错误信息。- 启动了 Gin 服务,并监听在 8085 端口上。
11、定义路由日志的格式
(1)完整代码
1 | |
(2)知识点
gin.DebugPrintRouteFunc: 这是 Gin 框架中的一个变量,用于设置在控制台打印路由信息的函数。您在代码中通过给该变量赋值一个函数来自定义路由信息的打印方式。在您的示例中,设置了一个自定义的打印函数,用于在控制台输出 HTTP 方法、URL 路径、处理函数名和处理函数的数量。其中,httpMethod是 HTTP 请求方法(如 “GET”、”POST” 等),absolutePath是路由的完整路径,handlerName是处理函数的名称,nuHandlers是处理函数的数量,通过调用log.Printf函数将路由调试打印的信息输出到控制台。r.GET("/bar", func(c *gin.Context) { ... }): 这是 Gin 框架中的一个方法,用于定义一个处理 HTTP GET 请求的路由。在这里,您定义了一个处理/bar路径的 GET 请求的路由,并传入一个匿名函数作为处理函数。可以在浏览器中输入网址[localhost:8080/bar](http://localhost:8080/bar)访问。r.POST("/foo", func(c *gin.Context) { ... }): 这是 Gin 框架中的一个方法,用于定义一个处理 HTTP POST 请求的路由。在这里,您定义了一个处理/foo路径的 POST 请求的路由,并传入一个匿名函数作为处理函数。这个匿名函数会在请求到达时被调用,其中c *gin.Context是处理函数的参数,表示当前请求的上下文。不能在浏览器中直接输入网址访问,因为这里的是POST请求,需要在客户端输入curl -X POST http://localhost:8080/foo或者在 PowerShell 中发送 HTTP 请求并指定请求方法访问Invoke-RestMethod -Uri 'http://localhost:8080/foo' -Method Post
(3)示例大意
- 导入了所需的包,包括日志处理、HTTP 相关的包以及 Gin 框架的包。
- 在
main函数中创建了一个 Gin 实例r,作为路由引擎。- 使用
gin.DebugPrintRouteFunc函数设置了一个自定义的路由调试打印函数,用于在控制台打印路由信息。- 定义了一个处理 POST 请求的路由
/foo,当该路由接收到 POST 请求时,会执行传入的匿名函数,该函数会在 HTTP 响应中返回 JSON 格式的响应 “foo”。- 定义了一个处理 GET 请求的路由
/bar,当该路由接收到 GET 请求时,会执行传入的匿名函数,该函数会在 HTTP 响应中返回 JSON 格式的响应 “bar”。- 定义了一个处理 GET 请求的路由
/status,当该路由接收到 GET 请求时,会执行传入的匿名函数,该函数会在 HTTP 响应中返回 JSON 格式的响应 “ok”。- 调用
r.Run()启动 HTTP 服务,监听地址为 0.0.0.0:8080。
12、在中间件中使用 Goroutine
(1)完整代码
1 | |
(2)知识点
c.Copy():创建一个上下文的副本,用于在 goroutine 中使用。在多个 goroutine 中处理请求时,需要使用上下文的副本,以避免竞态条件和并发访问的问题。go func() { ... }():这段代码是一个匿名的 Go 语言协程(goroutine),通过go关键字开启一个新的并发执行的函数。这个函数中的逻辑很简单,首先通过time.Sleep(5 * time.Second)模拟一个耗时的操作,即暂停当前 goroutine 的执行 5 秒钟。在这个暂停期间,当前的 goroutine 不会继续执行,而是让出 CPU 时间给其他 goroutine,从而实现了并发执行。当time.Sleep()结束后,该 goroutine 会继续执行剩下的代码。这段代码通过调用log.Println()在日志中打印一条消息,内容为 “Done! int path “ + cCp.Request.URL.Path。其中,cCp是在创建这个 goroutine 时通过c.Copy()创建的当前请求上下文的副本,用于记录请求的路径。time.Sleep():模拟一个长时间运行的任务,暂停当前 goroutine 的执行一段时间,以模拟耗时的操作。在这段代码中,使用time.Sleep(5 * time.Second)暂停当前 goroutine 的执行 5 秒。c.Request.URL.Path:获取当前请求的路径,用于在日志中记录处理请求的路径。- 通过分别访问
long_async目录和long_sync目录,会发现:前者每次刷新页面,后台就会立即打印日志,过五秒后才会打印输出路径;反观后者,每次刷新页面需要过五秒,后台才会同时输出日志和打印路径。long_async当前的 goroutine 不会继续执行,而是让出 CPU 时间给其他 goroutine,从而实现了并发执行。
(3)示例大意
- 使用
gin.Default()创建了一个默认的 Gin 引擎r,用于处理 HTTP 请求和响应。- 定义了两个路由:
/long_async路由,使用r.GET方法注册了一个处理 GET 请求的处理函数。在这个处理函数中,通过c.Copy()创建了一个上下文副本cCp,然后使用go关键字启动了一个 goroutine,在 goroutine 中使用time.Sleep()模拟了一个长时间运行的任务,最后在日志中打印了请求路径信息。/long_sync路由,同样使用r.GET方法注册了一个处理 GET 请求的处理函数。在这个处理函数中,也使用了time.Sleep()模拟了一个长时间运行的任务,但没有使用 goroutine,因此不需要拷贝上下文,直接通过c访问了请求路径信息,并在日志中打印了请求路径信息。- 使用
r.Run(":8080")启动了一个 web 服务器,监听在本地的 8080 端口上,等待接收来自客户端的请求。
13、优雅地重启或停止
(1)完整代码
1 | |
(2)知识点
srv := &http.Server{ Addr: ":8080", Handler: router, }创建了一个http.Server实例,用于配置 HTTP 服务器的监听地址和请求处理器.
Addr: ":8080":指定了服务器监听的地址和端口号。在这里,服务器会监听在本地的 8080 端口上,表示可以通过访问http://localhost:8080来与服务器进行通信。Handler: router:指定了服务器的请求处理器。在这里,使用了 Gin 框架创建的路由实例router作为服务器的请求处理器。Gin 路由可以用于注册不同的 HTTP 请求处理函数,根据请求的 URL 路径和 HTTP 方法来进行路由和处理。srv.ListenAndServe():这是http.Server的方法,用于启动服务器并开始监听和处理请求。它会一直在后台运行,直到服务器关闭或发生错误。err := srv.ListenAndServe():将srv.ListenAndServe()的返回值赋给变量err,用于捕获可能发生的错误。err != nil && err != http.ErrServerClosed:通过条件判断语句检查err是否不为空且不等于http.ErrServerClosed,即判断是否有错误发生且错误不是因为服务器被关闭导致的。log.Fatalf("listen: %s\n", err):如果有错误发生,使用log.Fatalf()方法将错误信息格式化并输出到标准错误(stderr),然后调用log.Fatal()方法终止程序的运行。log.Fatalf()是log包的一个函数,它会在输出错误信息后直接调用os.Exit(1)终止程序。quit := make(chan os.Signal):创建了一个用于接收信号的通道quit,该通道的类型是os.Signal,用于接收操作系统发送的信号。signal.Notify(quit, os.Interrupt):通过signal包的Notify函数,将os.Interrupt(表示中断信号,通常由用户在终端中按下 Ctrl+C 产生)注册到quit通道,表示当收到中断信号时,将向quit通道发送该信号。<-quit:通过从quit通道中接收信号,代码会一直阻塞在这里,直到接收到中断信号。一旦收到中断信号,代码会继续执行下一行。ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second):通过context.WithTimeout函数创建了一个带有超时的上下文ctx,超时时间设置为 5 秒。context.Background()返回一个空的上下文作为父上下文。defer cancel():使用defer语句延迟调用cancel()函数,确保在函数执行完毕后取消上下文,以避免资源泄漏。if err := srv.Shutdown(ctx); err != nil:调用srv.Shutdown方法来优雅地关闭服务器,传入之前创建的带有超时的上下文ctx。该方法会等待服务器处理完当前的请求后再关闭服务器,以实现优雅关闭。log.Fatal("Server Shutdown:", err):如果服务器关闭过程中发生错误,使用log.Fatal输出错误信息并终止程序运行。
(3)示例大意
首先,创建了一个默认的 Gin 路由实例,注册了一个处理函数到 “/“ 路径的 GET 请求,其中模拟了一个长任务,暂停 5 秒,然后返回 “Welcome Gin Server” 字符串作为响应。
接着,创建了一个 http.Server 实例,配置了监听地址为 “:8080”,并将 Gin 路由作为处理器。
然后,通过启动一个 goroutine 来监听和处理 HTTP 请求,调用
srv.ListenAndServe()方法来启动 HTTP 服务器。接下来,通过使用
os/signal包来等待中断信号(如 Ctrl+C),一旦接收到中断信号,服务器将开始优雅地关闭。创建了一个超时上下文
ctx,设置了 5 秒的超时时间,并通过srv.Shutdown()方法来优雅地关闭服务器,等待未完成的请求完成或超时。最后,使用
log.Fatal()输出错误信息并终止程序运行,或者输出 “Server exiting” 消息表示服务器成功退出。
14、路由组
(1)完整代码
1 | |
(2)知识点
router.Group("/v1"){...}: 这是创建了一个名为 “/v1” 的路由组。路由组可以用来对一组路由进行分组,方便管理和配置共享的中间件。
(3)示例大意
func main(): 这是 Go 语言的入口函数,是程序的入口点。router := gin.Default(): 创建了一个默认的 Gin 路由引擎实例。gin.Default()函数返回一个默认配置的 Gin 路由引擎,包含了常用的中间件,如日志、恢复等。v1 := router.Group("/v1"): 创建了一个名为 “/v1” 的路由组。路由组用于将多个路由进行分组管理,可以对这些路由应用相同的中间件、路由组级别的处理函数等。v1.POST("/login", loginEndpoint): 在 “/v1” 路由组下定义了一个 HTTP POST 方法的路由 “/login”,并将该路由与名为 loginEndpoint 的处理函数绑定。这表示当客户端发送 HTTP POST 请求到 “/v1/login” 时,将调用 loginEndpoint 函数来处理该请求。v1.POST("/submit", submitEndpoint): 在 “/v1” 路由组下定义了一个 HTTP POST 方法的路由 “/submit”,并将该路由与名为 submitEndpoint 的处理函数绑定。这表示当客户端发送 HTTP POST 请求到 “/v1/submit” 时,将调用 submitEndpoint 函数来处理该请求。v1.POST("/read", readEndpoint): 在 “/v1” 路由组下定义了一个 HTTP POST 方法的路由 “/read”,并将该路由与名为 readEndpoint 的处理函数绑定。这表示当客户端发送 HTTP POST 请求到 “/v1/read” 时,将调用 readEndpoint 函数来处理该请求。v2 := router.Group("/v2"): 创建了一个名为 “/v2” 的路由组,类似于上面的 “/v1” 路由组。v2.POST("/login", loginEndpoint): 在 “/v2” 路由组下定义了一个 HTTP POST 方法的路由 “/login”,并将该路由与名为 loginEndpoint 的处理函数绑定。v2.POST("/submit", submitEndpoint): 在 “/v2” 路由组下定义了一个 HTTP POST 方法的路由 “/submit”,并将该路由与名为 submitEndpoint 的处理函数绑定。v2.POST("/read", readEndpoint): 在 “/v2” 路由组下定义了一个 HTTP POST 方法的路由 “/read”,并将该路由与名为 readEndpoint 的处理函数绑定。router.Run(":8080"): 启动 HTTP 服务器并监听在端口 8080。一旦服务器启动,就会开始接受来自客户端的请求,并根据定义的路由规则和处理函数来处理这些请求。
15、如何记录日志
(1)完整代码
1 | |
(2)知识点
gin.DisableConsoleColor(): 禁用控制台颜色输出,这样在将日志写入文件时不会包含控制台颜色信息。
f, _ := os.Create("gin.log"): 创建一个名为 “gin.log” 的文件用于记录日志,并将文件对象保存在变量 f 中。如果创建文件出现错误,错误会被忽略。
gin.DefaultWriter = io.MultiWriter(f): 将文件 f 设置为 Gin 框架的默认日志写入器,这样日志将同时写入文件 f。
1
2
3
4
5
6
7wd, err := os.Getwd()
if err != nil {
fmt.Println("获取当前工作目录失败:", err)
return
}
logFilePath := wd + "/gin.log"
fmt.Println("gin.log 文件路径:", logFilePath)可以用这段代码获取当前工作目录,
gin.log文件就在该路径下。
(3)示例大意
- 调用
gin.DisableConsoleColor()禁用控制台颜色输出,因为日志将被写入文件,不需要控制台颜色。- 调用
os.Create("gin.log")创建一个名为 “gin.log” 的文件,用于记录日志,并将返回的文件对象赋值给变量f。- 将
f设置为 Gin 框架的默认日志写入器,通过调用gin.DefaultWriter = io.MultiWriter(f)。这样,Gin 框架的日志将同时写入gin.log文件和控制台(如果需要同时写入文件和控制台,可以注释掉当前行,将下一行的注释取消)。- 调用
gin.Default()创建一个默认的 Gin 路由引擎实例,并将返回的引擎对象赋值给变量router。- 使用
router.GET("/ping", ...)定义一个 HTTP GET 方法的路由 “/ping”,并传入一个请求处理函数,该函数会在收到请求时返回字符串 “pong”。- 调用
router.Run(":8080")启动 HTTP 服务器,并监听在端口 8080,等待接收客户端请求。一旦接收到请求,就会调用之前定义的请求处理函数进行处理。
16、HTML渲染
(1)完整代码
(2)代码解释
17、HTTP2 server 推送
(1)完整代码
1 | |
(2)知识点
var html = template.Must(template.New("https").Parse(...)): 定义了一个名为html的 HTML 模板。template.Must是一个辅助函数,用于将模板解析为template.Template对象,并在解析过程中检查是否出错。r.Static("/assets", "./assets"): 将静态文件夹"./assets"注册为 “/assets” 的静态资源路径,这样当客户端请求 “/assets/app.js” 时,会返回位于本地文件系统的 “./assets/app.js” 文件作为响应。r.SetHTMLTemplate(html): 设置之前定义的html模板为 Gin 框架的 HTML 模板。r.GET("/", func(c *gin.Context) { ... }): 定义一个 HTTP GET 方法的路由 “/“,并传入一个请求处理函数,该函数会在收到请求时执行。在这个例子中,请求处理函数中通过c.Writer.Pusher()检查是否支持服务器推送。如果支持,通过pusher.Push("/assets/app.js", nil)进行服务器推送,推送的资源路径为 “/assets/app.js”。然后使用c.HTML()返回之前定义的 HTML 模板,并设置状态码为 200 和数据为gin.H{"status": "success"}。r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key"): 启动 HTTP 服务器,并监听在 8080 端口,并使用 TLS 加密进行安全通信。第二个参数 “./testdata/server.pem” 是 TLS 证书文件的路径,第三个参数 “./testdata/server.key” 是 TLS 私钥文件的路径,用于在服务器端进行 TLS 握手和加密通信。这样,服务器将通过 HTTPS 监听客户端的请求。
(3)示例大意
- 定义 HTML 模板:使用
template.Must函数创建了一个名为 “https” 的 HTML 模板,用于返回给客户端。- 创建 Gin 路由引擎实例:使用
gin.Default()函数创建了一个默认的 Gin 路由引擎实例。- 注册静态文件路径:使用
r.Static()方法将静态文件夹 “./assets” 注册为 “/assets” 的静态资源路径,使得客户端可以通过访问 “/assets” 路径来获取静态文件。- 设置 HTML 模板:使用
r.SetHTMLTemplate()方法设置了之前定义的 HTML 模板。- 定义根路由处理函数:使用
r.GET()方法定义了一个 HTTP GET 方法的根路由处理函数,该处理函数会返回之前定义的 HTML 模板,并在响应头中设置了支持服务器推送的信息。- 启动 HTTPS 服务器:使用
r.RunTLS()方法在 “https://127.0.0.1:8080“ 地址上启动了一个监听 TLS 加密的 HTTPS 服务器,使用了之前定义的 TLS 证书和私钥文件。
18、 JSONP
使用 JSONP 向不同域的服务器请求数据。如果查询参数存在回调,则将回调添加到响应体中。
(1)完整代码
1 | |
(2)知识点
c.Query("callback"):c.Query方法用于获取 HTTP 请求中的查询参数。这里使用c.Query("callback")获取了名为 “callback” 的查询参数的值c.JSONP(http.StatusOK, gin.H{...}):c.JSONP方法用于将 JSON 数据以 JSONP 格式返回给客户端。http.StatusOK表示 HTTP 响应状态码为 200,gin.H{...}表示要返回的 JSON 数据。这里返回了一个包含 “callback” 和 “data” 两个字段的 JSON 数据,其中 “callback” 字段的值为上一步获取的查询参数 “callback” 的值,”data” 字段的值为一个包含 “foo” 字段的 map 数据。
19、Multipart/Urlencoded 绑定
(1)完整代码
1 | |
(2)知识点
type LoginForm struct { ... }:LoginForm是一个自定义的结构体类型,用于表示登录表单的数据结构。结构体中包含了两个字段:User和Password,分别对应表单中的 “user” 和 “password” 字段。router.POST("/login", func(c *gin.Context) { ... }):router.POST方法注册了一个 HTTP POST 方法的路由,路由路径为 “/login”。当客户端发送 HTTP POST 请求到 “/login” 路径时,Gin 会调用传入的匿名函数作为该路由的处理函数。c.ShouldBind(&form):c.ShouldBind方法用于将 HTTP 请求中的数据绑定到指定的结构体对象。这里使用c.ShouldBind(&form)将请求数据绑定到form变量,该变量是上一步定义的LoginForm结构体对象。c.JSON(200, gin.H{...}):c.JSON方法用于将 JSON 数据作为响应发送给客户端。200表示 HTTP 响应状态码为 200,gin.H{...}表示要返回的 JSON 数据。这里返回了一个包含 “status” 字段的 JSON 数据,字段的值根据登录表单的用户名和密码进行判断而定。c.JSON(401, gin.H{...}): 同样地,c.JSON方法也可以用于返回 HTTP 响应状态码为 401 的错误响应。这里通过c.JSON(401, gin.H{"status": "unauthorized"})和c.JSON(401, gin.H{"status": "missing params"})分别返回了未授权和缺少必填字段的错误信息。
(3)示例大意
- 定义了一个 LoginForm 结构体,用于绑定 POST 请求中的表单数据。该结构体包含了两个字段,分别是 User 和 Password,使用了 Gin 框架的表单绑定标签进行验证,要求这两个字段都是必填的。
- 在 main 函数中创建了一个 Gin 路由实例 router。
- 设置了一个 POST 路由 “/login”,并定义了处理函数。在该处理函数中,使用 c.ShouldBind 方法将请求数据绑定到 LoginForm 结构体。
- 如果绑定成功,即表单数据满足要求,那么进一步判断用户名和密码是否正确,如果正确,则返回 HTTP 状态码 200 和一个 JSON 格式的成功消息;否则,返回 HTTP 状态码 401 和一个 JSON 格式的未授权消息。
- 如果请求数据缺少必填字段,即绑定失败,那么返回 HTTP 状态码 401 和一个 JSON 格式的缺少参数消息。
- 最后,通过调用 router.Run(“:8080”) 启动 HTTP 服务器并监听在端口 8080,等待客户端请求。
20、表单
(1)完整代码
1 | |
(2)知识点
c.PostForm("message"): 使用c上下文对象的PostForm方法获取表单中名为 “message” 的字段的值。c.DefaultPostForm("nick", "anonymous"): 使用c上下文对象的DefaultPostForm方法获取表单中名为 “nick” 的字段的值,如果该字段为空,则返回默认值 “anonymous”。
(3)示例大意
- 导入了 “github.com/gin-gonic/gin” 包,引入 Gin 框架的依赖。
- 创建了一个 Gin 路由引擎,使用了默认的中间件。
- 定义了一个处理 POST 请求的路由 “/form_post”,并传入一个处理函数。
- 在处理函数中,使用了 c.PostForm 方法获取表单中的 “message” 字段值,并保存到变量 message 中。
- 使用了 c.DefaultPostForm 方法获取表单中的 “nick” 字段值,并保存到变量 nick 中,如果 “nick” 字段为空,则默认值为 “anonymous”。
- 使用了 c.JSON 方法返回 JSON 格式的响应,包含了三个键值对: “status”、”message” 和 “nick”。
- 调用 router.Run 方法启动 HTTP 服务器并监听在端口 8080,等待客户端的请求。
21、只绑定 url 查询字符串
(1)完整代码
1 | |
(2)代码解释
这段代码使用了 Gin 框架创建了一个 HTTP 服务器,并定义了一个路由处理函数
startPage用于处理 “/testing” 的请求,可以处理 GET、POST、PUT、DELETE 等任何请求方法。在路由处理函数中,通过c.ShouldBindQuery()方法将请求中的 Query String 绑定到person对象,其中person.Name和person.Address分别对应了请求中的 “name” 和 “address” 参数。接着使用
log.Println()方法输出绑定后的字段值到控制台。最后,通过调用
c.String()方法返回状态码 200 和 “Success” 字符串作为响应,表示处理成功。HTTP 服务器监听在端口 8085,并通过router.Run()启动。
22、路径中的参数
(1)完整代码
1 | |
(2)代码解释
这段代码使用了 Gin 框架创建了一个 HTTP 服务器,并定义了两个路由处理函数。第一个处理函数通过路由 “/user/:name” 匹配 URL 中的 “:name” 参数,并将其值绑定到变量 name 中,然后通过
c.String()方法返回 “Hello %s” 字符串,其中 %s 会被 name 的值替代。第二个处理函数通过路由 “/user/:name/*action” 匹配 URL 中的 “:name” 参数和 “*action” 参数,”*action” 表示匹配 “/user/:name” 后面的任意路径,将其值绑定到变量 name 和 action 中,然后通过
c.String()方法返回包含 name 和 action 的消息字符串。在 Gin 框架中,路由参数可以通过
:paramName的方式定义,表示在 URL 中匹配该位置的任意字符串,并将其值作为参数传递给路由处理函数。同时,Gin 还支持通配符 “*”,可以匹配任意路径片段,用于处理较为复杂的路由需求。
23、PureJSON
通常,JSON 使用 unicode 替换特殊 HTML 字符,例如 < 变为 \ u003c。如果要按字面对这些字符进行编码,则可以使用 PureJSON。Go 1.6 及更低版本无法使用此功能。
(1)完整代码
1 | |
(2)代码解释
这段代码使用了 Gin 框架创建了一个 HTTP 服务器,并定义了三个路由处理函数。其中
/json路由使用c.JSON()方法返回 JSON 格式的响应体,其中包含一个名为 “html” 的字段,值为 “Hello, world!“。c.JSON()方法会将响应体序列化为 JSON 格式并设置相应的 Content-Type 头,使得返回的响应体在客户端以 JSON 格式展示。而
/purejson路由使用c.PureJSON()方法同样返回 JSON 格式的响应体,但不会对响应体中的特殊字符进行转义,保留原始字符。这在某些情况下可能有用,例如当需要返回包含 HTML 标签等特殊字符的响应体时。最后,通过调用
r.Run()方法在 0.0.0.0:8080 上启动 HTTP 服务器并监听请求。Gin 框架的Run()方法会自动选择可用的端口并启动服务器。
24、POST 表单中的 URL 参数
1 | |
(1)完整代码
1 | |
(2)代码解释
这段代码使用了 Gin 框架创建了一个 HTTP 服务器,并定义了一个处理 POST 请求的路由处理函数。该处理函数从请求中获取 URL 查询参数 “id” 和 “page”,以及 POST 表单字段 “name” 和 “message” 的值,并将其打印输出。
使用
c.Query()方法可以获取 URL 查询参数的值,其中参数为参数名。而使用c.PostForm()方法可以获取 POST 表单字段的值,同样参数为字段名。如果 POST 表单中没有指定的字段,可以使用c.DefaultPostForm()方法设置一个默认值。最后,通过调用
router.Run()方法在 “:8080” 上启动 HTTP 服务器并监听请求。Gin 框架的Run()方法会自动选择可用的端口并启动服务器。
25、查询字符串参数
(1)完整代码
1 | |
(2)代码解释
这段代码使用了 Gin 框架创建了一个 HTTP 服务器,并定义了一个处理 GET 请求的路由处理函数。该处理函数从 URL 查询参数 “firstname” 和 “lastname” 的值,并将其组合成字符串后作为响应返回。
使用
c.Query()方法可以获取 URL 查询参数的值,其中参数为参数名。而使用c.DefaultQuery()方法可以获取 URL 查询参数的值,并设置一个默认值,当查询参数不存在时使用。这在示例中用于获取 “firstname” 的值,并设置默认值为 “Guest”。Gin 框架的
Run()方法会自动选择可用的端口并启动 HTTP 服务器,通过调用router.Run(":8080")在 “:8080” 上启动 HTTP 服务器并监听请求。
26、重定向
1 | |
(1)完整代码
1 | |
(2)代码解释
当访问
/test时,请求的 URL 路径被修改为/test2,然后再次处理请求。由于/test2路径有对应的路由处理函数,所以会返回{"hello": "world"}JSON 响应。
27、运行多个服务
(1)完整代码
1 | |
(2)代码解释
这段代码使用 Gin 框架创建了两个独立的 HTTP 服务器,分别监听不同的端口。
router01和router02函数分别定义了两个 Gin 路由处理器,分别处理根路径的 GET 请求,并返回不同的 JSON 响应。通过
errgroup包管理两个 Goroutine,分别启动了server01和server02的监听操作。每个服务器都使用了不同的监听地址和端口,以及不同的路由处理器。如果有错误发生,
errgroup.Wait()方法会等待两个 Goroutine 结束,并返回错误。如果返回的错误不为空,则使用log.Fatal()输出错误日志并终止程序运行。这段代码的主要作用是创建并同时启动两个独立的 HTTP 服务器,它们分别监听不同的端口,并使用 Gin 框架处理路由请求。使用
errgroup包可以方便地管理多个 Goroutine,并在任何一个 Goroutine 发生错误时及时捕获和处理。这种方式可以有效地提高服务器的可靠性和稳定性。
28、SecureJSON
使用 SecureJSON 防止 json 劫持。如果给定的结构是数组值,则默认预置
"while(1),"到响应体。
(1)完整代码
1 | |
(2)代码解释
这段代码使用 Gin 框架创建一个 HTTP 服务器,并定义了一个路由处理函数。代码的主要逻辑如下:
- 使用
gin.Default()创建一个默认的 Gin 实例r,它包含了一些常用的中间件,如日志、恢复等。- 注册一个处理路由
/someJSON的 GET 请求的处理函数。该处理函数接收一个gin.Context对象c,用于处理 HTTP 请求和构建 HTTP 响应。- 在处理函数中,创建一个字符串切片
names,包含了三个字符串元素。- 使用
c.SecureJSON(http.StatusOK, names)将names切片作为 JSON 响应返回给客户端。这里使用了http.StatusOK表示 HTTP 响应的状态码为 200 OK,并且在返回的 JSON 数据前加上了安全前缀,防止 JSON 劫持攻击。- 调用
r.Run(":8080")启动 HTTP 服务器,监听在 0.0.0.0:8080 地址上。这段代码的功能是创建一个简单的 HTTP 服务器,当客户端通过
/someJSON路径发送 GET 请求时,返回一个包含三个字符串元素的 JSON 数组作为响应。服务器监听在 0.0.0.0:8080 地址上,并使用 Gin 框架处理路由和构建 HTTP 响应。
29、从 reader 读取数据
(1)完整代码
1 | |
(2)代码解释
这段代码使用了 Gin 框架创建了一个 HTTP 服务器,并注册了一个处理路由 “/someDataFromReader” 的 GET 请求的处理函数。
gin.Default()创建了一个默认的 Gin 路由引擎。router.GET("/someDataFromReader", func(c *gin.Context) { ... })定义了处理路由 “/someDataFromReader” 的 GET 请求的处理函数。函数体中的代码会在接收到 “/someDataFromReader” 的 GET 请求时执行。http.Get("https://raw.githubusercontent.com/gin-gonic/logo/master/color.png")发起了一个 HTTP GET 请求,获取远程文件的内容。如果请求出错或者响应状态码不是 200 OK,会返回 503 Service Unavailable 状态码给客户端。c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)使用 Gin 提供的c.DataFromReader方法将文件数据作为响应返回给客户端。http.StatusOK是响应的状态码,contentLength是文件的长度,contentType是文件的 MIME 类型,reader是文件的内容,extraHeaders是一些额外的响应头,这里设置了 Content-Disposition 头,指定文件名为 “gopher.png”。router.Run(":8080")启动 HTTP 服务器并监听在 “:8080” 地址上,开始接受客户端的请求。总体来说,这段代码实现了一个简单的 HTTP 服务器,通过处理 GET 请求获取远程文件内容,并将文件数据作为响应返回给客户端,同时设置了文件的 Content-Disposition 头,指定文件名为 “gopher.png”。
30、 静态文件服务
(1)完整代码
1 | |
(2)代码解释
以上代码使用 Gin 框架创建了一个简单的静态文件服务器。通过注册静态文件路由,可以将指定目录下的静态文件返回给客户端,例如图片、CSS、JavaScript 等资源文件,以供客户端的浏览器进行访问和加载。这对于构建前端应用或者提供静态资源文件的需求非常有用。在代码中使用了不同的静态文件路由注册方式,包括
router.Static()、router.StaticFS()和router.StaticFile(),用于处理不同的静态文件请求。最后,通过调用router.Run(":8080")启动 HTTP 服务器,监听地址为 “0.0.0.0:8080”,并开始接收并处理客户端的请求。
31、 设置和获取 Cookie
(1)完整代码
1 | |
(2)代码解释
以上代码使用 Gin 框架创建了一个简单的 HTTP 服务器,并注册了一个 “/cookie” 路由的 GET 方法处理函数。在处理函数中,通过
c.Cookie("gin_cookie")获取名为 “gin_cookie” 的 Cookie 值,并在控制台打印出来。如果获取 Cookie 发生错误(例如不存在),则设置一个默认值,并通过c.SetCookie()方法设置名为 “gin_cookie” 的 Cookie 值为 “test”,过期时间为 3600 秒,路径为 “/“,域名为 “localhost”,不限制 HTTPS 连接,允许 JS 访问。最后,通过调用router.Run()启动 HTTP 服务器,监听默认的地址和端口(0.0.0.0:8080),并开始接收并处理客户端的请求。
32、支持 Let’s Encrypt
(1)完整代码
一行代码支持 LetsEncrypt HTTPS servers 示例。
1 | |
自定义 autocert manager 示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28package main
import (
"log"
"github.com/gin-gonic/autotls"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/acme/autocert"
)
func main() {
r := gin.Default() // 创建一个 Gin 路由引擎实例
// 注册 "/ping" 路由的 GET 方法处理函数
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
// 创建一个 Autocert 管理器,用于配置 Let's Encrypt 自动证书管理
m := autocert.Manager{
Prompt: autocert.AcceptTOS, // 接受服务条款
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), // 允许的域名白名单
Cache: autocert.DirCache("/var/www/.cache"), // 证书缓存目录
}
// 使用 Autotls 启动 HTTP 服务器并监听域名 "example1.com" 和 "example2.com",并配置自动证书管理器
log.Fatal(autotls.RunWithManager(r, &m))
}
(2)代码解释
以上代码使用 Gin 框架创建了一个简单的 HTTP 服务器,并注册了一个 “/ping” 路由的 GET 方法处理函数,用于处理 “/ping” 路径的请求,并返回 “pong” 字符串。然后,通过调用
autotls.Run()方法使用 Autotls 启动 HTTP 服务器并监听域名 “example1.com” 和 “example2.com”,这将自动为这两个域名配置 HTTPS,并使用 Let’s Encrypt 证书进行 TLS 加密。如果启动服务器出现错误,会通过log.Fatal()方法记录并输出错误信息,并终止程序的运行。
以上代码使用 Gin 框架创建了一个简单的 HTTP 服务器,并注册了一个 “/ping” 路由的 GET 方法处理函数,用于处理 “/ping” 路径的请求,并返回 “pong” 字符串。然后,通过创建一个 Autocert 管理器来配置 Let’s Encrypt 自动证书管理,包括接受服务条款、允许的域名白名单和证书缓存目录等参数。最后,通过调用
autotls.RunWithManager()方法使用 Autotls 启动 HTTP 服务器并监听域名 “example1.com” 和 “example2.com”,并配置自动证书管理器。如果启动服务器出现错误,会通过log.Fatal()方法记录并输出错误信息,并终止程序的运行。
33、将 request body 绑定到不同的结构体中
(1)完整代码
一般通过调用
c.Request.Body方法绑定数据,但不能多次调用这个方法。
1 | |
(2)代码解释
以上代码是一个使用 Gin 框架的 HTTP 请求处理函数 SomeHandler,它通过两次调用 c.ShouldBind() 方法尝试将请求体绑定到两个不同的结构体对象 formA 和 formB。首先尝试将请求体绑定到 formA 对象,如果绑定成功,则返回一个字符串 “the body should be formA”。如果绑定失败,则尝试将请求体绑定到 formB 对象,如果绑定成功,则返回一个字符串 “the body should be formB”。如果两次绑定都失败,则执行其他逻辑。需要注意的是,c.ShouldBind() 方法使用了 c.Request.Body,一旦读取,请求体将变为 EOF,无法再次使用,因此在使用该方法时需要注意不可重用的特性。
34、 单文件上传
(1)完整代码
1 | |
(2)代码解释
- 首先,创建了一个默认的 Gin 路由引擎
router := gin.Default()。- 设置了一个上传文件的接口路由为 “/upload”,并使用 POST 方法处理文件上传请求:
router.POST("/upload", func(c *gin.Context) {...})。这里使用了匿名函数作为路由处理函数,接收一个c *gin.Context参数,用于处理 HTTP 请求和响应。- 在路由处理函数中,通过
c.FormFile("file")获取上传的文件对象。c.FormFile方法是 Gin 框架提供的用于获取上传文件的方法,其中 “file” 是上传文件的字段名。如果没有找到对应的文件字段,会返回错误。- 使用
log.Println(file.Filename)打印上传文件的文件名。这里只是简单地将文件名打印到控制台,实际应用中可能需要根据需求进行文件名的处理。- 可以通过
c.SaveUploadedFile(file, dst)将文件保存到指定目录。其中file是上传的文件对象,dst是目标保存路径。在实际应用中,需要根据需求设置合适的目标保存路径,并处理文件名重复等情况。- 使用
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))返回上传成功的信息。这里使用了 Gin 框架提供的c.String方法,设置 HTTP 响应状态码为 200,返回一个字符串作为响应体,其中包含上传文件的文件名。- 最后,通过
router.Run(":8080")启动 Gin 服务器,监听在 8080 端口,等待接收文件上传请求。
35、 多文件上传
(1)完整代码
1 | |
(2)代码解释
- 创建 Gin 路由引擎:通过
gin.Default()创建了一个默认的 Gin 路由引擎,用于处理 HTTP 请求。- 设置文件上传接口:使用
router.POST("/upload", ...)将文件上传接口设置为 “/upload”,并指定 HTTP 方法为 POST。这意味着该接口将处理客户端发送的 POST 请求。- 获取上传的 multipart form 数据:在接口的处理函数中,使用
c.MultipartForm()方法获取上传的 multipart form 数据,其中c是*gin.Context类型的请求上下文对象。- 获取上传的文件对象列表:从
multipart.Form结构体中的File字段获取上传的文件对象列表。这里使用了 “upload[]” 作为文件字段的名称,表示可以上传多个文件,且字段名为 “upload[]”。- 处理上传的文件:通过遍历文件对象列表,可以对每个文件进行处理,例如获取文件名、保存文件到指定目录、处理文件名重复等。这里的处理逻辑通过注释进行了简单说明,实际应用中需要根据需求进行具体处理。
- 返回上传成功的信息:通过
c.String()方法返回上传成功的信息,包含上传的文件数量。- 启动 HTTP 服务器:使用
router.Run(":8080")启动 HTTP 服务器,监听在 8080 端口,等待接收文件上传请求。需要注意的是,这段代码还有一部分注释掉的代码,包括了设置
router.MaxMultipartMemory限制内存大小的功能。可以根据实际需求取消注释并设置合适的内存限制。同时,这段代码还没有处理文件上传过程中可能出现的错误情况,如文件大小限制、文件类型限制等,需要根据具体需求进行完善。
36、 使用 BasicAuth 中间件
(1)完整代码
1 | |
(2)代码解释
- 定义了一个
secrets变量,模拟了一些私人数据,使用了gin.H来表示 key-value 结构的 map。- 创建了一个默认的 Gin 路由引擎
r。- 使用
r.Group()方法创建了一个路由组/admin,并在该路由组上应用了gin.BasicAuth()中间件。gin.BasicAuth()中间件用于进行 HTTP 基本身份验证,需要传入一个gin.Accounts类型的参数,其中包含了允许访问的用户名和密码的键值对。- 在路由组中定义了一个 GET 请求的处理函数,处理
/admin/secrets路径的请求。在该处理函数中,使用c.MustGet()方法获取经过gin.BasicAuth()中间件处理后的用户信息,其中gin.AuthUserKey是 Gin 框架中用于存储经过认证的用户信息的上下文键名。- 根据用户信息从
secrets变量中获取对应的私人数据,如果存在则返回用户信息和私人数据,否则返回默认的信息。- 使用
r.Run()方法启动 HTTP 服务器,监听在 0.0.0.0:8080 地址上。
37、 使用 HTTP 方法
(1)完整代码
1 | |
(2)代码解释
- 以上代码使用 Gin 框架创建了一个 HTTP 服务器,并注册了不同的 HTTP 方法对应的处理函数。每个处理函数在接收到对应的 HTTP 请求时会被调用,从而进行相应的处理逻辑。
router.Run()函数会在默认端口 8080 上启动 HTTP 服务,可以通过设置环境变量PORT来修改端口号。如果需要固定端口号,可以通过router.Run(":3000")来指定特定的端口号
38、使用中间件
(1)完整代码
1 | |
(2)代码解释
- 创建 Gin 路由:代码通过
gin.New()函数创建了一个新的 Gin 路由实例,并赋值给变量r。这个路由实例没有任何默认的中间件。- 全局中间件:代码通过
r.Use()函数注册了两个全局中间件,分别是gin.Logger()和gin.Recovery()。gin.Logger()用于记录日志,gin.Recovery()用于恢复从处理函数中的 panic。- 路由和处理函数:代码通过
r.GET()、r.POST()等方法注册了多个路由,并指定了相应的处理函数。例如,r.GET("/benchmark", MyBenchLogger(), benchEndpoint)注册了一个 GET 请求的路由 “/benchmark”,并使用了自定义的中间件MyBenchLogger()和处理函数benchEndpoint()。- 路由组和嵌套路由组:代码通过
r.Group()方法创建了一个名为 “authorized” 的路由组,并在其中注册了多个路由和处理函数。这些路由和处理函数都位于 “authorized” 路由组下,并且共享路由组中注册的中间件AuthRequired(),用于认证授权。另外,代码还演示了如何在路由组内创建嵌套的路由组,例如testing := authorized.Group("testing")。- 启动 HTTP 服务:代码通过
r.Run(":8080")启动了一个 HTTP 服务,并监听在 0.0.0.0:8080 上。这将使服务在这个地址上接受来自客户端的请求。
39、 映射查询字符串或表单参数
(1)完整代码
1 | |
(2)代码解释
40、模型绑定和验证
(1)完整代码
1 | |
(2)代码解释
- 定义了一个名为
Login的结构体,其中包含了User和Password两个字段,用于接收登录请求中的用户和密码信息。结构体中使用了标签(tag)来指定了字段在不同请求中的绑定方式,如form:"user"表示在 HTML 表单中使用user字段名进行绑定,json:"user"表示在 JSON 中使用user字段名进行绑定,xml:"user"表示在 XML 中使用user字段名进行绑定。- 在
main函数中,创建了一个默认的 Gin 路由实例,并注册了三个路由处理函数分别处理/loginJSON、/loginXML、/loginForm这三个不同的路由。/loginJSON路由处理函数通过c.ShouldBindJSON(&json)方法将请求体中的 JSON 数据绑定到json结构体变量中,如果绑定失败,则返回错误信息;如果绑定成功,则判断json.User和json.Password是否符合预期的值,如果不符合,则返回未授权的状态码和错误信息,否则返回登录成功的状态码和信息。/loginXML路由处理函数通过c.ShouldBindXML(&xml)方法将请求体中的 XML 数据绑定到xml结构体变量中,如果绑定失败,则返回错误信息;如果绑定成功,则判断xml.User和xml.Password是否符合预期的值,如果不符合,则返回未授权的状态码和错误信息,否则返回登录成功的状态码和信息。/loginForm路由处理函数通过c.ShouldBind(&form)方法根据请求头中的Content-Type判断请求体中的数据类型,并将数据绑定到form结构体变量中,如果绑定失败,则返回错误信息;如果绑定成功,则判断form.User和form.Password是否符合预期的值,如果不符合,则返回未授权的状态码和错误信息,否则返回登录成功的状态码和信息。- 最后通过
router.Run(":8080")启动了服务,监听在0.0.0.0:8080地址上。
41、 路由参数
(1)完整代码
1 | |
(2)代码解释
- 第一个处理器函数处理了路由
/user/:name。:name表示该路由段为参数,可以匹配任何字符串,并将其作为参数传递给处理器函数。在处理器函数中,通过c.Param("name")获取路由参数中的name值,并使用c.String(http.StatusOK, "Hello %s", name)在响应中返回 “Hello {name}” 字符串。- 第二个处理器函数处理了路由
/user/:name/*action。:name和*action都表示参数,其中:name匹配一个字符串,*action匹配多个字符串。在处理器函数中,通过c.Param("name")和c.Param("action")获取路由参数中的name和action值,并根据这些参数值构建消息字符串。然后,使用c.String(http.StatusOK, message)在响应中返回该消息字符串。需要注意的是,如果没有其他路由匹配/user/:name,该处理器函数将处理该请求并进行重定向到/user/:name/。最后,通过调用
router.Run(":8080")启动 HTTP 服务器并监听在:8080端口上。一旦服务器启动成功,它将开始监听来自客户端的 HTTP 请求,并根据注册的路由处理器函数来处理这些请求。Gin 框架提供了很多方便的方法来处理路由、请求和响应,使得构建 HTTP 服务器变得简单和高效。
42、静态资源嵌入
(1)完整代码
1 | |
(2)代码解释
上述代码是一个简单的 Go 语言程序,使用了 Gin 框架和 go-assets-builder 库来创建一个 HTTP 服务器,并加载嵌入在程序中的模板文件进行渲染。下面对代码进行详细的分析:
func main()是 Go 语言程序的入口函数,程序从这里开始执行。gin.New()创建了一个新的 Gin 路由实例,用于处理 HTTP 请求和定义路由。loadTemplate()函数用于加载由 go-assets-builder 嵌入的模板文件,并返回解析后的模板和可能的错误。r.SetHTMLTemplate(t)将加载好的模板设置到 Gin 路由实例的 HTML 模板中,以便后续使用模板进行渲染。r.GET("/", func(c *gin.Context) { ... })定义了一个路由处理函数,处理根路径的 HTTP GET 请求。当用户访问根路径时,Gin 框架会调用这个处理函数来处理请求。c.HTML(http.StatusOK, "/html/index.tmpl", nil)在路由处理函数中调用c.HTML()方法返回一个 HTML 响应。这个方法使用加载的模板渲染数据,并设置 HTTP 状态码为 200(http.StatusOK)。r.Run(":8080")启动 HTTP 服务器,监听在 8080 端口上,开始接收和处理 HTTP 请求。loadTemplate()函数遍历由 go-assets-builder 嵌入的文件,读取文件内容并解析为模板,并将其命名为文件名,形成一个模板链。最终返回加载好的模板和可能的错误。总的来说,这段代码通过 Gin 框架创建了一个简单的 HTTP 服务器,加载了由 go-assets-builder 嵌入的模板文件,用于渲染 HTML 响应。这种方式可以将模板文件嵌入到 Go 语言程序中,方便部署和分发,同时避免了外部文件的依赖和管理。
43、不使用默认的中间件
(1)完整代码
1 | |
(2)代码解释
44、 XML/JSON/YAML/ProtoBuf 渲染
(1)完整代码
1 | |
(2)代码解释
- 导入了 Gin 框架和 net/http 包,以及一个自定义的 Protocol Buffers 文件包 protoexample。
- 创建了一个 Gin 的默认引擎实例 r,用于处理 HTTP 请求。
- 定义了路由处理函数,通过 r.GET() 方法将路由路径和处理函数绑定起来。
- 第一个路由处理函数处理 “/someJSON” 路径的 GET 请求,返回一个 JSON 格式的响应,其中包含一个 “message” 字段和一个 “status” 字段,值分别为 “hey” 和 http.StatusOK。
- 第二个路由处理函数处理 “/moreJSON” 路径的 GET 请求,返回一个 JSON 格式的响应,其中包含一个结构体 msg,结构体有三个字段,分别为 “Name”、”Message” 和 “Number”,通过结构体字面量赋值后作为响应的内容。
- 第三个路由处理函数处理 “/someXML” 路径的 GET 请求,返回一个 XML 格式的响应,其中包含一个 “message” 字段和一个 “status” 字段,值分别为 “hey” 和 http.StatusOK。
- 第四个路由处理函数处理 “/someYAML” 路径的 GET 请求,返回一个 YAML 格式的响应,其中包含一个 “message” 字段和一个 “status” 字段,值分别为 “hey” 和 http.StatusOK。
- 第五个路由处理函数处理 “/someProtoBuf” 路径的 GET 请求,返回一个 Protocol Buffers 格式的响应,其中包含一个自定义的 Test 结构体,结构体有两个字段,分别为 “Label” 和 “Reps”,通过结构体字面量赋值后作为响应的内容。
- 最后通过 r.Run() 方法启动 HTTP 服务器,在地址 0.0.0.0:8080 上监听请求。