Gin 路由注册与请求参数获取
Gin 路由注册与请求参数获取
文章目录
- Gin 路由注册与请求参数获取
- 一、Web应用开发的两种模式
- 1.前后端不分离模式
- 2.前后端分离模式
- 二、RESTful介绍
- 三、API接口
- 3.1 RESTful API设计指南
- 3.2 API与用户的通信协议
- 3.3 RestFul API接口设计规范
- 3.3.1 api接口
- 3.3.2 接口文档:
- 3.4 restful规范(10条,规定了这么做,公司可以不采用)
- 四、图书管理系统设计
- 五、Gin 路由类型
- 通配符路由
- 六、路由参数
- 6.1 获取URL后面的参数
- 6.2 获取path参数
- 6.3 取JSON参数
- 七、路由组
- 7.1 普通路由
- 7.2 路由组
- 八、重定向
- 8.1 HTTP重定向
- 8.2 路由重定向
- 九、请求参数绑定
- 9.1 获取查询参数
- 9.2 获取表单数据
- 十、路由拆分与注册
- 10.1 介绍
- 10.2 路由拆分成单独文件或包
- 10.3 路由拆分成多个文件
- 10.4 路由拆分到不同的APP
- 十一、小黄书起步:Web 接口之用户模块设计
- 11.1 用户模块分析
- 11.2 目录结构
- 11.3 Handler 的用途
- 11.4 用分组路由来简化注册
- 11.5 接收请求数据:接收请求结构体
- 11.6 接收请求数据:Bind 方法
- 11.7 校验请求:正则表达式
- 11.8 校验请求:预编译正则表达式
- 11.9 校验请求:Go 正则表达式不支持部分语法
- 11.10 校验请求:全部校验
一、Web应用开发的两种模式
1.前后端不分离模式
- 也叫前后端混合开发模式, 需要后端写模板语言(dtl), 返回的是HTML页面
- 浏览器 : 请求动态页面
- 后端 : 返回HTML
-
优点:可以直接渲染页面, 方便处理请求数据
-
缺点:耦合度非常高, 不方便扩展
2.前后端分离模式
- 前端 : 只写前端
- 后端 : 只专注于写后端接口, 返回 json, xml格式数据
- 流程 :
浏览器到静态文件服务器请求静态页面, 静态服务器返回静态页面
JS 请求达到后端, 后端再返回 JSON 或 XML格式的数据
- 优点
- 不需要管前端怎么实现, 后端开发者需要做的就是写接口
- 只需要知道, 你前端传过来什么, 然后需要后端这边传回去什么就行了
- 主要的就是操作逻辑, 解耦合性高
- 缺点
- 程序员不知道前端的具体流程, 然后对表的设计, 对业务或许就理解的没有那么透彻
- 还存在前后端联调各种问题, 前端和后端的沟通等
二、RESTful介绍
RESTful(Representational State Transfer)代表的是一种基于HTTP协议设计的软件架构风格,它通常用于构建Web服务,是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。RESTful架构的设计理念是将资源表示为URI(统一资源标识符),通过HTTP协议的GET、POST、PUT、DELETE等方法对资源进行操作。以下是RESTful架构的一些关键特点:
- 资源(Resource):在RESTful架构中,所有的数据或服务都被抽象为资源,每个资源都有一个唯一的标识符(URI)。
- 表现层(Representation):资源的表现层是指资源在不同的表示形式之间进行切换,通常使用JSON或XML格式。客户端和服务器之间通过资源的表现层进行通信。
- 状态转移(State Transfer):RESTful架构通过HTTP方法(GET、POST、PUT、DELETE等)实现状态的转移,对资源进行增删改查的操作。
- 无状态(Stateless):RESTful服务是无状态的,每个请求都包含足够的信息,使服务器能够理解和处理请求,而无需依赖之前的请求。
三、API接口
3.1 RESTful API设计指南
参考资料 阮一峰 理解RESTful架构
3.2 API与用户的通信协议
总是使用HTTPs协议。
3.3 RestFul API接口设计规范
3.3.1 api接口
- 规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介
3.3.2 接口文档:
- 可以手动写(公司有平台,录到平台里)
- 自动生成(coreapi,swagger)
3.4 restful规范(10条,规定了这么做,公司可以不采用)
-
数据的安全保障,通常使用https进行传输
-
域名中会含有API标识
https://api.example.com 尽量将API部署在专用域名
https://127.0.0.0:8080/api/ API很简单
-
请求地址中带版本信息,或者在请求头中
https://127.0.0.0:8080/api/v1/
-
任何东西都是资源,均使用名词表示 (尽量不要用动词)
https://api.example.com/v1/books/
https://api.example.com/v1/get_all_books(不符合规范)
-
请求方式区分不同操作
-
get获取:从服务器取出资源(一项或多项)
-
post新增数据:在服务器新建一个资源
-
put/patch:patch是局部更新,put是全部(基本上更新都用put)
-
delete:从服务器中删除
-
-
在请求路径中带过滤,通过在url上传参的形式传递搜索条件
https://api.example.com/v1/?name=‘金’&order=asc
https://api.example.com/v1/name?sortby=name&order=asc
https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
-
返回数据中带状态码
-
http请求的状态码
-
返回的json格式中到状态码(标志当次请求成功或失败)
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
更多状态码参考:http://tools.jb51.net/table/http_status_code
-
-
返回数据中带错误信息
-
错误处理,应返回错误信息,error当做key
{error: "Invalid API key" }
-
-
对不同操作,返回数据符合如下规范(这只是规范)
GET /books:返回资源对象的列表(数组)[{},{},{}] GET /books/1:返回单个资源对象 {} POST /books:返回新生成的资源对象 {新增的书} PUT /books/1:返回完整的资源对象 {返回修改后的} PATCH /books/1: 返回完整的资源对象 {返回修改后的} DELETE /books/1: 返回一个空文档 {status:100,msg:查询成功,data:null}
-
返回结果中带连接
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
{"link": {"rel": "collection https://www.example.com/zoos","href": "https://api.example.com/zoos","title": "List of zoos","type": "application/vnd.yourformat+json" }}
四、图书管理系统设计
例如,我们现在要编写一个管理书籍的系统,我们可以查询对一本书进行查询、创建、更新和删除等操作,我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。按照经验我们通常会设计成如下模式:
请求方法 | URL | 含义 |
---|---|---|
GET | /book | 查询书籍信息 |
POST | /create_book | 创建书籍记录 |
POST | /update_book | 更新书籍信息 |
POST | /delete_book | 删除书籍信息 |
同样的需求我们按照RESTful API设计如下:
请求方法 | URL | 含义 |
---|---|---|
GET | /book | 查询书籍信息 |
POST | /book | 创建书籍记录 |
PUT | /book | 更新书籍信息 |
DELETE | /book | 删除书籍信息 |
新建一个book.go
文件,键入如下代码:
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.GET("/book", func(c *gin.Context) {c.String(http.StatusOK, "查询书籍信息")})r.POST("/book", func(c *gin.Context) {c.String(http.StatusOK, "新增书籍信息")})r.PUT("/book", func(c *gin.Context) {c.String(http.StatusOK, "修改书籍信息")})r.DELETE("/book", func(c *gin.Context) {c.String(http.StatusOK, "删除书籍信息")})r.Run(":8080")
}
接下来我们可以使用Postman来作为客户端的来调用我们刚刚写好的接口。
五、Gin 路由类型
Gin 支持很多类型的路由:
- 静态路由:完全匹配的路由,也就是前面 我们注册的 hello 的路由。
- 参数路由:在路径中带上了参数的路由。
- 通配符路由:任意匹配的路由。
通配符路由
通配符路由究竟匹配上了什么,也是通过 Param 方法获得的。
通配符路由不能注册这种 /users/*
,/users/*/a
。也就是说,*
不能单独出现。
六、路由参数
6.1 获取URL后面的参数
- URL参数可以通过
DefaultQuery()
或Query()
方法获取 DefaultQuery()
若参数不存在则返回默认值,Query()
若不存在,返回空串- 指的是URL中
?
后面携带的参数,例如:/user/search?username=贾维斯&address=北京
。
func main() {//Default返回一个默认的路由引擎r := gin.Default()r.GET("/user/search", func(c *gin.Context) {username := c.DefaultQuery("username", "贾维斯")//username := c.Query("username")address := c.Query("address")//输出json结果给调用方c.JSON(http.StatusOK, gin.H{"message": "ok","username": username,"address": address,})})r.Run()
}
6.2 获取path参数
请求的参数通过URL路径传递,例如:/user/search/贾维斯/北京
。在Gin框架中,提供了c.Param
方法可以获取路径中的参数。 获取请求URL路径中的参数的方式如下。
func main() {//Default返回一个默认的路由引擎r := gin.Default()r.GET("/user/search/:username/:address", func(c *gin.Context) {username := c.Param("username")address := c.Param("address")//输出json结果给调用方c.JSON(http.StatusOK, gin.H{"message": "ok","username": username,"address": address,})})r.Run(":8080")
}
6.3 取JSON参数
当前端请求的数据通过JSON提交时,例如向/json
发送一个JSON格式的POST请求,则获取请求参数的方式如下:
package mainimport ("encoding/json""fmt""github.com/gin-gonic/gin""net/http"
)func main() {r := gin.Default()r.POST("/json", func(c *gin.Context) {// 注意:下面为了举例子方便,暂时忽略了错误处理b, _ := c.GetRawData() // 从c.Request.Body读取请求数据fmt.Printf("raw data: %s\n", string(b))// 定义map或结构体var m map[string]interface{}// 反序列化_ = json.Unmarshal(b, &m)c.JSON(http.StatusOK, m)})r.Run(":8080")
}
七、路由组
在Gin框架中,路由组是一种用于组织和管理路由的机制。路由组可以帮助开发者更好地组织代码,提高可读性,并且能够对一组路由应用相同的中间件。以下是关于路由组的介绍:
7.1 普通路由
普通路由是指直接注册在Gin引擎上的路由,这些路由没有被分组,是独立存在的。下面是一个普通路由的简单例子:
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()router.GET("/hello", func(c *gin.Context) {c.String(http.StatusOK, "Hello, Gin!")})router.GET("/world", func(c *gin.Context) {c.String(http.StatusOK, "World, Gin!")})router.Run(":8080")
}
上述例子中,/hello
和 /world
是两个独立的普通路由。
7.2 路由组
路由组通过Group
方法创建,可以将一组相关的路由放到同一个路由组中。通过路由组,可以更好地组织代码和应用中间件。以下是一个简单的路由组示例:
package mainimport ("github.com/gin-gonic/gin""net/http"
)func main() {router := gin.Default()// 创建一个路由组apiGroup := router.Group("/api")// 在路由组中注册路由apiGroup.GET("/users", func(c *gin.Context) {c.String(http.StatusOK, "Get Users")})apiGroup.POST("/users", func(c *gin.Context) {c.String(http.StatusOK, "Create User")})router.Run(":8080")
}
上述例子中,/api
是一个路由组,包含了两个路由 /users
(GET和POST)。这样,相同业务功能的路由被组织在一起,提高了代码的可读性和可维护性。
八、重定向
8.1 HTTP重定向
HTTP 重定向很容易。 内部、外部重定向均支持。
r.GET("/test", func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})
8.2 路由重定向
路由重定向,使用HandleContext
:
r.GET("/test", func(c *gin.Context) {// 指定重定向的URLc.Request.URL.Path = "/test2"r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"hello": "world"})
})
九、请求参数绑定
在Gin框架中,请求参数绑定是一种常见的操作,它允许你从HTTP请求中提取参数并将其绑定到Go语言结构体中。这样可以更方便地处理请求数据。以下是关于请求参数绑定的一些建议和示例:
9.1 获取查询参数
你可以使用c.Query
或c.DefaultQuery
方法来获取URL中的查询参数。
package mainimport ("github.com/gin-gonic/gin""net/http"
)type QueryParams struct {Name string `form:"name"`Age int `form:"age"`
}func main() {router := gin.Default()router.GET("/user", func(c *gin.Context) {var queryParams QueryParams// 使用 c.ShouldBindQuery 绑定查询参数到结构体if err := c.ShouldBindQuery(&queryParams); err == nil {c.JSON(http.StatusOK, gin.H{"name": queryParams.Name,"age": queryParams.Age,})} else {c.String(http.StatusBadRequest, "参数绑定失败")}})router.Run(":8080")
}
上述例子中,通过c.ShouldBindQuery
将查询参数绑定到QueryParams
结构体中,然后使用这个结构体处理请求。
9.2 获取表单数据
使用c.ShouldBind
或c.ShouldBindJSON
方法可以将POST请求的表单数据或JSON数据绑定到结构体中。
package mainimport ("github.com/gin-gonic/gin""net/http"
)type FormData struct {Name string `form:"name"`Age int `form:"age"`
}func main() {router := gin.Default()router.POST("/user", func(c *gin.Context) {var formData FormData// 使用 c.ShouldBind 绑定表单数据到结构体if err := c.ShouldBind(&formData); err == nil {c.JSON(http.StatusOK, gin.H{"name": formData.Name,"age": formData.Age,})} else {c.String(http.StatusBadRequest, "参数绑定失败")}})router.Run(":8080")
}
在上述例子中,c.ShouldBind
将表单数据绑定到FormData
结构体中。
十、路由拆分与注册
10.1 介绍
在较大的 Gin 项目中,为了保持代码的可维护性和可读性,通常会将路由进行拆分和组织。这使得每个功能模块的路由可以单独维护,并且提高了代码的可重用性。接下来,我们将介绍如何拆分和注册 Gin 框架的路由。
10.2 路由拆分成单独文件或包
首先,我们可以将不同的功能模块拆分到单独的文件或包中。例如,假设我们有一个用户模块,目录结构如下:
.
├── go.mod
├── internal
│ └── web
│ └── user.go
└── main.go
可以将其路由定义放在一个 user.go
文件中:
// Package web user.go
package webimport ("github.com/gin-gonic/gin""net/http"
)type UserHandler struct {
}func (u *UserHandler) RegisterRoutes(server *gin.Engine) {userGroup := server.Group("/user") //ug is user groupuserGroup.GET("/", getUserList)userGroup.GET("/:id", getUserByID)userGroup.POST("/", createUser)}func getUserList(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Get user list"})
}func getUserByID(c *gin.Context) {id := c.Param("id")c.JSON(http.StatusOK, gin.H{"message": "Get user by ID", "id": id})
}func createUser(c *gin.Context) {// Handle creating a new userc.JSON(http.StatusOK, gin.H{"message": "Create user"})
}
然后在主文件中导入并注册这些路由:
// main.go
package mainimport ("github.com/gin-gonic/gin""user-demo/internal/web"
)func main() {// 创建一个默认的 Gin 引擎实例server := gin.Default()// 创建一个 UserHandler 实例u := web.UserHandler{}// 使用 UserHandler 实例注册用户模块的路由u.RegisterRoutes(server) // 注册用户模块的路由// 在端口 8080 上启动 Gin 服务器server.Run(":8080") // 启动 Gin 服务器
}
10.3 路由拆分成多个文件
如果项目较大,可以将不同的功能模块的路由分别拆分到多个文件中。项目结构目录如下:
.
├── go.mod
├── go.sum
├── mian.go
└── web├── user_handlers.go└── user_routes.go
例如,将用户模块的路由拆分成 user_routes.go
和 user_handlers.go
两个文件:
// Package web user_routes.go
package webimport ("github.com/gin-gonic/gin""net/http"
)type UserHandler struct {
}func getUserList(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Get user list"})
}func getUserByID(c *gin.Context) {id := c.Param("id")c.JSON(http.StatusOK, gin.H{"message": "Get user by ID", "id": id})
}func createUser(c *gin.Context) {// Handle creating a new userc.JSON(http.StatusOK, gin.H{"message": "Create user"})
}
// Package web user_routes.go
package webimport ("github.com/gin-gonic/gin"
)func (u *UserHandler) RegisterRoutes(server *gin.Engine) {userGroup := server.Group("/user") //ug is user groupuserGroup.GET("/", getUserList)userGroup.GET("/:id", getUserByID)userGroup.POST("/", createUser)
}
在主文件中导入并注册这些路由:
// main.go
package mainimport ("demo2/web""github.com/gin-gonic/gin"
)func main() {// 创建一个默认的 Gin 引擎实例server := gin.Default()// 创建一个 UserHandler 实例u := web.UserHandler{}// 使用 UserHandler 实例注册用户模块的路由u.RegisterRoutes(server) // 注册用户模块的路由// 在端口 8080 上启动 Gin 服务器server.Run(":8080") // 启动 Gin 服务器
}
10.4 路由拆分到不同的APP
在更复杂的项目中,可能需要将不同的功能模块拆分成独立的应用(APP)。每个应用负责自己的路由和处理逻辑。这样可以更好地分离不同模块的职责。
例如,假设我们有一个用户模块和一个商品模块,其目录结构如下:
.
├── apps
│ ├── product_app
│ │ └── product.go
│ └── user_app
│ └── user.go
├── go.mod
├── go.sum
└── main.go
可以创建两个独立的应用:
// Package product_app product.go
package product_appimport ("github.com/gin-gonic/gin""net/http"
)type ProductHandler struct {
}func (p *ProductHandler) RegisterRoutes(server *gin.Engine) {productGroup := server.Group("/product")productGroup.GET("/", getProductList)productGroup.GET("/:id", getProductByID)productGroup.POST("/", createProduct)}func getProductList(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Get product list"})
}func getProductByID(c *gin.Context) {id := c.Param("id")c.JSON(http.StatusOK, gin.H{"message": "Get product by ID", "id": id})
}func createProduct(c *gin.Context) {// Handle creating a new productc.JSON(http.StatusOK, gin.H{"message": "Create product"})
}
// Package user_app user.go
package user_appimport ("github.com/gin-gonic/gin""net/http"
)type UserHandler struct {
}func (u *UserHandler) RegisterRoutes(server *gin.Engine) {userGroup := server.Group("/user") //ug is user groupuserGroup.GET("/", getUserList)userGroup.GET("/:id", getUserByID)userGroup.POST("/", createUser)}func getUserList(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Get user list"})
}func getUserByID(c *gin.Context) {id := c.Param("id")c.JSON(http.StatusOK, gin.H{"message": "Get user by ID", "id": id})
}func createUser(c *gin.Context) {// Handle creating a new userc.JSON(http.StatusOK, gin.H{"message": "Create user"})
}
在主文件中导入并注册这些路由:
// main.go
package mainimport ("demo3/apps/product_app""demo3/apps/user_app""github.com/gin-gonic/gin"
)func main() {// 创建一个默认的 Gin 引擎实例server := gin.Default()// 创建一个 UserHandler 实例u := user_app.UserHandler{}// 使用 UserHandler 实例注册用户模块的路由u.RegisterRoutes(server) // 注册用户模块的路由// 创建一个 ProductHandler 实例p := product_app.ProductHandler{}p.RegisterRoutes(server)// 在端口 8080 上启动 Gin 服务器server.Run(":8080") // 启动 Gin 服务器
}
十一、小黄书起步:Web 接口之用户模块设计
11.1 用户模块分析
我们现在要设计一个用户模块,对于一个用户模块来说,最先要设计的接口就是:注册和登录。而后要考虑提供:编辑和查看用户信息。同样的需求我们按照RESTful API设计如下:
请求方法 | URL | 含义 |
---|---|---|
GET | /users/profile | 查询用户信息 |
POST | /users/signup | 用户登录 |
POST | /users/login | 用户注册 |
POST | /users/edit | 编辑用户信息 |
首先,我们创建一个webook
目录,并且初始化go mod
mkdir webook
go mod init webook
11.2 目录结构
项目目录结构如图:
在 webook 顶级目录下有:
- main 文件,用于启动 webook。
- 一个 internal 包,里面放着的就是我们所有的业务 代码。
- 一个 pkg 包,这是我们用于存放公共库和包。
11.3 Handler 的用途
接着我们在user.go
中直接定义了一个 UserHandler
,然后将所有 和用户有关的路由都定义在了这个 Handler
上,同时,也定义了一个 RegisterRoutes
的方法,用来注册路由。这里用定义在 UserHandler
上的方法来作为对应路由的处理逻辑。
11.4 用分组路由来简化注册
你可以注意到,就是我们所有的路由都有 /users
这个前缀,要是手一抖就有可能写错,这时候可以考虑使用 Gin
的分组路由功能,修改后如下:
11.5 接收请求数据:接收请求结构体
一般来说,我们都是定义一个结构体来接受数据。这里我们使用了方法内部类 SignUpRequest
来接收数据。
11.6 接收请求数据:Bind 方法
Bind
方法是 Gin
里面最常用的用于接收请求的方法。
Bind
方法会根据 HTTP 请求的 Content-Type
来决定怎么处理。
比如我们的请求是 JSON 格式,Content-Type
是 application/json
,那么 Gin
就会使用 JSON 来反序列化。
如果 Bind
方法发现输入有问题,它就会直接返回一个错误响应到前端。
11.7 校验请求:正则表达式
在我们这个注册的业务里面,校验分为如下:
- 邮箱需要符合一定的格式:也就是账号这里,必须是一个合法的邮箱。
- 密码和确认密码需要相等:这是为了确保用户没有输错。
- 密码需要符合一定的规律:要求用户输入的密码必须不少于八位,必须要包含数字、特殊字符。
综上所述,我们用正则表达式来校验请求,正则表达式是一种用于匹配和操作文本的强大工 具,它是由一系列字符和特殊字符组成的模式,用 于描述要匹配的文本模式。正则表达式可以在文本中查找、替换、提取和验证 特定的模式。代码如图:
11.8 校验请求:预编译正则表达式
我们可以预编译正则表达式来提高校验速度。
11.9 校验请求:Go 正则表达式不支持部分语法
前面我们用的是官方自带的,但是 Go 自带的正 则表达式不支持一些语法,比如说我这里想要用 的表达式:^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$
类似于 ?=.
这种就不支持。所以我们换用另外一个开源的正则表达式匹配 库:github.com/dlclark/regexp2
。
11.10 校验请求:全部校验
整体的校验如图,注意我们区分了不同的错误,返回了不同的错误提示。
最后,完整代码如下:
user.go
文件
package webimport ("fmt"regexp "github.com/dlclark/regexp2""github.com/gin-gonic/gin""net/http"
)type UserHandler struct {emailExp *regexp.RegexppasswordExp *regexp.Regexp
}func NewUserHandler() *UserHandler {const (emailRegexPattern = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"passwordRegexPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$`)emailExp := regexp.MustCompile(emailRegexPattern, regexp.None)passwordExp := regexp.MustCompile(passwordRegexPattern, regexp.None)return &UserHandler{emailExp: emailExp,passwordExp: passwordExp,}
}func (u *UserHandler) RegisterRoutes(server *gin.Engine) {ug := server.Group("/user") //ug is user groupug.GET("/profile", u.Profile) // 查询用户信息接口ug.POST("/signup", u.SignUp) // 注册接口ug.POST("/login", u.Login) // 登录接口ug.POST("/logout", u.Logout) // 登出接口ug.POST("/edit", u.Edit) // 修改用户信息接口}func (u *UserHandler) RegisterRoutesV1(ug *gin.RouterGroup) {ug.GET("/profile", u.Profile) // 查询用户信息接口ug.POST("/signup", u.SignUp) // 注册接口ug.POST("/login", u.Login) // 登录接口ug.POST("/logout", u.Logout) // 登出接口ug.POST("/edit", u.Edit) // 修改用户信息接口}
func (u *UserHandler) Profile(ctx *gin.Context) {
}
func (u *UserHandler) SignUp(ctx *gin.Context) {type SignUpRequest struct {Email string `json:"email"`Password string `json:"password"`ConfirmPassword string `json:"confirmPassword"`}var request SignUpRequest// 如果 Bind 方法发现输入有问题,它就会直接返回一 个错误响应到前端。if err := ctx.Bind(&request); err != nil {return}ok, err := u.emailExp.MatchString(request.Email)if err != nil {ctx.String(http.StatusOK, "系统错误")return}if !ok {ctx.String(http.StatusOK, "邮箱格式错误")return}ok, err = u.passwordExp.MatchString(request.Password)if err != nil {ctx.String(http.StatusOK, "系统错误")return}if !ok {ctx.String(http.StatusOK, "密码必须包含至少一个数字、一个字母、一个特殊字符,并且长度至少为8位")return}if request.Password != request.ConfirmPassword {ctx.String(http.StatusOK, "两次密码不一致")return}ctx.String(http.StatusOK, "注册成功")fmt.Printf("请求体为:%v", request)
}
func (u *UserHandler) Login(ctx *gin.Context) {}func (u *UserHandler) Logout(ctx *gin.Context) {}
func (u *UserHandler) Edit(ctx *gin.Context) {}
main.go
文件:
package mainimport ("github.com/gin-gonic/gin""strings""time""webook/internal/web"
)func main() {server := gin.Default()u := web.NewUserHandler()u.RegisterRoutes(server)//ug := server.Group("/user/v1") //ug is user group//c.RegisterRoutesV1(ug)server.Run(":8080")
}
最后,我们通过postman 请求接口:http://127.0.0.1:8080/user/signup/
相关文章:

Gin 路由注册与请求参数获取
Gin 路由注册与请求参数获取 文章目录 Gin 路由注册与请求参数获取一、Web应用开发的两种模式1.前后端不分离模式2.前后端分离模式 二、RESTful介绍三、API接口3.1 RESTful API设计指南3.2 API与用户的通信协议3.3 RestFul API接口设计规范3.3.1 api接口3.3.2 接口文档…...

Linux第11步_解决“挂载后的U盘出现中文乱码”
学习完“通过终端挂载和卸载U盘”,我们发现U盘下的中文文件名会出现乱码,现在讲解怎么解决这个问题。其实就是复习一下“通过终端挂载和卸载U盘”,单独讲解,是为了解决问题,一次性搞好,我们会不长记性。 在…...
【第一节】安装java jdk 21
在 Java Downloads | Oracle 中国 网站下载jdk21的包 查看jdk 命令 /usr/libexec/java_home -V 设置环境变量 配置环境变量 在~/.bash_profile文件里面加入以下环境变量 export JAVA_HOME/Library/Java/JavaVirtualMachines/jdk-21.jdk/Contents/Home export PATH$PATH:$J…...

vue3+echart绘制中国地图并根据后端返回的坐标实现涟漪动画效果
1.效果图 2.前期准备 main.js app.use(BaiduMap, {// ak 是在百度地图开发者平台申请的密钥 详见 http://lbsyun.baidu.com/apiconsole/key */ak: sRDDfAKpCSG5iF1rvwph4Q95M6tDCApL,// v:3.0, // 默认使用3.0// type: WebGL // ||API 默认API (使用此模式 BMapBMapGL) });i…...

HCIA-Datacom题库(自己整理分类的)_09_Telent协议【13道题】
一、单选 1.某公司网络管理员希望能够远程管理分支机构的网络设备,则下面哪个协议会被用到? RSTP CIDR Telnet VLSM 2.以下哪种远程登录方式最安全? Telnet Stelnet v100 Stelnet v2 Stelnet v1 解析: Telnet 明文传输…...
Git专栏篇
一、基础知识 二、常用手段 1. 复制其他提交到本分支 目的:现有git仓库,该仓库有两个分支a和b,将a分支的最近三个版本提交内容复制 到b分支的提交上。 在 Linux 系统中,你可以按照以下步骤将分支 A 的最近三个版本的提交内容复…...

Java-字符串-String类
1 需求 1.1 Field Summary 1.2 Constructor Summary public String() : 空构造public String(byte[] bytes) : 把字节数组转成字符串public String(byte[] bytes,int index, int length) : 把字节数组的一部分转成字符串public String(char[] value) : 把字符数组转成字符串p…...
ubuntu安装docker指定版本
ubuntu安装docker指定版本 https://docs.docker.com/engine/install/ubuntu/ 安装apt源 # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.d…...
说一下 jsp 的 4 种作用域?
说一下 jsp 的 4 种作用域? 在 JSP(JavaServer Pages)中,有四种作用域,它们决定了对象的可见性和生命周期。这四种作用域分别是: 页面作用域(Page Scope): 页面作用域表…...

性能分析与调优: Linux 使用ELRepo升级CentOS内核
目录 一、实验 1.环境 2.agent 服务器使用ELRepo升级CentOS内核 二、问题 1. RHEL-7, SL-7 或者 CentOS-7系统如何安装ELRepo 2.RHEL-8或者RHEL-9系统如何安装ELRepo 一、实验 1.环境 (1)主机 表1-1 主机 主机架构组件IP备注prometheus 监测 系…...
【【RTC实时时钟实验 -- 在HDMI上显示-FPGA 小实验】】
RTC实时时钟实验 – 在HDMI上显示 top.v module RTS_TOP#(parameter TIME_INIT 48h24_01_06_11_08_00 ,parameter WAIT_TIME 13d8000 ,parameter SLAVE_ADDR 7b1010001 , // E2PROM 浠庢満鍦板潃parameter CLK_FR…...

Flutter 图片和资源的高效使用指南
文章目录 指定资源什么是 [pubspec.yaml](https://dart.cn/tools/pub/pubspec) 文件 图片图片常用的配置属性加载本地图片通过 pubspec.yml 文件进行配置图片目录使用 Image.asset 小部件加载本地图片 加载网络图片通过 Image.network小部件加载网络图片:使用Image.…...
RedisTemplate 怎么获取到链接信息?怎么获取到所有key?怎么获取指定key?
获取Redis的链接信息: (RedisTemplate<String, ?> redisTemplate) {RedisConnectionFactory connectionFactory redisTemplate.getConnectionFactory();(!(connectionFactory LettuceConnectionFactory)) {System..println();;}LettuceConnectionFactory l…...
【Unity】动态申请权限
1、AndroidManifest.xml在<application></application>内添加一行: <meta-data android:name"unityplayer.SkipPermissionsDialog" android:value"true" /> 作用:屏蔽应用启动时弹出申请权限弹窗(危…...

tp8/6 插件PhpOffice\PhpSpreadsheet导入表格
一、安装 composer require phpoffice/phpspreadsheet 官网:phpoffice/phpspreadsheet - Packagist 二、代码 <?php namespace app\services\upload\model; use app\services\BaseServices; use \PhpOffice\PhpSpreadsheet\Spreadsheet; use \PhpOffice\Php…...

Android studio VideoView 应用设计
一、运行效果: 二、新建empty activity项目: 三、打开activity_main.xml布局文件,添加VideoView: <VideoViewandroid:id="@+id/videoView"android:layout_width="368dp"android:layout_height="573dp"app:layout_constraintBottom_toBot…...
Python基础(十八、文件操作读取)
文章目录 一、open方法二、read和readlines方法三、readline方法四、关闭操作五、with open语句总结 一、open方法 Python 中可以使用 open 方法来打开一个文件,该方法会返回一个文件对象。open 方法的语法如下: file_object open(file_name, mode)其…...

Mac 16g约等于Windows多少g?
Mac 16g 内存等于 Windows 320g 内存 何为“黄金内存”? Mac 的内存是用黄金做的,而 Windows 的内存是用铁做的。 黄金的密度是 19.32 g/cm,而铁的密度是 7.874 g/cm。 因此,16g 的黄金体积是 0.082 cm,而 16g 的铁…...

快麦ERP退货借助APPlink快速同步CRM
什么是APPlink? APPlink是RestCloud打造的一款简单易用的零代码自动化集成平台,为业务流程提供自动化的解决方案,将企业内部的核心系统以及第三方应用程序和云服务等进行集成。无论是开发人员还是业务人员,都可以使用APPlink轻松…...
springMVC获取请求参数的方式
文章目录 springmvc获取参数的方式1、ServletAPI获取参数(原生态)2、通过控制器的形参取值3、 RequestParam4、通过POJO获取请求参数 springmvc获取参数的方式 1、ServletAPI获取参数(原生态) 将HttpServletRequest作为控制器方…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...