Gin框架学习 (二)

Gin框架

1. context的用处

Golang 甚至把用 Context 称为一种并发模式,标准库中定义了 Context 接口,并且在多种第三方库中都有支持。

其实原因和 goroutine 有关,goroutine 易于创建,更容易泄露,而且也没有什么通用的接口结束一个 goroutine(甚至连 ID 都拿不到)。

所以 Context 模式声明一些接口,显式传递给子函数,子函数的 goroutine 主动检查 Context 的状态并作出正确的响应。

Context 是一种接口,相同请求范围内的 goroutine 需要主动检查 Context 的状态来进行合适的处理:

Done() <-chan struct{
    
    }:返回一个管道,当 Context 取消或者超时的时候会被关闭
Err() error:返回 Done 管道关闭的原因
Deadline() (deadline time.Time, ok bool):返回将要超时的时间
Value(key interface{
    
    }) interface{
    
    }:返回 Context 的值

Golang 建议 Context 作为第一个参数主动进行传递,表示子函数位于指定的上下文中,比如轻量级 Web 框架 gin 的每个请求在独立的 goroutine 中执行,每个 view 函数接受一个 Context 结构,此结构实现了 Context 接口,如进行 streaming 处理:

func Messages(ctx *gin.Context) {
    
    
      messagesCh := make(chan interface{
    
    }, 10)

      queryMessages(ctx, messagesCh)

      ctx.Stream(func(w io.Writer) bool {
    
    
          select {
    
    
          case message, ok := <-messagesCh:
              if ok {
    
    
                  w.Write(MessageToBytes(message))
              }
              return ok
          case <-ctx.Done():
              return false
          }
      })
  }

假设 queryMessages 函数会启动 goroutine 来查询数据,通过 messagesCh 返回数据,ctx.Stream 的回调会不断轮询 messagesCh 来向客户端返回数据,同时也关注 Context 是否结束并提前结束请求。

同时 Context 支持嵌套:
父 context 关闭同时也会关闭子 context。
切记不要传递 nil 作为 context,如果不确定使。用哪个具体的类型,请传递 context.TODO。

Next方法——它的作用就是先执行以下一个中间件,执行完了再回来继续执行接下来的逻辑。
Abort方法——中断操作,用于阻断请求。

实例:

func CheckToken(c *gin.Context) {
    
    
	session, err := GetSession(c)
	if err != nil {
    
    
		if err == http.ErrNoCookie {
    
    
			log.Errorf("In CheckToken GetSession fail: %v", err)
			c.Error(ErrUnauthorized)
		} else if err == badger.ErrKeyNotFound {
    
    
			log.Errorf("In CheckToken GetSession fail: %v", err)
			c.Error(ErrOutdatedSession)
		} else {
    
    
			log.Errorf("In CheckToken GetSession fail: %v", err)
			c.Error(ErrInternalServerError)
		}

		c.Abort()
		return
	}

	log.Infof("session: %+v", session)

	c.Set("sid", session.Sid)
	c.Set("user_id", session.UserID)
	c.Set("user_name", session.UserName)

	RefreshSession(session)

	c.Next()
}

https://www.zhihu.com/question/269905592/answer/359259436

2. http.HandlerFunc()

//http.Handler
type Handler interface {
    
    
    ServeHTTP(ResponseWriter, *Request)
}

一般当创建一个如下的方法时,可通过http.HandlerFunc(name)转换为Handler类型,因为HanderFunc里有ServeHttp方法,实现了Handler接口。

type HandlerFunc func(ResponseWriter, *Request)

func (HandlerFunc) ServeHTTP

示例:

// ErrorHandler 对错误结果统一处理
func ErrorHandler() gin.HandlerFunc {
    
    
	return func(c *gin.Context) {
    
    
		c.Next()
		errs := c.Errors.ByType(gin.ErrorTypeAny)
		if len(errs) > 0 {
    
    
			err := errs.Last().Err
			switch err.(type) {
    
    
			case *ApiError:
				parsedErr := err.(*ApiError)
				c.JSON(parsedErr.HTTPCode, wrapFailResponse(parsedErr.StatusCode, parsedErr.Status))
				return
			default:
			}
		}
	}
}

3. gin路由配置

1. 基本路由
gin框架封装了http库,提供了 GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS 这些http请求方式。

使用 router.method() 来绑定路由:

router := gin.Default()

router.GET("/get", func(c *gin.Context) {
    
     c.JSON(200, gin.H{
    
    "message": "get方法"}) })

router.POST("/post", func(c *gin.Context) {
    
     c.JSON(200, gin.H{
    
    "message": "post方法"}) })

router.PUT("/put", func(c *gin.Context) {
    
     c.JSON(200, gin.H{
    
    "message": "put方法"}) })

router.DELETE("/delete", func(c *gin.Context) {
    
     c.JSON(200, gin.H{
    
    "message": "delete"}) })

router.PATCH("/patch", func(c *gin.Context) {
    
     c.JSON(200, gin.H{
    
    "message": "patch"}) })

router.HEAD("/head", func(c *gin.Context) {
    
     c.JSON(200, gin.H{
    
    "message": "head"}) })

router.OPTIONS("/options", func(c *gin.Context) {
    
     c.JSON(200, gin.H{
    
    "message": "options"}) })

router.Run(":9999")//指定端口  localhost:9999

2.分组路由

