小言_互联网的博客

Golang 入门-Gin框架深入了解使用

584人阅读  评论(0)

目录

框架架构

HTTP 服务器

生命周期

Context

路由

基本路由

路由参数

路由群组

控制器

数据解析绑定

请求

请求头

请求参数

Cookies

上传文件

响应

响应头

附加Cookie

字符串响应

JSON/XML/YAML响应

视图响应

文件响应

重定向

同步异步

视图

传参

视图组件

中间件

分类使用方式 

自定义中间件

中间件参数

内置中间件 (简单认证BasicAuth)

数据库

Mongodb


框架架构

  • HTTP 服务器

1.默认服务器

router.Run()

2.HTTP 服务器

除了默认服务器中 router.Run() 的方式外,还可以用 http.ListenAndServe(),比如


  
  1. func main() {
  2. router := gin.Default()
  3. http.ListenAndServe( ":8080", router)
  4. }

或者自定义 HTTP 服务器的配置:


  
  1. func main() {
  2. router := gin.Default()
  3. s := &http.Server{
  4. Addr: ":8080",
  5. Handler: router,
  6. ReadTimeout: 10 * time.Second,
  7. WriteTimeout: 10 * time.Second,
  8. MaxHeaderBytes: 1 << 20,
  9. }
  10. s.ListenAndServe()
  11. }

3.HTTP 服务器替换方案 想无缝重启、停机吗? 以下有几种方式:

我们可以使用 fvbock/endless 来替换默认的 ListenAndServe。但是 windows 不能使用。


  
  1. router := gin.Default()
  2. router.GET( "/", handler)
  3. // [...]
  4. endless.ListenAndServe( ":4242", router)

除了 endless 还可以用manners:

manners兼容windows

manners.ListenAndServe(":8888", r)

如果你使用的 golang 版本大于 1.8 版本, 那么可以用 http.Server 内置的 Shutdown 方法来实现优雅的关闭服务, 一个简单的示例代码如下:


  
  1. srv := http.Server{
  2. Addr: ":8080",
  3. Handler: router,
  4. }
  5. go func() {
  6. if err :+ srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  7. log.Fatalf( "listen: %s\n", err)
  8. }
  9. }
  10. // 其他代码, 等待关闭信号
  11. ...
  12. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  13. defer cancel()
  14. if err := srv.Shutdown(ctx); err != nil {
  15. log.Fatal( "Server Shutdown: ", err)
  16. }
  17. log.Println( "Server exiting")
  • 生命周期

  • Context

路由

  • 基本路由

gin 框架中采用的路由库是 httprouter


  
  1. // 创建带有默认中间件的路由:
  2. // 日志与恢复中间件
  3. router := gin.Default()
  4. //创建不带中间件的路由:
  5. //r := gin.New()
  6. router.GET( "/someGet", getting)
  7. router.POST( "/somePost", posting)
  8. router.PUT( "/somePut", putting)
  9. router.DELETE( "/someDelete", deleting)
  10. router.PATCH( "/somePatch", patching)
  11. router.HEAD( "/someHead", head)
  12. router.OPTIONS( "/someOptions", options)
  • 路由参数

api 参数通过Context的Param方法来获取


  
  1. router.GET( "/string/:name", func(c *gin.Context) {
  2. name := c.Param( "name")
  3. fmt.Println( "Hello %s", name)
  4. })

URL 参数通过 DefaultQuery 或 Query 方法获取


  
  1. // url 为 http://localhost:8080/welcome?name=ningskyer时
  2. // 输出 Hello ningskyer
  3. // url 为 http://localhost:8080/welcome时
  4. // 输出 Hello Guest
  5. router.GET( "/welcome", func(c *gin.Context) {
  6. name := c.DefaultQuery( "name", "Guest") //可设置默认值
  7. // 是 c.Request.URL.Query().Get("lastname") 的简写
  8. lastname := c.Query( "lastname")
  9. fmt.Println( "Hello %s", name)
  10. })

