Gin 开发框架 | 青训营笔记

176 阅读5分钟

Introduce

Gin 是一个基于 Go 语言的 Web 框架,是一个 HTTP Web 服务器,采用了类似于 Echo 的编程框架。它可以用于构建高性能、可扩展的 Web 应用程序,提供了路由、参数绑定、JSON/XML 渲染、Cookie/命令行参数/环境变量等功能。

以下是 Gin 的主要特点:

  1. 高性能:Gin 大量采用了嵌套 HandlerFunc 的方法,不仅可以将多个中间件串联起来,还能 像树形结构一样可以灵活组合和嵌套,这种实现方式使得 Gin 在处理请求时效率非常高。
  2. 中间件插件机制:Gin 提供了丰富的中间件函数,并提供了实现自定义中间件的方法,可以轻松地对请求进行前置/后置处理,实现很多实用的功能,如日志记录、路由拦截、鉴权等。
  3. 路由:Gin 提供了 RESTful 风格的路由设置,支持 GET/POST/PUT/DELETE 等 HTTP 请求方法,支持参数路由、分组路由、静态文件服务器等功能。
  4. 参数绑定:Gin 提供了多种参数绑定的方法,支持绑定 URL 参数、表单参数、JSON 请求参数、XML 请求参数等,支持参数校验和数据转换。
  5. JSON/XML 渲染:Gin 自带了 JSON/XML 渲染功能,可以方便地将数据以 JSON/XML 格式返回给客户端。
  6. 异常处理:Gin 提供了强大的异常处理机制,可以方便地捕获全局异常、HTTP 异常、路由异常等,并提供了友好的错误信息显示方式。
  7. 多种渲染模版:Gin 支持多种模板引擎,如 html/template、amber、handlebars、jet、mustache、pongo2 等,可以根据需要灵活选择。

Install

  1. 可以使用 go get 命令安装。

    go get -u github.com/gin-gonic/gin
    
  2. 如果使用了 IDE,可以直接在源代码中引入上述包,IDE 可以自动下载。

框架的简单使用

创建一个服务

创建服务是建立一个 Web 服务的第一步,使用以下代码创建一个服务。

ginServer := gin.Default()

服务创建成功后,会获得一个 *gin.Engine 类型的变量 ginServer,之后就可以通过它对服务进行设置、控制。

简单设置

自定义网站图标

在创建服务后,可以对其进行一些设置,比如可以通过使用 Gin 的 favicon 中间件来为应用程序添加自定义的网站图标

ginServer.Use(favicon.New("./GinDemo/main/favicon.jpg"))

具体来说,当 Web 应用程序启动后,用户在浏览器中访问该应用时,浏览器会自动请求获取网站的 favicon.ico 文件(即网站图标文件),如果没有设置该文件,则会产生一个 404 错误。当我们将该中间件添加到 ginServer 中之后,它会拦截所有请求,如果该请求为 GET 方法,并且请求 URL 路径为 "/favicon.ico",则会将指定的网站图标文件作为响应数据返回给客户端。这样就可以避免浏览器产生 404 错误,同时为 Web 应用程序提供了一个自定义的网站图标。

设置中间件(拦截器)

过滤器设计模式的应用,可以编写一个自定义中间件,并进行注册,依次对请求作某些处理。

func myHandler() gin.HandlerFunc {
    return func(context *gin.Context) {
        context.Set("user_session", "user")
        context.Abort()
        //context.Next()
    }
}

上面自定义了一个处理函数,context.Abort() 可以将请求拦截,context.Next() 表示请求通过。gin.Context 类型可以类比 Java 中的 HttpServletRequest,可以通过此类型的变量获取请求的相关信息,包括请求头、请求参数、请求体等。当然,gin.Context 提供了一些 Gin 框架特有的方法和属性。

当定义好了处理函数,可以使用 ginServer.Use(myHandler()) 语句注册该中间件,那么此服务的所有请求都会被该中间件拦截处理。也可以设置拦截某个特定的请求,设置方法在下文讲述。

