自学内容网 自学内容网

Go开发指南-Gin与Web开发

目录:
(1)Go开发指南-Hello World
(2)Go开发指南-Gin与Web开发

Gin 是一个用 Go 语言编写的轻量级、高性能的 Web 框架,主要用于构建 API 服务和微服务。由于其简洁的 API 设计和强大的路由功能,Gin 在 Go 社区中广受欢迎。

运行Web程序

创建一个目录web-service-gin,初始化模块

go mod init example/web-service-gin

创建main.go

package main

import (
"net/http"

"github.com/gin-gonic/gin"
)

type album struct {
ID     string  `json:"id"`
Title  string  `json:"title"`
Artist string  `json:"artist"`
Price  float64 `json:"price"`
}

func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.Run("localhost:8080")
}

var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}

func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}

将其运行起来: go run .,就能访问http://lcoalhost:8080/albums了。

再新增一个POST接口:


// add post handler
func postAlbums(c *gin.Context) {
var newAlbum album

if err := c.BindJSON(&newAlbum); err != nil {
return
}

albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}

// update router
router.POST("/albums", postAlbums)

此时用POST方法来访问就可以来新增album了。

根据指定id返回

新增一个接口:

// add getById handler
func getAlbumByID(c *gin.Context) {
    id := c.Param("id")
    for _, a := range albums {
        if a.ID == id {
            c.IndentedJSON(http.StatusOK, a)
            return
        }
    }
    c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}

// update router
router.GET("/albums/:id", getAlbumByID)

发起请求:curl http://localhost:8080/albums/1 即可获取第一个album了。

连接数据库

Web服务中访问数据库是必不可少的,下面以postgresql数据库为例来演示如何连接数据库。

先创建数据库和表:

CREATE DATABASE music;

CREATE TABLE album(
    id SERIAL PRIMARY KEY,
    title VARCHAR(36) NOT NULL,
    artist VARCHAR(36) NOT NULL,
    price NUMBRIC(10, 2) NOT NULL
) ;

INSERT INTO album(title, artist, price) VALUES
('Album A', 'Artist 1', 9.99),
('Album B', 'Artist 1', 14.99),
('Album C', 'Artist 2', 19.99);

创建main.go:

package main

import (
"database/sql"
"fmt"
"log"
"net/http"

"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)

type Album struct {
ID     string  `json:"id"`
Title  string  `json:"title"`
Artist string  `json:"artist"`
Price  float64 `json:"price"`
}

var db *sql.DB

func main() {
dsn := "user=postgres password=admin dbname=music sslmode=disable"
var err error
db, err = sql.Open("postgres", dsn)
if err != nil {
log.Fatal("Failed to connect to database", err)
}
pingErr := db.Ping()
if pingErr != nil {
log.Fatal("Failed to ping database", pingErr)
}
fmt.Println("Connected!")

router := gin.Default()
router.GET("/albums/:artist", getAlbumsByArtist)
router.Run("localhost:8080")
}

func getAlbumsByArtist(c *gin.Context) {
artist := c.Param("artist")
albums, err := queryAlbumsByArtist(artist)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

if len(albums) == 0 {
c.JSON(http.StatusNotFound, gin.H{"message": "No albums found for artist"})
return
}
c.JSON(http.StatusOK, albums)
}

func queryAlbumsByArtist(artist string) ([]Album, error) {
rows, err := db.Query("SELECT * FROM album WHERE artist = $1", artist)

if err != nil {
return nil, err
}
defer rows.Close()

var albums []Album
for rows.Next() {
var album Album
if err := rows.Scan(&album.ID, &album.Title, &album.Artist, &album.Price); err != nil {
return nil, err
}
albums = append(albums, album)
}

if err = rows.Err(); err != nil {
return nil, err
}
return albums, nil
}

注意,在导入包时使用了 _ "github.com/lib/pq", 此处_表示该包被导入,但不直接在代码中使用,其作用是执行该包的初始化函数。这种用法称为空白标识符导入,通常用于以下几种情况:

  • 注册驱动程序或插件
  • 执行包的初始化代码

访问curl http://localhost:8080/albums/Artist%201,即可获取返回结果。

Gin的中间件

Gin的中间件函数可以在请求到达handler之前做一些前置处理或者在响应返回给客户端之前做后置处理。Gin提供了很多内置的中间件函数,比如常见的日志记录,CORS处理等。开发者也可以根据需要自己定制中间件函数。

日志中间件

下面以内置的日志中间件函数为例来说明如何做日志前处理:

package main

import (
"log"
"time"
"github.com/gin-gonic/gin"
)

func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start)
log.Printf("Request - Method: %s | Status : %d | Duration: %v", c.Request.Method, c.Writer.Status(), duration)
}
}

func main() {
router := gin.Default()

router.Use(LoggerMiddleware())
router.GET("/", func(c *gin.Context) {
c.String(200, "Hello, World!")
})
router.Run(":8080")
}

访问 http://localhost:8080/, 会看到输出日志中打印日志:

Request - Method: GET | Status : 200 | Duration: 24.291µs

自定义中间件

假设我们需要在请求被处理之前对其进行鉴权,可以自定义中间件:

package main

import (
"github.com/gin-gonic/gin"
)

func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
apiKey := c.GetHeader("X-API-Key")
if apiKey == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
return
}
c.Next()
}
}

func main() {
router := gin.Default()

authGroup := router.Group("/api")
authGroup.Use(AuthMiddleware())
{
authGroup.GET("/data", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Authenticated and authorized!"})
})
}

router.Run(":8080")
}

当访问http://localhost:8080/api/data时,返回{"error":"Unauthorized"}错误。

路由分组

上文中设置鉴权中间件时对路由进行了分组。Gin允许对路由进行分组,以便更好地组织和维护代码。

下面继续展示路由分组功能:

package main

import (
"github.com/gin-gonic/gin"
)

func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
apiKey := c.GetHeader("X-API-Key")
if apiKey == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
return
}
c.Next()
}
}

func main() {
router := gin.Default()

public := router.Group("/public")
{
public.GET("/info", func(c *gin.Context) {
c.String(200, "Public information")
})
public.GET("/products", func(c *gin.Context) {
c.String(200, "Public product list")
})
}

private := router.Group("/private")
private.Use(AuthMiddleware())
{
private.GET("/data", func(c *gin.Context) {
c.String(200, "Private data accessible after authentication")
})
private.POST("/create", func(c *gin.Context) {
c.String(200, "Create a new resource")
})
}

router.Run(":8080")
}

控制器与Handlers

当后端接口不断增加时,如果将所有的业务逻辑全部放在路由的handlers里面是不明智的。

最好是增加控制器来处理业务逻辑,如下所示:

package main

import (
"github.com/gin-gonic/gin"
)

type UserController struct{}

func (uc *UserController) GetUserInfo(c *gin.Context) {
userID := c.Param("id")
c.JSON(200, gin.H{"id": userID, "name": "John Doe", "email": "john@example.com"})
}

func main() {
router := gin.Default()
userController := &UserController{}
router.GET("/users/:id", userController.GetUserInfo)

router.Run(":8080")
}

参考资料

[1]. https://go.dev/doc/tutorial/web-service-gin


原文地址:https://blog.csdn.net/xiaoyi52/article/details/143626545

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!