func serverAuthAPI(router *gin.Engine) {
    
    
	r := router.Group("/api/auth")
	r.POST("/login", LoginHandler)
	r.POST("/logout", CheckToken, ShouldChangePassword, LogoutHandler)
	r.GET("/captcha", GenerateCaptchaHandler)
}
	//没有路由的页面
	//为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码
	htmlDir := config.GetConfig().ClientControlServer.HTMLDir
	router.Use(static.Serve("/", static.LocalFile(htmlDir, false)))
	router.NoRoute(func(c *gin.Context) {
    
    
		c.File(filepath.Join(htmlDir, static.INDEX))
	})

3. 设置中间件
Gin Mode的选择:开发调试过程中,使用debug模式就可以了。在上线的时候,一定要选择release模式。test可以用在测试场景中。

	gin.SetMode(gin.DebugMode)
	router := gin.New()
	router.Use(LoggerByTime())//中间件:日志
	router.Use(gin.Recovery())
	router.Use(Cors())//中间件:跨域
	router.Use(ErrorHandler())	//统一错误处理
// ErrorHandler 对错误结果统一处理
func ErrorHandler() gin.HandlerFunc {
    
    
	return func(c *gin.Context) {
    
    
		c.Next()	//先按序执行相应服务,结果返回时才进入错误信息处理
		errs := c.Errors.ByType(gin.ErrorTypeAny)
		if len(errs) > 0 {
    
    
			err := errs.Last().Err
			switch err.(type) {
    
    
			case *ApiError:
				parsedErr := err.(*ApiError)
				c.JSON(parsedErr.HTTPCode, wrapFailResponse(parsedErr.StatusCode, parsedErr.Status))
				return
			default:
			}
		}
	}
}

https://blog.csdn.net/yuyinghua0302/article/details/105296475

4. gin获取URL参数

1. 获取URL路径全部参数
以/为分割符,每个参数以“:”为参数表示动态变量,会自动绑定到路由对应的参数上
路由规则:[:]表示可以不用匹配

比如:http://localhost:8080/user/李四/20/北京/男 将匹配 “http://localhost:8080/user/:name/:age/:address/:sex”
上面的这个链接中,可以通过向上面讲的使用/user/:name/:age/:address/:sex来分别匹配李四、20、北京、男

获取参数:

//http://localhost:8080/user/李四/20/北京/男
router.GET("/user/:name/:age/:address/:sex", func(c *gin.Context) {
    
    
    //打印URL中所有参数
    //"[{name 李四} {age 20} {address 北京} {sex 男}]\n"
    c.JSON(http.StatusOK, fmt.Sprintln(c.Params))
})

2. 获取URL路径单个参数
使用gin.Context对象的Param(key)方法获取某一个key的值,方法声明如下:

//http://localhost:8080/login/15949629528/123456
router.GET("/login/:name/:password", func(c *gin.Context) {
    
    
  c.JSON(http.StatusOK, gin.H{
    
    
    //{ name: "15949629528", password: "123456" }
    "name":     c.Param("name"),
    "password": c.Param("password"),
  })
})

访问:http://localhost:8080/login/15949629528/123456
输出结果:{ name: “15949629528”, password: “123456” }

3. 获取URL中指定的参数【GET、POST请求】
比如:http://localhost:8080/login?name=张三&password=123456
可以使用接下在的方法获取请求参数name、password的值:

//返回URL中key的值
func (c *Context) Query(key string) string
//GET请求
router.GET("/login", func(c *gin.Context) {
    
    
  //{ name: "张三", password: "123456" }
  c.JSON(http.StatusOK, gin.H{
    
    
    "name":     c.Query("name"),
    "password": c.Query("password"),
  })
})

//POST请求
router.POST("/login", func(c *gin.Context) {
    
    
	//{"name":"张三","password":"123456"}
	c.JSON(http.StatusOK, gin.H{
    
    
		"name":     c.Query("name"),
		"password": c.Query("password"),
	})
})

访问:http://localhost:8080/login?name=张三&password=123456
输出结果:{ name: “张三”, password: “123456” }

4. 获取指定默认值的参数的【GET、POST请求】
gin.Context.DefaultQuery()方法,允许你指定接收的参数名,以及没有接收到该参数值时,设置的默认值,声明如下:

func (c *Context) DefaultQuery(key, defaultValue string) string

只有当请求没有携带key,那么此时的默认值就会生效。其他情况,默认值不生效。即使URL中的该key的值为空,那么也不会启用默认值,获取的值就是空。

获取URL中的参数值:

//GET请求
router.GET("/user", func(c *gin.Context) {
    
    
  //{ name: "张三", password: "123456" }
  c.JSON(http.StatusOK, gin.H{
    
    
    "name":     c.DefaultQuery("name", "默认张三"),
    "password": c.DefaultQuery("password", "默认密码"),
  })
})

//POST请求
router.POST("/user", func(c *gin.Context) {
    
    
//{"name":"张三","password":"默认密码"}
	c.JSON(http.StatusOK, gin.H{
    
    
	  "name":     c.DefaultQuery("name", "默认张三"),
	  "password": c.DefaultQuery("password", "默认密码"),
	})
})

访问:http://localhost:8080/user?password=
输出结果:{ name: “默认张三”, password: “默认密码” }

【Gin框架二】Gin基本路由(1)

猜你喜欢

转载自blog.csdn.net/weixin_43202635/article/details/115162081