表单参数通过 PostForm 方法获取


  
  1. //form
  2. router.POST( "/form", func(c *gin.Context) {
  3. type := c.DefaultPostForm( "type", "alert") //可设置默认值
  4. msg := c.PostForm( "msg")
  5. title := c.PostForm( "title")
  6. fmt.Println( "type is %s, msg is %s, title is %s", type, msg, title)
  7. })
  • 路由群组


  
  1. someGroup := router.Group( "/someGroup"){
  2. someGroup.GET( "/someGet", getting)
  3. someGroup.POST( "/somePost", posting)
  4. }

控制器

  • 数据解析绑定

模型绑定可以将请求体绑定给一个类型,目前支持绑定的类型有 JSON, XML 和标准表单数据 (foo=bar&boo=baz)。 要注意的是绑定时需要给字段设置绑定类型的标签。比如绑定 JSON 数据时,设置 json:"fieldname"。 使用绑定方法时,Gin 会根据请求头中 Content-Type 来自动判断需要解析的类型。如果你明确绑定的类型,你可以不用自动推断,而用 BindWith 方法。 你也可以指定某字段是必需的。如果一个字段被 binding:"required" 修饰而值却是空的,请求会失败并返回错误。


  
  1. // Binding from JSON
  2. type Login struct {
  3. User string `form:"user" json:"user" binding:"required"`
  4. Password string `form:"password" json:"password" binding:"required"`
  5. }
  6. func main() {
  7. router := gin.Default()
  8. // 绑定JSON的例子 ({"user": "manu", "password": "123"})
  9. router.POST( "/loginJSON", func(c *gin.Context) {
  10. var json Login
  11. if c.BindJSON(&json) == nil {
  12. if json.User == "manu" && json.Password == "123" {
  13. c.JSON(http.StatusOK, gin.H{ "status": "you are logged in"})
  14. } else {
  15. c.JSON(http.StatusUnauthorized, gin.H{ "status": "unauthorized"})
  16. }
  17. }
  18. })
  19. // 绑定普通表单的例子 (user=manu&password=123)
  20. router.POST( "/loginForm", func(c *gin.Context) {
  21. var form Login
  22. // 根据请求头中 content-type 自动推断.
  23. if c.Bind(&form) == nil {
  24. if form.User == "manu" && form.Password == "123" {
  25. c.JSON(http.StatusOK, gin.H{ "status": "you are logged in"})
  26. } else {
  27. c.JSON(http.StatusUnauthorized, gin.H{ "status": "unauthorized"})
  28. }
  29. }
  30. })
  31. // 绑定多媒体表单的例子 (user=manu&password=123)
  32. router.POST( "/login", func(c *gin.Context) {
  33. var form LoginForm
  34. // 你可以显式声明来绑定多媒体表单:
  35. // c.BindWith(&form, binding.Form)
  36. // 或者使用自动推断:
  37. if c.Bind(&form) == nil {
  38. if form.User == "user" && form.Password == "password" {
  39. c.JSON( 200, gin.H{ "status": "you are logged in"})
  40. } else {
  41. c.JSON( 401, gin.H{ "status": "unauthorized"})
  42. }
  43. }
  44. })
  45. // Listen and serve on 0.0.0.0:8080
  46. router.Run( ":8080")
  47. }

请求

  • 请求头

  • 请求参数

  • Cookies

  • 上传文件


  
  1. router.POST( "/upload", func(c *gin.Context) {
  2. file, header , err := c.Request.FormFile( "upload")
  3. filename := header.Filename
  4. fmt.Println(header.Filename)
  5. out, err := os.Create( "./tmp/"+filename+ ".png")
  6. if err != nil {
  7. log.Fatal(err)
  8. }
  9. defer out.Close()
  10. _, err = io.Copy(out, file)
  11. if err != nil {
  12. log.Fatal(err)
  13. }
  14. })

响应

  • 响应头

  • 附加Cookie

  • 字符串响应

