Gin

3. Gin RESTful API 开发

2025-11-22  本文已影响0人  Surpassme

3. Gin RESTful API 开发

3.1 RESTful API简介

3.1.1 RESTful API 定义

    REST(Representational State Transfer,表现层状态转换)是一种软件架风格设计风格,而不是一种标准。它提供了一组设计原则和约束条件,主要用于客户端和服务端交互类的软件。基于这个风格设计的软件可以更简洁、具有层次。

    REST 并没有一个明确的标准,更像是一种设计风格,满足这种设计风格的程序或接口称之为RESTful,因此RESTful API就是满足REST风格的接口。

    RESTful 风格的程序或API需要遵循一些约束条件和规则,其主要特点如下所示:

    资源可以是图片、音乐、视频等,它是网络上的一个实体。

    对资源的操作包括查询、创建、修改和删除,这些操作分别对应于HTTP协议中的GET、POST、PUT、DELETE方法。即使用RESTful风格的接口单从接口上只能定位其资源,但无法知道它具体进行了什么操作,需要具体了解其发生了什么操作,要从HTTP请求方法类型上进行判断。

3.1.2 RESTful API 设计规范

    遵循RESTful风格的设计规范主要如下所示:

3.2 API设计与实现

3.2.1 前后端分离

    前后端分离是指软件设计中用户界面(前端)和后端逻辑、数据存储等的分离,前后端通信通过API进行通信。

    前后端分离所带来的好处如下所示:

3.2.2 设计RESTful API

    在前后端分离的架构中,RESTful API是连接前后端的重要桥梁,一个清晰且规范的RESTful API可以有效帮助前后端开发人员进行查询数据、执行操作、相互通信等。主要包含以下几个部分。

1.资源与URI设计

    在RESTful API中,资源是要操作的对象,而URI是资源的唯一标识。一个良好的API通常使用名词表示资源,并尽量保持简洁清晰。

2.HTTP方法设计

    不要在URI中使用动词,而是要依赖于HTTP方法来表示资源的操作类型。

GET /users           # 获取所有用户
POST /users          # 创建一个新的用户
PUT /users/{id}      # 更新指定ID的用户
DELETE /users/{id}   # 删除指定ID的用户

3.请求与响应格式

    RESTful API 通常使用JSON来进行数据交换。前端可以通过API获取数据,后端则根据请求返回响应数据。

4.状态码

    状态码用于表示API请求的结果,遵循HTTP协议中的状态码。

5.分页与过滤

    当资源数据量比较大时,常常需要支持分页和过滤功能。通过在API中加入分页参数和过滤条件可以有效减小前端一次性加载大量数据的负担。

GET /users?page_num=1&limit=20
GET /users?status=actived

6.版本管理

    为保证新旧版本的API兼容性,通过在API的URI中加入相应的版本号以方便管理,示例如下所示:

GET /v1/users
GET /v2/users

7.安全性与身份验证

    在目前的Web环境中,API的安全性也日益重要,常用和身份验证如下所示:

8.错误响应

    API 应当能够返回明确的错误信息,以帮助客户端处理异常情况。通常错误信息需要包含状态码错误信息错误详细信息

3.3 RESTful API案例

    以下将演示通过接口从数据库中查询、创建、更新和删除用户。

3.3.1 路由设计

{
    users := r.Group("/api/v2/users")
    users.GET("/", controller.GetUsers)
    users.GET("/:id", controller.GetUsersById)
    users.POST("/", controller.CreateUsers)
    users.PUT("/:id", controller.UpdateUsers)
    users.DELETE("/:id", controller.DeleteUsersById)
}

3.3.2 数据库设计

CREATE DATABASE IF NOT EXISTS restful_api_db;
USE restful_api_db;
CREATE TABLE  IF NOT EXISTS users (
    id INT NOT NULL AUTO_INCREMENT,
    name varchar(255) NOT NULL ,
    age INT DEFAULT NULL,
    location varchar(255) DEFAULT NULL,
    passwd varchar(255) NOT NULL,
    PRIMARY KEY (id)
) 
ENGINE=InnoDB ,
CHARACTER SET utf8mb4,
COLLATE utf8mb4_0900_ai_ci;

3.3.3 模型代码

package models

// 数据库对应的结构体
type User struct {
    Id       int    `json:"id"`
    Name     string `json:"name"`
    Age      int    `json:"age"`
    Location string `json:"location"`
    Passwd   string `json:"passwd"`
}

