zoukankan      html  css  js  c++  java
  • Go语言Gin-1.路由

    1.gin简介

    • 镜像配置:

      // 安装出现timeout问题可以配置go module镜像,终端执行下面命令,为阿里镜像
      go env -w GO111MODULE=on
      go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct
      
    • 安装gin:

      import "github.com/gin-gonic/gin"
      
    • 一切始于hello world

      package main
      
      import (
          "net/http"
      
          "github.com/gin-gonic/gin"
      )
      
      func main() {
          // 1.创建路由
         r := gin.Default()
         // 2.绑定路由规则,执行的函数
         // gin.Context,封装了request和response
         r.GET("/", func(c *gin.Context) {
            c.String(http.StatusOK, "hello World!")
         })
         // 3.监听端口,默认在8080
         // Run("里面不指定端口号默认为8080") 
         r.Run(":8000")
      }
      
    • 当运行时候会出现如下错误;

      can't load package: package gin_demo is not in GOROOT
      // 可以使用go mod模式,在当前项目目录下初始化mod
      go mod init
      
      
    • 此时访问127.0.0.1:8000就会返回hello world。

    2.gin路由

    2.1 基本路由

    • gin框架中采用的路由哭给予httpprouter做的。

      packge main
      import (
          "net/http"
          "github.com/gin-gonic/gin"
      )
      
      func main() {
          r := gin.Default()
          r.GET("/", func(c *gin.Context) {
              c.String(http.StatusOK, "hello word")
          })
          r.POST("/xxxpost",getting)
          r.PUT("/xxxput")
          //监听端口默认为8080
          r.Run(":8000")
      }
      

    2.2 Restful风格API

    • Gin 支持Restful风格API。表现层状态转化,如

      1.获取文章 /blog/getXxx Get blog/Xxx
      
      2.添加 /blog/addXxx POST blog/Xxx
      
      3.修改 /blog/updateXxx PUT blog/Xxx
      
      4.删除 /blog/delXxxx DELETE blog/Xxx
      

    2.3API参数

    • API参数,可以通过Context 的Param方法来获取API参数。

      func main() {
      	r := gin.Default()
      	r.GET("/user/:name/*action", func(context *gin.Context) {
      		// 获取URL上name
      		name := context.Param("name")
      		// 获取URL上action
      		action := context.Param("action")
      		fmt.Println(action)
      		// 去除两边 /
      		action = strings.Trim(action, "/")
      		context.String(http.StatusOK, name+" is "+action)
      	})
      	r.Run(":8000")
      }
      
      
    • 如下图:

      ![F3A7649C-316C-49E1-84C2-0A5909EC46E7](/Users/xujunkai/Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/Users/874524334/QQ/Temp.db/F3A7649C-316C-49E1-84C2-0A5909EC46E7.png)

    2.4URL参数

    • URL参数可以通过DefaultQuery()或Query()方法获取。

    • DefaultQuery()若参数不对称,则返回默认值。Query()若不存在,返回空串。

      r.GET("userinfo", func(c *gin.Context){
      		name := c.DefaultQuery("name","Tom")
      		c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
      	})
      
    • 当不加参数访问URL:http://127.0.0.1:8000/userinfo,返回是:hello Tom,当加参数访问URL:http://127.0.0.1:8000/userinfo/?name=Jery,返回是:hello Jery

    2.5表单参数

    • 表单传输为post请求,http常见的传输格式为:

      • application/json
      • application/x-www-form-urlencoded
      • application/xml
      • multipart/form-data
    • 表单可以通过如下方式访问,该方法默认解析x-www-form-urlencoded或from-data格式的参数。

      <form action="http://localhost:8000/form" method="post" action="application/x-www-form-urlencoded">
          用户名:<input type="text" name="username" placeholder="请输入你的用户名">  <br>
          密&nbsp;&nbsp;&nbsp;码:<input type="password" name="userpassword" placeholder="请输入你的密码">  <br>
          <input type="submit" value="提交">
      </form>
      
    • post请求处理:

      r.POST("/form", func(c *gin.Context) {
      		types := c.DefaultPostForm("type", "post")
      		username := c.PostForm("username")
      		password := c.PostForm("userpassword")
      		c.String(http.StatusOK,fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
      	})
      
    • 如下示例图:

      image-20200719145622507

      ![73478227-4D2D-4890-A7A8-B25BA628A84E](/Users/xujunkai/Library/Containers/com.tencent.qq/Data/Library/Application Support/QQ/Users/874524334/QQ/Temp.db/73478227-4D2D-4890-A7A8-B25BA628A84E.png)

    2.6上传单个文件

    • gin文件上传与原生的net/http方法类似,不同在于gin把原声request封装到c.Request中

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>Document</title>
      </head>
      <body>
          <form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
                上传文件:<input type="file" name="file" >
                <input type="submit" value="提交">
          </form>
      </body>
      </html>
      
      package main
      
      import (
          "github.com/gin-gonic/gin"
      )
      
      func main() {
          r := gin.Default()
          //限制上传最大尺寸
          r.MaxMultipartMemory = 8 << 20
          r.POST("/upload", func(c *gin.Context) {
              file, err := c.FormFile("file")
              if err != nil {
                  c.String(500, "上传图片出错")
              }
              // c.JSON(200, gin.H{"message": file.Header.Context})
              c.SaveUploadedFile(file, file.Filename)
              c.String(http.StatusOK, file.Filename)
          })
          r.Run()
      }
      
    • 上传特定文件

      // 获取headers
      _, headers, err := c.Request.FormFile("file")
      if err != nil {
        log.Printf("error")
      }
      if headers.Size > 1024*1024*2 {
        fmt.Println("文件太大了")
        return
      }
      if headers.Header.Get("Content-Type") != "image/png" {
        fmt.Println("只允许上传png图片")
        return
      }
      
      

    2.7上传多个文件

    func main() {
       // 1.创建路由
       // 默认使用了2个中间件Logger(), Recovery()
       r := gin.Default()
       // 限制表单上传大小 8MB,默认为32MB
       r.MaxMultipartMemory = 8 << 20
       r.POST("/upload", func(c *gin.Context) {
          form, err := c.MultipartForm()
          if err != nil {
             c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
          }
          // 获取所有图片
          files := form.File["files"]
          // 遍历所有图片
          for _, file := range files {
             // 逐个存
             if err := c.SaveUploadedFile(file, file.Filename); err != nil {
                c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
                return
             }
          }
          c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
       })
       //默认端口号是8080
       r.Run(":8000")
    }
    

    2.8路由的routes group

    • 为了管理一些相同的URL
    func main() {
       // 1.创建路由
       // 默认使用了2个中间件Logger(), Recovery()
       r := gin.Default()
       // 路由组1 ,处理GET请求
       v1 := r.Group("/v1")
       // {} 是书写规范
       {
          v1.GET("/login", login)
          v1.GET("submit", submit)
       }
       v2 := r.Group("/v2")
       {
          v2.POST("/login", login)
          v2.POST("/submit", submit)
       }
       r.Run(":8000")
    }
    

    2.9路由原理

    • httproter会将所有路由规则构造一颗前缀树。比如有root and as at cn com

    2.10路由拆分与注册

    2.10.1 基本路由注册

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    func helloHandler(c *gin.Context) {
    	c.JSON(http.StatusOK, gin.H{
    		"message": "Hello www.topgoer.com!",
    	})
    }
    func main()  {
    	r := gin.Default()
    	// 指定路由函数。
    	r.GET("/topgoer", helloHandler)
    	if err := r.Run(); err!=nil{
    		fmt.Println("startup service failed. err:%v
    ", err)
    	}
    }
    // 访问http://localhost:8080/topgoer 返回{"message":"Hello www.topgoer.com!"}
    

    2.10.2 路由拆分成单独文件或包

    • 当你的项目规模太大,就不适合在项目的main.go文件中去实现路由注册相关逻辑了。我们会倾向于把路由部分的代码拆分出来。形成单独一个文件或包

      // 项目目录结构
      gin_demo
      	|__go.sum
      	|__main.go
      	|__go.mod
      	|__routers
      			 |___routers.go
      	
      
    • Routers/routers.go

      package routers
      
      import (
      	"github.com/gin-gonic/gin"
      	"net/http"
      )
      
      func helloHandler(c *gin.Context){
      	c.JSON(http.StatusOK, gin.H{
      		"message": "Hello www.topgoer.com",
      	})
      }
      // SetupRouter 用于配置路由信息
      func SetupRouter() *gin.Engine {
      	r := gin.Default()
      	r.GET("/topgoer", helloHandler)
      	return r
      }
      
      
    • main.go

      package main
      
      import (
      	"fmt"
      	"gin_demo/routers"
      )
      
      func main() {
      	r :=routers.SetupRouter()
      	if err:= r.Run(); err != nil {
      		fmt.Printf("startUp service failed, err:%v
      ", err)
      	}
      }
      

    2.10.3 路由拆分成多个文件

    • 当我们业务规模继续膨胀时候,单独的一个routers文件包已经无法满足,我们需求如下的目录

      // 项目目录结构
      gin_demo
      	|__go.sum
      	|__main.go
      	|__go.mod
      	|__routers
      			 |___blog.go
      			 |___shop.go
      

    2.10.4路由拆分到不同APP

    • 有时候项目实在太大,那么我们倾向于把业务拆分更详细一些,例如把不同业务代码拆分成不同APP

      gin_demo
        |__app
      	|   |__blog
      	|		|   |__handler.go
        |   |   |__router.go
      	|   |__shop
      	|       |__handler.go
      	|				|__router.go
      	|__go.sum
      	|__main.go
      	|__go.mod
      	|__routers
      			 |___routers.go
      
    • app/blog/handler.go

    package blog
    
    import (
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    func blogHandler(c *gin.Context){
    	c.JSON(http.StatusOK, gin.H{
    		"message": "Hello blog handler",
    	})
    }
    
    
    • app/blog/router.go

      package blog
      
      import "github.com/gin-gonic/gin"
      
      func Routers(e *gin.Engine){
      	e.GET("/blog",blogHandler)
      }
      
    • app/shop/handler.go

      package shop
      
      import (
      	"github.com/gin-gonic/gin"
      	"net/http"
      )
      
      func helloHandler(c *gin.Context){
      	c.JSON(http.StatusOK, gin.H{
      		"message": "Hello handler",
      	})
      }
      
      func commentHandler(c *gin.Context) {
      	c.JSON(http.StatusOK, gin.H{
      		"message": "comment handler",
      	})
      }
      
      
    • app/shop/router.go

      package shop
      
      import "github.com/gin-gonic/gin"
      
      func Routers(e *gin.Engine) {
      	e.GET("/hello",helloHandler)
      	e.GET("/comment",commentHandler)
      }
      
    • routers/routers.go

      package routers
      
      import "github.com/gin-gonic/gin"
      
      type Option func(engine *gin.Engine)
      
      
      var options = []Option{}
      
      // 注册app路由配置
      func Include(opts ...Option) {
      	options = append(options, opts...)
      }
      //初始化操作
      func Init() *gin.Engine {
      	r := gin.New()
      	for _, opt := range options{
      		opt(r)
      	}
      	return r
      }
      
      
      
    • main.go

      package main
      
      import (
      	"fmt"
      	"gin_demo/app/blog"
      	"gin_demo/app/shop"
      	"gin_demo/routers"
      )
      
      func main() {
      	// 家在多个APP的路由配置
      	routers.Include(shop.Routers,blog.Routers)
      	// 初始化路由
      	r := routers.Init()
      	if err := r.Run(); err != nil {
      		fmt.Println("startup service failed, err:%v
      ", err)
      	}
      }
      
  • 相关阅读:
    C++字符串(srtring)反转
    字典(Dictionary)
    畅通工程
    子串计算
    神奇的口袋
    SLT 优先队列 哈弗曼树最小带权路径
    大数阶乘
    整数拆分
    A+B (带有,的数字)
    Hdu 1232 畅通工程
  • 原文地址:https://www.cnblogs.com/xujunkai/p/13341737.html
Copyright © 2011-2022 走看看