介绍
-
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 mainimport ("net/http""github.com/gin-gonic/gin"
)func main() {// 1.创建路由r := gin.Default()//创建的Engine// 2.绑定路由规则,执行的函数// gin.Context,封装了request和responser.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.Engine
在Gin里面,一个web服务器被抽象成为Engine。你可以在一个应用里面创建多个Engine实例,监听不同的端口。
Engine承担了路由注册、接入middleware的核心职责。,笔者在查看源码的时候,发现Engine其实是组合了RouterGroup,RouterGroup才是实现路由功能的核心组件。
gin.Context
gin.Context是Gin里面的核心类型,该类十分重要,我们会频繁与其打交道。
在Gin里面,它的核心职责有两个:
- 处理请求
- 返回响应
上图中的Request代表的就是请求,Writer代表的就是响应。
Gin路由注册
Gin为每一个HTTP方法(最常用的就是Get和Post)都提供了一个注册路由的方法。
基本上都是两个参数:
- 路由规则:比如说/hello这种静态路由。
- 处理函数;也就是我们注册的,返回hello,world的方法。
路由分类
- 静态路由:完全匹配的路由,也就是前面我们注册的hello的路由。
- 参数路由:在路径中带上了参数的路由。
- 通配符路由:任意匹配的路由。
r.GET("/views?id=*.html", func(context *gin.Context) {path := context.Param(".html")context.String(http.StatusOK, "匹配上的值是,%s", path)})
该用什么方法?什么路由?
在面对一个业务的时候,该怎么设计路由?
- 应该用GET方法还是POST方法?
- 该用路由参数?还是查询参数?还是把参数放到body里面?
适用于初学者的两条原则:
- 用户是查询数据的,用GET,参数放到查询参数里面,即?a=123这种。
- 用户是提交数据的,用POST,参数全部放到Body里面。
使用Gin设计一个用户模块相关的接口
对于一个用户模块来说,最先要设计的接口就是:注册和登录。
而后要考虑提供:编辑和查看用户信息。
这里,我们直接定义一个UserHandler,然后将所有和用户有关的路由都定义在这个Handler上,也可以理解用定义在UserHandler上的方法来作为对应路由的处理逻辑。
package webimport ("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 mainimport ("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) {//定义在方法内部的好处在于只有该方法能够使用SignUpReqtype SignUpReq struct {Email string `json:"email"`Password string `json:"password"`ConfirmPassword string `json:"confirmPassword"`}ctx.String(http.StatusOK, "hello,你在注册")
}
接收请求数据:Bind方法
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) {//定义在方法内部的好处在于只有该方法能够使用SignUpReqtype SignUpReq struct {Email string `json:"email"`Password string `json:"password"`ConfirmPassword string `json:"confirmPassword"`}var req SignUpReqif 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)}