3.3.4 逻辑代码

package controller

func GetUsers(ctx *gin.Context) {
    var queryUsers []models.User
    var responseUsers []models.User
    // 从数据库中查询数据
    db.ConnectDB().Find(&queryUsers)

    if len(queryUsers) <= 0 {
        ctx.JSON(
            http.StatusNotFound,
            gin.H{
                "status_code": http.StatusNotFound,
                "message":     "查询结果为空",
            })
        return
    }
    for _, v := range queryUsers {
        responseUsers = append(responseUsers, models.User{
            Id:       v.Id,
            Name:     v.Name,
            Age:      v.Age,
            Location: v.Location,
        })
    }

    ctx.JSON(
        http.StatusOK,
        gin.H{
            "status_code": http.StatusOK,
            "count":       len(responseUsers),
            "data":        responseUsers,
        })
}
func GetUsersById(ctx *gin.Context) {
    var requestUsers models.User
    id := ctx.Param("id")
    // 从数据库查询指定ID的用户
    db.ConnectDB().Where("id = ?", id).Find(&requestUsers)

    result := models.User{
        Id:       requestUsers.Id,
        Name:     requestUsers.Name,
        Age:      requestUsers.Age,
        Location: requestUsers.Location,
    }

    ctx.JSON(
        http.StatusOK,
        gin.H{
            "status_code": http.StatusOK,
            "data":        result,
        })

}
func CreateUsers(ctx *gin.Context) {
    name := ctx.PostForm("name")
    age, err := strconv.Atoi(ctx.PostForm("age"))
    if err != nil {
        log.Panicf("解析年龄出错:%v", err)
        return
    }
    location := ctx.PostForm("location")
    passwd := ctx.PostForm("passwd")

    requestUsers := models.User{
        Name:     name,
        Age:      age,
        Location: location,
        Passwd:   passwd,
    }
    // 保存数据到数据库
    db.ConnectDB().Create(&requestUsers)

    ctx.JSON(
        http.StatusOK,
        gin.H{
            "status_code": http.StatusOK,
            "message":     fmt.Sprintf("用户%s创建成功", requestUsers.Name),
        })
}
func UpdateUsers(ctx *gin.Context) {
    var requestUsers models.User
    id := ctx.Param("id")
    age, err := strconv.Atoi(ctx.PostForm("age"))
    if err != nil {
        log.Printf("解析年龄出错:%s", err)
    } else {
        db.ConnectDB().Model(&requestUsers).Where("id = ? ", id).Update("age", age)
    }
    location := ctx.PostForm("location")
    passwd := ctx.PostForm("passwd")

    if location != "" {
        db.ConnectDB().Model(&requestUsers).Where("id = ? ", id).Update("location", location)
    }

    if passwd != "" {
        db.ConnectDB().Model(&requestUsers).Where("id = ? ", id).Update("passwd", passwd)
    }

    ctx.JSON(
        http.StatusOK,
        gin.H{
            "status_code": http.StatusOK,
            "message":     fmt.Sprintf("ID=%s的用户更新成功", id),
        })
}
func DeleteUsersById(ctx *gin.Context) {
    id := ctx.Param("id")
    // 删除指定ID的用户
    db.ConnectDB().Delete(&models.User{}, id)

    ctx.JSON(
        http.StatusOK,
        gin.H{
            "status_code": http.StatusOK,
            "message":     fmt.Sprintf("ID=%s的用户删除成功", id),
        })
}
func getDsn() string {
    mysqlHost := os.Getenv("MYSQL_HOST")
    mysqlPort := os.Getenv("MYSQL_PORT")
    mysqlUsername := os.Getenv("MYSQL_USERNAME")
    mysqlPasswd := os.Getenv("MYSQL_PASSWD")
    dbName := "restful_api_db"

    dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", mysqlUsername, mysqlPasswd, mysqlHost, mysqlPort, dbName)
    return dsn
}

var db *gorm.DB
var err error

func ConnectDB() *gorm.DB {
    dsn := getDsn()
    db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatalf("连接数据库错误:%+v", err)
    }
    return db
}

3.4 RESTful API 测试

    在以上接口完成后,即可以使用API接口工具,例如Postman、Apifox、cURL等工具进行测试。

上一篇 下一篇

猜你喜欢

热点阅读