加载相关文件

如果某个请求需要返回一个 HTML 页面等,则需要在服务中加载相应的 HTML 页面以及相关资源文件。

// 加载静态文件 html
ginServer.LoadHTMLGlob("./GinDemo/templates/*")
​
// 加载资源文件 css js
ginServer.Static("/GinDemo/static", "./GinDemo/static")

定义请求

这是最关键的部分,即设置请求路径,使得可以通过该路径访问相应的资源或服务。

GET

ginServer.GET("/hello", func(context *gin.Context) {
    context.JSON(200, gin.H{
        "message": "hello world",
    })
})

上面的代码可以实现设置一个路径为 /hello 的 GET 请求,context.JSON 可以向请求发起者返回一个 JSON 格式的数据,使用 gin.H 封装。

Gin 也支持 Restful API,下面给出一个例子。

ginServer.GET("/user/:name/:age", func(context *gin.Context) {
    name := context.Param("name")
    age := context.Param("age")
    fmt.Println(name, age)
    
    // 返回数据
    context.JSON(200, gin.H{
        "message": "get success",
        "name":    name,
        "age":     age,
    })
})

如果要相应一个 HTML 页面,则可以使用下面的方式。注意,返回的内容必须要先注册。

ginServer.GET("/index", func(context *gin.Context) {
    context.HTML(http.StatusOK, "index.html", gin.H{
        "name": "zhangsan",
    })
})

POST

表单提交是很常用的请求,下面给出一个简单的表单处理的例子。

ginServer.POST("/form", func(context *gin.Context) {
    // request body
    username := context.PostForm("username")
    password := context.PostForm("password")
    fmt.Println(username, password)
​
    // 可以写校验逻辑
​
    // 返回数据
    context.JSON(http.StatusOK, gin.H{
        "username": username,
        "password": password,
    })
})

如果请求者发送了 JSON 格式的数据,可以通过下面的代码处理。

ginServer.POST("/json", func(context *gin.Context) {
    // request body
    body, err := context.GetRawData()
    if err != nil {
        fmt.Println("get raw data failed, err: ", err)
        return
    }
​
    var m map[string]interface{}
    err = json.Unmarshal(body, &m)
    if err != nil {
        fmt.Println("unmarshal failed, err: ", err)
        return
    }
    fmt.Println(m)
​
    // 返回数据
    context.JSON(http.StatusOK, m)
})

PUT

ginServer.PUT("/put", func(context *gin.Context) {
    // 获取参数
    username := context.Query("username")
    password := context.Query("password")
    fmt.Println(username, password)
    // 返回数据
    context.JSON(200, gin.H{
        "message": "put success",
    })
})

DELETE

ginServer.DELETE("/delete", func(context *gin.Context) {
    // 获取参数
    username := context.Query("username")
    password := context.Query("password")
    fmt.Println(username, password)
    // 返回数据
    context.JSON(200, gin.H{
        "message": "delete success",
    })
})

路由分组

当项目规模增加,需要定义的请求就会增加,使用路由分组可以让文件结构变得更加清晰,提升代码的可读性。这个与在 Java Web 中通过编写一个 Base Servlet,其他的 Servlet 继承 Base Servlet 实现的功能一样,但是从下面的代码可以看出,Go 实现起来要简易许多。

v1 := ginServer.Group("/v1")
{
    v1.GET("/login", func(context *gin.Context) {
        context.JSON(200, gin.H{
            "message": "v1 login",
        })
    })
    v1.GET("/submit", func(context *gin.Context) {
        context.JSON(200, gin.H{
            "message": "v1 submit",
        })
    })
}

使用 ginServer.Group() 可以创建一个请求根路径,然后在下面的代码体中写子路径,然后就可以通过两个路径拼接来访问资源或服务了。

后记

学习过 Java Web 开发的相关技术,但是与 Go 语言通过 Gin 开发后端相比还是复杂很多,Go 提供的库和 Gin 框架提供的属性和方法大大方便了 Web 应用、服务的开发。