Gin + GORM 接口可视化测试

只是让自己了解Gin和GORM的一个demo

Go 复制代码
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type User struct {
	ID   uint   `json:"id" gorm:"primaryKey"`
	Name string `json:"name"`
	Age  int    `json:"age"`
}

var db *gorm.DB

func init() {
	// 2. 初始化 GORM 连接 MySQL 数据库
	var err error
	// ⚠️ 请务必将下方 'root:你的密码' 替换成你本地 MySQL 的真实账号密码
	dsn := "root:root@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True&loc=Local"
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("数据库连接失败:" + err.Error())
	}
	// 自动迁移表结构(程序启动时会自动在 test 库里创建 user_infos 表)
	err = db.AutoMigrate(&User{})
	if err != nil {
		return
	}
}

func main() {
	r := gin.Default()
	// 允许所有跨域请求的中间件
	r.Use(func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")                                // 允许任意来源
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") // 允许的请求方法
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")     // 允许的请求头
		c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")                 // 暴露给前端的响应头

		// 专门处理浏览器发出的预检请求(OPTIONS),直接返回 204 状态码并终止后续逻辑
		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}
		c.Next()
	})
	r.GET("/users", GetUsers)          // 查询所有用户
	r.GET("/users/:id", GetUserByID)   // 查询单个用户
	r.POST("/users", CreateUser)       // 新增用户
	r.PUT("/users/:id", UpdateUser)    // 更新用户
	r.DELETE("/users/:id", DeleteUser) // 删除用户
	// 启动服务,监听 8080 端口
	err := r.Run(":8080")
	if err != nil {
		return
	}
}

// --- 下面是具体的增删改查逻辑实现 ---

// 【查】获取所有用户
func GetUsers(c *gin.Context) {
	var users []User
	db.Find(&users)
	c.JSON(http.StatusOK, gin.H{"data": users})
}

// 【查】根据 ID 获取单个用户
func GetUserByID(c *gin.Context) {
	id := c.Param("id")
	var user User
	if err := db.First(&user, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": user})
}

// 【增】创建新用户
func CreateUser(c *gin.Context) {
	var user User
	// 将前端传来的 JSON 数据绑定到 user 结构体
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "参数格式错误"})
		return
	}
	db.Create(&user)
	c.JSON(http.StatusCreated, gin.H{"message": "创建成功", "data": user})
}

// 【改】更新用户信息
func UpdateUser(c *gin.Context) {
	id := c.Param("id")
	var user User
	// 先查找用户是否存在
	if err := db.First(&user, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
		return
	}

	var input User
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "参数格式错误"})
		return
	}

	// 更新字段并保存
	user.Name = input.Name
	user.Age = input.Age
	db.Save(&user)

	c.JSON(http.StatusOK, gin.H{"message": "更新成功", "data": user})
}

