3. Gin RESTful API 开发

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风格的设计规范主要如下所示:

  • 使用斜杠(/)表示层级关系且层级不要过深
  • 不要在URI尾部使用斜杠(/)
  • 使用连字符(-)来提高URI可读性,而不使用横线(-)
  • 在URI中使用小写英文字母
  • 不要使用文件扩展名
  • URL中不要使用动词,使用请求方式表示动作
  • 资源表示使用复数
  • 使用查询参数过滤URI集合

3.2 API设计与实现

3.2.1 前后端分离

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

  • 前端可以看作是直接与用户进行交互的部分,前端负责收集用户的输入和展现输出结果
  • 后端一般是指在后台服务器上运行的程序,包含后端逻辑处理、数据存储和一些其他依赖的组件等。

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

  • 模块化:前后端分离,可以独立处理每个部分、从而实现模块化,使得维护起来更容易
  • 可扩展性:前后端分离允许更大的可扩展性,使得每个部分都可以根据程序的需要独立扩展
  • 可复用性:前后端分离可以使得每个部分在不同的程序或上下文中进行复用
  • 安全性:前后端分离有助于提高安全性,更好的控制对敏感数据或功能的访问
  • 可屏蔽前后端技术栈所带来的差异:前后端关注的技术领域不同,采用前后端分离后,可以使得各自关注自身领域内的技术栈

3.2.2 设计RESTful API

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

1.资源与URI设计

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

  • 资源名称:应使用复数名词来表示资源集合,例如使用/users表示所有用户
  • 资源标识符:一个具体的资源应使用唯一标识符来表示,例如/users/{id}表示为特定ID的用户

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的安全性也日益重要,常用和身份验证如下所示:

  • API Key: 通过请求头可查询参数传递API密钥进行身份验证
  • JWT(JSON Web Token):通过Bear Token机制在请求头中传递JWT进行身份验证
  • OAuth: 用于第三方授权验证

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,
		})
}
  • 按用户ID查询
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等工具进行测试。

本文同步在微信订阅号上发布,如各位小伙伴们喜欢我的文章,也可以关注我的微信订阅号:woaitest,或扫描下面的二维码添加关注:

posted @ 2025-11-23 23:50  Surpassme  阅读(1)  评论(0)    收藏  举报