c.String(http.StatusOK, "some string")
  • JSON/XML/YAML响应


  
  1. r.GET( "/moreJSON", func(c *gin.Context) {
  2. // You also can use a struct
  3. var msg struct {
  4. Name string `json:"user" xml:"user"`
  5. Message string
  6. Number int
  7. }
  8. msg.Name = "Lena"
  9. msg.Message = "hey"
  10. msg.Number = 123
  11. // 注意 msg.Name 变成了 "user" 字段
  12. // 以下方式都会输出 : {"user": "Lena", "Message": "hey", "Number": 123}
  13. c.JSON(http.StatusOK, gin.H{ "user": "Lena", "Message": "hey", "Number": 123})
  14. c.XML(http.StatusOK, gin.H{ "user": "Lena", "Message": "hey", "Number": 123})
  15. c.YAML(http.StatusOK, gin.H{ "user": "Lena", "Message": "hey", "Number": 123})
  16. c.JSON(http.StatusOK, msg)
  17. c.XML(http.StatusOK, msg)
  18. c.YAML(http.StatusOK, msg)
  19. })
  • 视图响应

先要使用 LoadHTMLTemplates() 方法来加载模板文件


  
  1. func main() {
  2. router := gin.Default()
  3. //加载模板
  4. router.LoadHTMLGlob( "templates/*")
  5. //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
  6. //定义路由
  7. router.GET( "/index", func(c *gin.Context) {
  8. //根据完整文件名渲染模板,并传递参数
  9. c.HTML(http.StatusOK, "index.tmpl", gin.H{
  10. "title": "Main website",
  11. })
  12. })
  13. router.Run( ":8080")
  14. }

模板结构定义


  
  1. <html>
  2. <h1>
  3. {{ .title }}
  4. </h1>
  5. </html>

不同文件夹下模板名字可以相同,此时需要 LoadHTMLGlob() 加载两层模板路径


  
  1. router.LoadHTMLGlob( "templates/**/*")
  2. router.GET( "/posts/index", func(c *gin.Context) {
  3. c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
  4. "title": "Posts",
  5. })
  6. c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
  7. "title": "Users",
  8. })
  9. }

templates/posts/index.tmpl


  
  1. <!-- 注意开头 define 与结尾 end 不可少 -->
  2. {{ define "posts/index.tmpl" }}
  3. <html> <h1>
  4. {{ .title }}
  5. </h1>
  6. </html>
  7. {{ end }}

gin也可以使用自定义的模板引擎,如下


  
  1. import "html/template"
  2. func main() {
  3. router := gin.Default()
  4. html := template.Must(template.ParseFiles( "file1", "file2"))
  5. router.SetHTMLTemplate(html)
  6. router.Run( ":8080")
  7. }
  • 文件响应


  
  1. //获取当前文件的相对路径
  2. router.Static( "/assets", "./assets")
  3. //
  4. router.StaticFS( "/more_static", http.Dir( "my_file_system"))
  5. //获取相对路径下的文件
  6. router.StaticFile( "/favicon.ico", "./resources/favicon.ico")
  • 重定向


  
  1. r.GET( "/redirect", func(c *gin.Context) {
  2. //支持内部和外部的重定向
  3. c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
  4. })
  • 同步异步

goroutine 机制可以方便地实现异步处理


  
  1. func main() {
  2. r := gin.Default()
  3. //1. 异步
  4. r.GET( "/long_async", func(c *gin.Context) {
  5. // goroutine 中只能使用只读的上下文 c.Copy()
  6. cCp := c.Copy()
  7. go func() {
  8. time.Sleep( 5 * time.Second)
  9. // 注意使用只读上下文
  10. log.Println( "Done! in path " + cCp.Request.URL.Path)
  11. }()
  12. })
  13. //2. 同步
  14. r.GET( "/long_sync", func(c *gin.Context) {
  15. time.Sleep( 5 * time.Second)
  16. // 注意可以使用原始上下文
  17. log.Println( "Done! in path " + c.Request.URL.Path)
  18. })
  19. // Listen and serve on 0.0.0.0:8080
  20. r.Run( ":8080")
  21. }

视图

  • 传参

  • 视图组件

