Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错
借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范
要安装Gin软件包,您需要安装Go并首先设置Go工作区。
1.首先需要安装Go(需要1.10+版本),然后可以使用下面的Go命令安装Gin。
如果大家是使用GoLand编辑器的话,可以直接在命令行窗口里面运行下面的命令
go get -u github.com/gin-gonic/gin
2.将其导入您的代码中:
import “github.com/gin-gonic/gin”
3.(可选)导入net/http。例如,如果使用常量,则需要这样做http.StatusOK。
import “net/http”
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 1.创建路由
r := gin.Default()//创建的Engine
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
r.GET("/:name", func(c *gin.Context) {
name := c.Param("name")//参数路由
id := c.Query("id")//查询参数
c.String(http.StatusOK, "hello World! ,%s ,id is %s", name, id)
})
// 3.监听端口,默认在8080
// Run("里面不指定端口号默认为8080")
err := r.Run(":8000")
if err != nil {
return
}
}
这样一个简单Gin使用案例就实现了。接着我们执行该文件,命令行窗口中看到输出监听端口就说明已经启动成功了
启动失败的最常见原因就是端口失败了,下面是一个端口占用的错误。
在Gin里面,一个web服务器被抽象成为Engine。你可以在一个应用里面创建多个Engine实例,监听不同的端口。
Engine承担了路由注册、接入middleware的核心职责。,笔者在查看源码的时候,发现Engine其实是组合了RouterGroup,RouterGroup才是实现路由功能的核心组件。
gin.Context是Gin里面的核心类型,该类十分重要,我们会频繁与其打交道。
在Gin里面,它的核心职责有两个:
上图中的Request代表的就是请求,Writer代表的就是响应。
Gin为每一个HTTP方法(最常用的就是Get和Post)都提供了一个注册路由的方法。
基本上都是两个参数:
r.GET("/views?id=*.html", func(context *gin.Context) {
path := context.Param(".html")
context.String(http.StatusOK, "匹配上的值是,%s", path)
})
在面对一个业务的时候,该怎么设计路由?
适用于初学者的两条原则:
对于一个用户模块来说,最先要设计的接口就是:注册和登录。
而后要考虑提供:编辑和查看用户信息。
这里,我们直接定义一个UserHandler,然后将所有和用户有关的路由都定义在这个Handler上,也可以理解用定义在UserHandler上的方法来作为对应路由的处理逻辑。
package web
import (
"github.com/gin-gonic/gin"
"net/http"
)
type UserHandler struct {
}
func (c *UserHandler) SignUp(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello,你在注册")
}
func (c *UserHandler) Login(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello,你在登录")
}
func (c *UserHandler) EditUser(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello,你在编辑")
}
func (c *UserHandler) UserProfile(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello,你在查看")
}
func (c *UserHandler) RegisterRoutes(server *gin.Engine) {
ug := server.Group("/users")//分组路由
ug.POST("/signup", c.SignUp)
ug.POST("/login", c.Login)
ug.POST("/edit", c.EditUser)
ug.GET("/profile", c.UserProfile)
}
package main
import (
"awesomeProject/src/internal/web"
"github.com/gin-gonic/gin"
)
func main() {
// 1.创建路由
r := gin.Default()
// 2.绑定路由规则,执行的函数
c := &web.UserHandler{}
c.RegisterRoutes(r)
// 3.监听端口,默认在8080
// Run("里面不指定端口号默认为8080")
err := r.Run(":8000")
if err != nil {
return
}
}
接下来,按照一般的流程,前端或者客户端就会调用我们的接口,例如(调用/users/signup接口实现注册功能)将相关的参数传递给后端,通常都是json格式的数据。
那现在,后端拿到相关的数据之后改如何处理呢?
通常分为三步:
因此,我们的问题就是怎么在后端接收这个JSON数据?
一般来说,我们都是定义一个结构体来接受数据。
func (c *UserHandler) SignUp(ctx *gin.Context) {
//定义在方法内部的好处在于只有该方法能够使用SignUpReq
type SignUpReq struct {
Email string `json:"email"`
Password string `json:"password"`
ConfirmPassword string `json:"confirmPassword"`
}
ctx.String(http.StatusOK, "hello,你在注册")
}
Bind方法是Gin里面最常用的用于接收请求的方法。
Bind方法会根据HTTP请求的Content-Type来决定怎么处理。比如我们的请求是JSON格式,Content-Type是application/json,那么Gin就会使用JSON来反序列化。
如果Bind方法发现输入有问题,它就会直接返回一个错误响应到前端。这句话怎么理解呢?例如,我们定义的email是string类型,如果前端传过来一个整型的话,那么gin就会直接返回一个400错误码给前端。
func (c *UserHandler) SignUp(ctx *gin.Context) {
//定义在方法内部的好处在于只有该方法能够使用SignUpReq
type SignUpReq struct {
Email string `json:"email"`
Password string `json:"password"`
ConfirmPassword string `json:"confirmPassword"`
}
var req SignUpReq
if err := ctx.Bind(&req); err != nil {
return
}
//ctx.String(http.StatusOK, "hello,你在注册")
ctx.JSON(200, gin.H{
"msg": "hello,你在注册",
})
}
校验请求无可避的需要使用到正则表达式,一般我们获取到参数之后都会对参数进行校验要求其符合一定的格式然后才允许入库。这里就涉及到相关的业务逻辑了,我们暂时抛开不说。
正则匹配库推荐使用一个开源的正则匹配库:https://github.com/dlclark/regexp2,官方自带的正则表达式笔者查阅了一些资料好像是不支持一些语法的。
目前大多数的web框架,都提供了CORS的解决方案。Gin里面提供了一个middleware来解决跨域问题。
执行下面命令即可在代码中使用。
go get github.com/gin-gonic/contrib
熟悉Android中okhttp框架的同学可以用拦截器来理解middleware,所有的请求都会经过这些middleware。所以适合解决一些所有业务都关心的东西。
func (c *UserHandler) RegisterRoutes(server *gin.Engine) {
//需要在POST或者GET方法之前调用,无论接口访问成功或失败middleware都会执行
server.Use(
func(context *gin.Context) {
fmt.Println("第一个 middleware")
}, func(context *gin.Context) {
fmt.Println("第二个 middleware")
}, cors.New(cors.Config{
AllowCredentials: true,//是否允许带上用户认证信息(比如cookie)
AllowedHeaders: []string{"Content-Type"},//业务请求中可以带上的头
AllowOriginFunc: func(origin string) bool {//哪些来源是允许的
println("origin is " + origin)
if strings.HasPrefix(origin, "http://localhost") {
return true
}
return true
},
MaxAge: 12 * time.Hour,
}))
ug := server.Group("/users") //分组路由
ug.POST("/signup", c.SignUp)
ug.POST("/login", c.Login)
ug.POST("/edit", c.EditUser)
ug.GET("/profile", c.UserProfile)
}