// 【删】删除用户
func DeleteUser(c *gin.Context) {
	id := c.Param("id")
	result := db.Delete(&User{}, id)

	if result.RowsAffected == 0 {
		c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
}
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Gin 接口测试工具</title>
    <style>
        body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 900px; margin: 30px auto; padding: 20px; background-color: #f5f7fa; }
        .container { display: flex; gap: 20px; }
        .panel { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); flex: 1; }
        h2 { margin-top: 0; color: #333; border-bottom: 2px solid #42b983; padding-bottom: 10px; }
        input, button { width: 100%; padding: 10px; margin: 8px 0; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
        button { background-color: #42b983; color: white; border: none; cursor: pointer; font-weight: bold; transition: 0.3s; }
        button:hover { background-color: #369c6e; }
        button.delete-btn { background-color: #ff4d4f; }
        button.delete-btn:hover { background-color: #d9363e; }
        pre { background: #282c34; color: #abb2bf; padding: 15px; border-radius: 4px; overflow-x: auto; min-height: 100px; }
        table { width: 100%; border-collapse: collapse; margin-top: 10px; }
        th, td { border: 1px solid #eee; padding: 10px; text-align: left; }
        th { background-color: #f9fafc; }
    </style>
</head>
<body>

<h1 style="text-align: center; color: #42b983;">Gin + GORM 接口可视化测试</h1>

<div class="container">
    <!-- 左侧:操作区 -->
    <div class="panel">
        <h2>🚀 发送请求</h2>

        <label>用户 ID (用于查询/修改/删除):</label>
        <input type="number" id="userId" placeholder="例如: 1">

        <label>姓名:</label>
        <input type="text" id="userName" placeholder="例如: 张三">

        <label>年龄:</label>
        <input type="number" id="userAge" placeholder="例如: 25">

        <button onclick="createUser()">➕ 新增用户 (POST)</button>
        <button onclick="getUser()">🔍 查询单个用户 (GET)</button>
        <button onclick="updateUser()">✏️ 更新用户 (PUT)</button>
        <button class="delete-btn" onclick="deleteUser()">❌ 删除用户 (DELETE)</button>

        <hr>
        <button onclick="getAllUsers()" style="background-color: #007bff;">📋 刷新并获取所有用户列表</button>
    </div>

    <!-- 右侧:结果展示区 -->
    <div class="panel">
        <h2>📊 返回结果</h2>
        <pre id="responseResult">// 接口返回的 JSON 数据会显示在这里...</pre>

        <h3>当前数据库用户列表:</h3>
        <table>
            <thead>
            <tr>
                <th>ID</th>
                <th>姓名</th>
                <th>年龄</th>
            </tr>
            </thead>
            <tbody id="userTableBody">
            <!-- 动态生成的表格行 -->
            </tbody>
        </table>
    </div>
</div>

<script>
    const API_BASE = 'http://localhost:8080/users';

    // 辅助函数:显示返回结果
    function showResult(data) {
        document.getElementById('responseResult').textContent = JSON.stringify(data, null, 4);
    }

    // 1. 获取所有用户
    async function getAllUsers() {
        try {
            const res = await fetch(API_BASE);
            const data = await res.json();
            showResult(data);

            // 渲染到表格
            const tbody = document.getElementById('userTableBody');
            tbody.innerHTML = '';
            if (data.data) {
                data.data.forEach(user => {
                    tbody.innerHTML += `<tr><td>${user.id}</td><td>${user.name}</td><td>${user.age}</td></tr>`;
                });
            }
        } catch (error) {
            showResult({ error: "请求失败,请检查后端是否启动!" });
        }
    }

    // 2. 新增用户
    async function createUser() {
        const name = document.getElementById('userName').value;
        const age = Number(document.getElementById('userAge').value);         if (!name || !age) return alert("请输入姓名和年龄!");

        try {
            const res = await fetch(API_BASE, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ name, age })
            });
            const data = await res.json();
            showResult(data);
            getAllUsers(); // 自动刷新列表
        } catch (error) {
            showResult({ error: "请求失败" });
        }
    }

    // 3. 查询单个用户
    async function getUser() {
        const id = document.getElementById('userId').value;
        if (!id) return alert("请输入用户ID!");

        try {
            const res = await fetch(`${API_BASE}/${id}`);
            const data = await res.json();
            showResult(data);
        } catch (error) {
            showResult({ error: "请求失败" });
        }
    }

    // 4. 更新用户
    async function updateUser() {
        const id = document.getElementById('userId').value;
        const name = document.getElementById('userName').value;
        const age = Number(document.getElementById('userAge').value);         if (!name || !age) return alert("请输入姓名和年龄!");

        if (!id || !name || !age) return alert("请输入完整的ID、姓名和年龄!");

        try {
            const res = await fetch(`${API_BASE}/${id}`, {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ name, age })
            });
            const data = await res.json();
            showResult(data);
            getAllUsers();
        } catch (error) {
            showResult({ error: "请求失败" });
        }
    }

    // 5. 删除用户
    async function deleteUser() {
        const id = document.getElementById('userId').value;
        if (!id) return alert("请输入要删除的用户ID!");

        if (!confirm("确定要删除吗?")) return;

        try {
            const res = await fetch(`${API_BASE}/${id}`, { method: 'DELETE' });
            const data = await res.json();
            showResult(data);
            getAllUsers();
        } catch (error) {
            showResult({ error: "请求失败" });
        }
    }

    // 页面加载时自动获取一次列表
    window.onload = getAllUsers;
</script>

</body>
</html>
  • & (取地址符):作用是"我要这个变量的地址/指针 "。所以 &user 的意思就是"把 user 这个变量在内存中的地址交出来"。
  • * (解引用符):作用才是"我要这个指针指向的值"。
  1. 为了修改原来的数据(最主要的原因)

    Go 语言的函数默认是"值传递"的。如果你只传 user(不带 &),Go 会把 user 里的数据完整复制一份 传给函数。

    那么,当 ShouldBindJSON 辛苦地把前端传来的 JSON 数据解析好并填进去时,它填的是那份**"复印件"** 。等函数运行结束,"复印件"被扔掉,你原本定义的 user 变量里依然空空如也。

    而当你传入 &user(指针/地址)时,就相当于告诉函数:"原件放在这个地址上,你直接去那个地址把数据填进我的原件里!"这样函数内部的操作才能真实地改变你的 user 变量。

  2. 为了避免大数据量的复制(性能优化)

    如果结构体非常大,每次调用函数都复制一份会非常消耗内存和 CPU。直接传一个轻量级的指针(地址),效率会高得多。

html 复制代码
// 必须加 *User 接收指针,调用时必须传 &user
func fillData(user *User) {
    user.Name = "张三"
}

var u User
fillData(&u) // 必须显式取地址!
java 复制代码
package org.example;

public class Main {

    // 直接接收 User 对象,不用管什么指针不指针
    public static void fillData(User user) {
        user.setName("张三"); // 这里修改的也是原本的那个对象
    }

    public static void main(String[] args) {
        User u = new User();
        fillData(u); // 直接传 u 就行!Java 内部自动传递的是引用
        System.out.println(u.getName()); // 输出: 张三
    }
}
class User{
    String Name;
    public  void setName(String user)
    {
        this.Name = user;
    }
    public String getName()
    {
        return Name;
    }


}

我还是更倾向与go的特性

相关推荐
lolo大魔王1 小时前
Gin 框架中间件超详细实战教程(原理、全局中间件、路由中间件、自定义中间件、跨域、日志拦截)
中间件·gin
lolo大魔王3 小时前
Gin 框架响应格式与 HTML 模板渲染完整实战教程
前端·html·gin
必胜刻9 天前
Go 调用Coze工作流实现 AI 游戏生成
开发语言·ai·golang·gin
比特森林探险记11 天前
context 在 gRPC / Gin / K8s 中的实战
容器·kubernetes·gin
GoFly开发者12 天前
好消息!Gin+GORM-Gen开发框架已集成完成,正在进行测试和编写使用文档中,需要的开发朋友可以等待使用及订阅哦
gin·gorm·gorm-gen
Soonyang Zhang13 天前
nccl分析(三)——GPU-Initiated Networking(gin)数据发送过程分析
gin·nccl
呆萌很14 天前
【Gin】中间件练习题
gin
会编程的土豆15 天前
Gin 核心概念速记
数据库·后端·gin·goland
GDAL15 天前
Gin c.HTML 完整教程
html·gin