Gin 实现基础 CRUD 接口
前面2篇讲了关于 gin + mysql + jwt + rbac 等基础 web搭建操作, 主要目的还是学习 go 语言的一些应用工具,
然后本篇继续来实现一个名为 notice 的公告模块, 包含数据的增删查改, 这个几乎是咱们讨饭的重点, 当然接口逻辑都是比较简单, 体验一下就好.
项目目录
我是在 mac 终端下, 用命令行来模拟一下 tree 的功能, 体现目录结构.
cj@mini gin-shop-admin %
find . -path './.git' -prune -o -print | sed -e 's;[^/]*/;|____;g;s;____|; |;g'
.
|____go.mod
|____internal
| |____middleware
| | |____auth.go
| |____db
| | |____db.go
|____go.sum
|____api
| |____routers.go
| |____handlers
| | |____auth
| | | |____views.go
| | |____user
| | | |____menu.go
| | | |____views.go
| | |____notice
| | | |____views.go
| | |____rule
| | | |____views.go
|____.vscode
| |____extensions.json
|____tmp
| |____runner-build
|____main.go
|____pkg
| |____utils
| | |____response.go
| | |____tools.go
| |____jwtt
| | |____jwt.go
cj@mini gin-shop-admin %
数据库表
主要是 notice 表, 字段如下:
-- ###### 公告模块
drop table if exists notice;
create table notice (
id int not null auto_increment primary key comment '自增id'
, title varchar(200) not null comment '公告标题'
, content text not null comment '公告内容'
, orders int not null default 50
, create_time datetime default current_timestamp
, update_time datetime default current_timestamp on update current_timestamp
);
模块路由
api/routers.go
package api
import (
"github.com/gin-gonic/gin"
"youge.com/api/handlers/auth"
"youge.com/api/handlers/notice"
"youge.com/internal/middleware"
)
// 统一注册入口
func RegisterAllRouters(r *gin.Engine) {
// 登录认证模块
authGroup := r.Group("/api/auth")
{
// auth.POST("/register", Register)
authGroup.POST("/login", auth.Login)
}
// 用户管理模块
// 公告模块
noticeGroup := r.Group("api/notice")
noticeGroup.Use(middleware.JWT()) // 也是需要 token 认证
{
noticeGroup.GET("/notice", notice.GetNoticeList) // get
noticeGroup.POST("/notice", notice.CreateNotice) // add
noticeGroup.PUT("/notice/:id", notice.UpdateNotice) // update
noticeGroup.DELETE("/notice/:id/delete", notice.DeleteNotice) // del
}
// ... 更多模块
}
对应的接口逻辑实现都放在 /api/handlers/notice/views.go 里面, 不做分层了哈, 直接干.
GET - 查询公告接口
前端: http://localhost:8000/api/notice/notice?page=1&limit=10
后端: /api/handlers/notice/views.go => GetNoticeList
- 分页查询, 小数据版的 limit, offset 实现
- 请求参数验证 + 默认值实现
package notice
import (
"strconv"
"time"
"github.com/gin-gonic/gin"
"youge.com/internal/db"
"youge.com/pkg/utils"
)
// 公告表
type Notice struct {
ID int `db:"id" json:"id"`
Title string `db:"title" json:"title"`
Content string `db:"content" json:"content"`
Orders string `db:"orders" json:"orders"`
CreatedAt time.Time `db:"create_time" json:"create_time"`
UpdatedAt time.Time `db:"update_time" json:"update_time"`
}
// 请求参数处理, omitempty, 表示可选
type NoticeRequest struct {
Page int `form:"page" binding:"omitempty,min=1"`
Limit int `form:"limit" binding:"omitempty,min=1,max=1000"`
}
// 接口: 获取通知列表
func GetNoticeList(c *gin.Context) {
// 绑定并验证参数
var req NoticeRequest
if err := c.ShouldBindQuery(&req); err != nil {
utils.BadRequest(c, "请求参数错误")
return
}
// 设置默认值, page=1, limit=10
if req.Page == 0 {
req.Page = 1
}
if req.Limit == 0 {
req.Limit = 10
}
// 分页查询数据
// 页面偏移量
offset := (req.Page - 1) * req.Limit
mysql := `
select
id
, title
, content
, orders
, create_time
, update_time
from notice
order by create_time desc
limit ?, ?;
`
var noticeList []Notice
if err := db.Query(¬iceList, mysql, offset, req.Limit); err != nil {
utils.BadRequest(c, "获取数据失败")
return
}
// 查询总条数
var total int
// 这个sql 肯定不会错, 除非表不在, 不校验了
db.Get(&total, "select count(*) as total from notice;")
// 返回结果
utils.Success(c, gin.H{
"list": noticeList,
"totalCount": total,
"limit": req.Limit,
})
}
补充参数验证的工具函数
主要为了对标一下 fastapi 这个我觉得超级棒的 web 框架, 现在放弃的原因主要是, go 可能性能更为主流.
这里安装一下第三方库, 它和 Python 的 pydantic 差不多呢.
go get "github.com/go-playground/validator/v10"
工具函数放在了 pkg/utils/tools.go 中了.
package utils
import (
"errors"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
// 验证和绑定前端 json 请求体
func BindAndValidate(c *gin.Context, request interface{}) error {
// 1. 绑定请求体到结构体
if err := c.ShouldBindJSON(request); err != nil {
return err
}
// 2. 验证结构体字段
validate := validator.New()
if err := validate.Struct(request); err != nil {
return err
}
return nil
}
POST - 新增公告接口
前端: http://localhost:8000/api/notice/notice
前端请求体 json : {"title": "test01", "content": "test123"}
后端: /api/handlers/notice/views.go => CreateNotice
func CreateNotice(c *gin.Context) {
// 验证前端传过来的 json 参数
var req struct {
Title string `json:"title" validate:"required,min=1,max=200"`
Content string `json:"content" validate:"required"`
}
if err := utils.BindAndValidate(c, &req); err != nil {
utils.BadRequest(c, "请求参数错误")
return
}
// 数据写入数据库
mysql := `
insert into notice(title, content)
values (?, ?);
`
rows, err := db.Exec(mysql, req.Title, req.Content)
if err != nil {
utils.BadRequest(c, "新增公告失败")
return
}
utils.Success(c, gin.H{
"affectedRows": rows,
})
}
PUT - 修改公告接口
前端 : http://localhost:8000/api/notice/notice/9
后端: /api/handlers/notice/views.go => UpdateNotice
func UpdateNotice(c *gin.Context) {
// 获取并校验路径参数
idStr := c.Param("id")
id, err := strconv.Atoi(idStr)
if err != nil {
utils.BadRequest(c, "请求参数错误")
return
}
// 根据 id 修改数据库内容
// 直接通过 var 定义的结构体是匿名的, 用于局部, 而 type 是整个包内
var req struct {
Title string `json:"title" validate:"required,min=1,max=200"`
Content string `json:"content" validate:"required"`
}
if err := utils.BindAndValidate(c, &req); err != nil {
utils.BadRequest(c, "请求参数错误")
return
}
mysql := `
update notice
set title = ?, content = ?
where id = ?;
`
rows, err := db.Exec(mysql, req.Title, req.Content, id)
// id 不存在 或者 更新异常都提示失败
if err != nil || rows == 0 {
utils.BadRequest(c, "更新数据错误")
return
}
utils.Success(c, gin.H{
"affectedRows": rows,
})
}
DELETE - 删除公告接口
前端 : http://localhost:8000/api/notice/notice/9/delete
后端: /api/handlers/notice/views.go => DeleteNotice
func DeleteNotice(c *gin.Context) {
idStr := c.Param("id")
id, err := strconv.Atoi(idStr)
if err != nil {
utils.BadRequest(c, "请求参数错误")
return
}
// 根据 id 进行删除, 错误就不处理得了, 不会错的
rows, _ := db.Exec("delete from notice where id = ?", id)
utils.Success(c, gin.H{
"affectedRows": rows,
})
}
至此, 一个常用的 CRUD 接口就写完了, 其他模块都是大同小异的, 只是复杂度不同而已.
Gin 实现基础 CRUD 接口的更多相关文章
- GO学习-(14) Go语言基础之接口
Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口类型 在Go语言中接口(interface)是一种类型,一种抽象的类 ...
- 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait
[源码下载] 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait 作者:webabcd 介绍速战速决 之 PHP 类基础 抽象类 接口 trait 示例1.类的相关知识点 1(基础 ...
- [.net 面向对象编程基础] (16) 接口
[.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目 ...
- spring中基础核心接口总结
spring中基础核心接口总结理解这几个接口,及其实现类就可以快速了解spring,具体的用法参考其他spring资料 1.BeanFactory最基础最核心的接口重要的实现类有:XmlBeanFac ...
- Go语言基础之接口
Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口介绍 在Go语言中接口(interface)是一种类型,一种抽象的类 ...
- C#基础--类/接口/成员修饰符,多态、重载、重写,静态和非静态
C#基础--类/接口/成员修饰符,多态.重载.重写,静态和非静态 类/接口/成员修饰符 C#修饰符---接口: 接口默认访问符是internal接口的成员默认访问修饰符是public C#修饰符--类 ...
- Java基础十--接口
Java基础十--接口 一.接口的定义和实例 /* abstract class AbsDemo { abstract void show1(); abstract void show2(); } 8 ...
- Java基础-面向接口(interface)编程
Java基础-面向接口(interface)编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.接口的概念 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的“类 ...
- MyBatis-Plus使用(2)-CRUD接口
参考文档:https://mybatis.plus/guide/crud-interface.html MyBatis-Plus自带的CRUD方法分为Mapper层和Service层,大多数功能是重叠 ...
- mybatispluys-Mapper CRUD 接口
Mapper CRUD 接口 通用 CRUD 封装BaseMapper (opens new window)接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部 ...
随机推荐
- 11. Docker 微服务实战(将项目打包生成镜像,在 Docker 当中作为容器实例运行)
11. Docker 微服务实战(将项目打包生成镜像,在 Docker 当中作为容器实例运行) @ 目录 11. Docker 微服务实战(将项目打包生成镜像,在 Docker 当中作为容器实例运行) ...
- Tensorflow 安装和测试(Anaconda4.7.10+windows10)
一. 软件下载 二. 配置相关 1. 修改 Jupyter notebook 默认工作路径 (1)打开 Anaconda Prompt ,输入 jupyter notebook --generate- ...
- 库卡机器人KR240电源模块维修思路讲解
一.库卡机器人KR240电源模块故障诊断 故障诊断是维修过程中的关键步骤.使用库卡提供的诊断工具或软件,对库卡机器人KR240电源模块进行故障诊断.重点关注电源供应.输出电压.电流等关键参数.通过诊断 ...
- 豆包:php如何模拟多客户端访问服务器
在 PHP 中模拟多客户端访问服务器可以通过以下几种方式实现,具体方法根据需求选择: 方法 1:使用 cURL 多请求(Multi Handle) 通过 curl_multi_* 系列函数实现并发 ...
- 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
一.Windows 版 DeepSeek-R1.Ollama 与 AnythingLLM 介绍及核心使用场景 一.组件功能与定位 DeepSeek-R1 模型特性:支持 FP16 计算 ...
- 【主流技术】Spring Boot Starter 的应用场景与自动配置
目录 前言 一.Spring Boo Starter 简介 二.如何自定义 Starter 2.1命名规范 2.2整体结构 2.3模块开发 2.3.1依赖引入 2.3.2xxxAutoConfigur ...
- rust学习笔记(1)
参考 rust圣经 参考 通过例子学习rust cargo 是rust的包管理器+编译工具 创建新项目 使用下述指令创建一个新的项目 cargo new rust_learn 执行 使用 cargo ...
- 『Plotly实战指南』--绘图初体验
今天,打算通过绘制一个简单的散点图,来开启我们 Plotly 绘图的初次尝试. 本文目的不是介绍如何绘制散点图,而是通过散点图来介绍Plotly 绘图的基础步骤. 1. 绘制散点图:初探 Plotly ...
- 自行为一加6编译Postmarket os内核
序 在为自己的一加6刷上PostmarketOS后突然某一天想使用它的照相机功能.原因是想到使用pmos拍照后笔者可以直接使用scp指令来传输手机相片到自己运行着GNU/Linux的电脑上,就感到相对 ...
- python list 差集
前言 有时候我们希望基于list得到一个集合C,该集合C的元素可以被描述为元素在集合A中而不在集合B中.即:差集. 基于set A = [1, 2, 3] B = [2, 3, 4] C = set( ...