中间件

  • 分类使用方式 


  
  1. // 1.全局中间件
  2. router.Use(gin.Logger())
  3. router.Use(gin.Recovery())
  4. // 2.单路由的中间件,可以加任意多个
  5. router.GET( "/benchmark", MyMiddelware(), benchEndpoint)
  6. // 3.群组路由的中间件
  7. authorized := router.Group( "/", MyMiddelware())
  8. // 或者这样用:
  9. authorized := router.Group( "/")
  10. authorized.Use(MyMiddelware())
  11. {
  12. authorized.POST( "/login", loginEndpoint)
  13. }
  • 自定义中间件


  
  1. //定义
  2. func Logger() gin.HandlerFunc {
  3. return func(c *gin.Context) {
  4. t := time.Now()
  5. // 在gin上下文中定义变量
  6. c.Set( "example", "12345")
  7. // 请求前
  8. c.Next() //处理请求
  9. // 请求后
  10. latency := time.Since(t)
  11. log.Print(latency)
  12. // access the status we are sending
  13. status := c.Writer.Status()
  14. log.Println(status)
  15. }
  16. }
  17. //使用
  18. func main() {
  19. r := gin.New()
  20. r.Use(Logger())
  21. r.GET( "/test", func(c *gin.Context) {
  22. //获取gin上下文中的变量
  23. example := c.MustGet( "example").( string)
  24. // 会打印: "12345"
  25. log.Println(example)
  26. })
  27. // 监听运行于 0.0.0.0:8080
  28. r.Run( ":8080")
  29. }
  • 中间件参数

  • 内置中间件 (简单认证BasicAuth)


  
  1. // 模拟私有数据
  2. var secrets = gin.H{
  3. "foo": gin.H{ "email": "foo@bar.com", "phone": "123433"},
  4. "austin": gin.H{ "email": "austin@example.com", "phone": "666"},
  5. "lena": gin.H{ "email": "lena@guapa.com", "phone": "523443"},
  6. }
  7. func main() {
  8. r := gin.Default()
  9. // 使用 gin.BasicAuth 中间件,设置授权用户
  10. authorized := r.Group( "/admin", gin.BasicAuth(gin.Accounts{
  11. "foo": "bar",
  12. "austin": "1234",
  13. "lena": "hello2",
  14. "manu": "4321",
  15. }))
  16. // 定义路由
  17. authorized.GET( "/secrets", func(c *gin.Context) {
  18. // 获取提交的用户名(AuthUserKey)
  19. user := c.MustGet(gin.AuthUserKey).( string)
  20. if secret, ok := secrets[user]; ok {
  21. c.JSON(http.StatusOK, gin.H{ "user": user, "secret": secret})
  22. } else {
  23. c.JSON(http.StatusOK, gin.H{ "user": user, "secret": "NO SECRET :("})
  24. }
  25. })
  26. // Listen and serve on 0.0.0.0:8080
  27. r.Run( ":8080")
  28. }

数据库

  • Mongodb

Golang常用的Mongodb驱动为 mgo.v2,具体可查看文档

使用方式如下:


  
  1. //定义 Person 结构,字段须为首字母大写
  2. type Person struct {
  3. Name string
  4. Phone string
  5. }
  6. router.GET( "/mongo", func(context *gin.Context){
  7. //可本地可远程,不指定协议时默认为http协议访问,此时需要设置 mongodb 的nohttpinterface=false来打开httpinterface。
  8. //也可以指定mongodb协议,如 "mongodb://127.0.0.1:27017"
  9. var MOGODB_URI = "127.0.0.1:27017"
  10. //连接
  11. session, err := mgo.Dial(MOGODB_URI)
  12. //连接失败时终止
  13. if err != nil {
  14. panic(err)
  15. }
  16. //延迟关闭,释放资源
  17. defer session.Close()
  18. //设置模式
  19. session.SetMode(mgo.Monotonic, true)
  20. //选择数据库与集合
  21. c := session.DB( "adatabase").C( "acollection")
  22. //插入文档
  23. err = c.Insert(&Person{Name: "Ale", Phone: "+55 53 8116 9639"},
  24. &Person{Name: "Cla", Phone: "+55 53 8402 8510"})
  25. //出错判断
  26. if err != nil {
  27. log.Fatal(err)
  28. }
  29. //查询文档
  30. result := Person{}
  31. //注意mongodb存储后的字段大小写问题
  32. err = c.Find(bson.M{ "name": "Ale"}).One(&result)
  33. //出错判断
  34. if err != nil {
  35. log.Fatal(err)
  36. }
  37. fmt.Println( "Phone:", result.Phone)
  38. })

感谢阅读 ~ Golang文章会持续更新,一起学习,谢谢


转载:https://blog.csdn.net/qq_34284638/article/details/104970420
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场