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作为控制器方…...
android常用方法
获取应用安装来源 private String getAppInstaller(Context context, String packageName) {return context.getPackageManager().getInstallerPackageName(packageName);}判断是否系统应用 在/system/app 或者 /system/priv-app目录下的应用。 public boolean isSystem(Conte…...
Linux内核--网络协议栈(一)Socket通信原理和实例讲解
目录 一、引言 二、Socket ------>2.1、socket编程 ------>2.2、Socket的创建 三、收发数据 四、断开连接 五、删除套接字 六、网络 IO 一、引言 本章开始进入linux内核中网络部分的学习,先简单介绍一下socket套接字 二、Socket 一个数据包经由应用程序产生…...
Spring事务(2):声明式事务管理案例-转账(xml、注解)
1 编写转账案例,引出事务管理问题 需求:账号转账,Tom账号取出1000元,存放到Jack账号上 1.1 建表脚本(MySQL) CREATE TABLE t_account (id INT(11) NOT NULL AUTO_INCREMENT,name VARCHAR(20) NOT NULL,m…...
NACHI机器人模拟示教器如何切换中文
前言 现在开始学习机器人的编程语言,那么要学习会用首先得用模拟示教器来学习,但是全是英文确实比较难受一些些,没有中文来的直观。所以摸透一下如何给示教器更换语言。 具体步骤 步骤一:将中文的汉化包下载下来。具体的下载链…...
用通俗易懂的方式讲解:使用 Mistral-7B 和 Langchain 搭建基于PDF文件的聊天机器人
在本文中,使用LangChain、HuggingFaceEmbeddings和HuggingFace的Mistral-7B LLM创建一个简单的Python程序,可以从任何pdf文件中回答问题。 一、LangChain简介 LangChain是一个在语言模型之上开发上下文感知应用程序的框架。LangChain使用带prompt和few…...
综合智慧能源监测管理平台,实现能源管理“透明”化
能源问题是全球面临的最大问题,在提高经济增长的同时,也引发了能源供应危机及环境严重等问题,降低能源管理、低碳环保是我们未来发展的必经之路。 为了解决这一问题,智慧能源管理平台应运而生。平台采用微服务架构,整…...
【大数据进阶第三阶段之Datax学习笔记】使用阿里云开源离线同步工具DataX 实现数据同步
【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax概述 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax快速入门 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax类图 【大数据进阶第三阶段之Datax学习笔记】使用…...
kotlin chunked 和 windowed
kotlin chunked的作用 将集合按照指定的数量分割成多个结合 val numbers listOf(0,1,2,3,4,5,6,7,8,9) //把集合按照一个结合3个元素分割 Log.d("chunked", numbers.chunked(3).toString()) // 打印结果 [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] kotlin windowed…...
C语言光速入门笔记
C语言是一门面向过程的编译型语言,它的运行速度极快,仅次于汇编语言。C语言是计算机产业的核心语言,操作系统、硬件驱动、关键组件、数据库等都离不开C语言;不学习C语言,就不能了解计算机底层。 目录 C语言介绍C语言特…...
Flutter+Go_Router+Fluent_Ui仿阿里网盘桌面软件开发跨平台实战-买就送仿小米app开发
Flutter是谷歌公司开发的一款开源、免费的UI框架,可以让我们快速的在Android和iOS上构建高质量App。它最大的特点就是跨平台、以及高性能。 目前 Flutter 已经支持 iOS、Android、Web、Windows、macOS、Linux 的跨平台开发。 Flutter官方介绍,目前Flutte…...
美国做科普的网站/今日新闻头条新闻
2014年9月2日,魅族召开发布会,公布最新旗舰机型 MX4 ,对比几个月前备受关注的锤子手机和小米 M4 ,新一代手机品牌的传播方式已经基本成型。再过一周,苹果最新的发布会也将到来,大屏 iPhone 6 的发布已经几乎…...
wordpress极简淘客主题/湖北seo关键词排名优化软件
Description vani和cl2在一片树林里捉迷藏…… 这片树林里有N座房子,M条有向道路,组成了一张有向无环图。 树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。如果从房子A沿着路走下去能够到达B&#…...
nike建设网站的目的/网络广告策划书
随时随地阅读更多技术实战干货,获取项目源码、学习资料,请关注源代码社区公众号(ydmsq666) from:https://blog.csdn.net/lmj623565791/article/details/79623159 很多时候,由于迭代周期有限,开发任务多、时间紧&#…...
怎么看网站开发的技术/seo每日工作
故障起因:最近的天气温度骤升骤降,咱随时增减衣服倒没感冒,可我那台电脑却得了“重感冒”。如果天气暖和电脑就“活蹦乱跳”的,但如果气温有些低的话,每次得开两三次机电脑才能正常启动。而这几天寒流来了,…...
视频直播网站/怎样推广网站
/** 1.ruby环境 官网下载的:tar -zxvf xxxxx安装包xxxx 解压到/opt/ruby/下 执行命令 cd ruby ./configure --prefix/usr/local/ruby; 文件夹如果不存在就新建 make make install 2.ruby的redis客户端安装 下载:https://rubygems.org/downloads/redi…...
自学建立网站/花钱推广的网络平台
一、new只是隐藏父类中的同名方法。基类和父类中都存在这个方法。 namespace ConsoleApplication1 {class Program{static void Main(string[] args){BaseClass bcdc new DerivedClass();bcdc.Method2(); //结果:Base - Method2Console.Read();}public class BaseC…...