• 01 . Go框架之Gin框架从入门到熟悉(路由和上传文件)


    Gin框架简介

    Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确,已经发布了1.0版本。具有快速灵活,容错方便等特点。其实对于golang而言,web框架的依赖要远比Python,Java之类的要小。自身的net/http足够简单,性能也非常不错。框架更像是一些常用函数或者工具的集合。借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。

    Gin特性
    /*
    		快速:  路由不使用反射,基于Radix树,内存占用少
    		中间件: HTTP请求,先经过一系列中间件和最终操作来处理,例如: Logger, Authorization,GZIP等, 
    					 这个特性和NodeJs的Koa框架很像, 中间件机制也极大的提高了框架的可扩展性.
    		
    		异常处理:  服务始终可用, 不会宕机,Gin可以捕获panic,并恢复,而且极为便利的机制处理HTTP请求过程中发生的错误.
        JSON:  Gin可以解析并验证请求的JSON, 这个特性对于Restful API的开发尤其有用.
        路由分组:  例如需要授权和不需要授权的API分组,不同版本的API分组.
        					而且分组可嵌套,且性能不受影响.
        渲染内置:  原生支持JSON, XML和HTML的渲染.
    */
    

    govendor包管理

    govendor好处

    Go从1.5版本之后,就默认优先使用vendor子目录中的依赖库,而不是$GOPATH/src中的依赖包,这样我们可以把工程源码到处复制,使用时直接go build就可以了,不需要考虑一大堆第三方依赖包的管理问题。

    安装govendor
    go get -u github.com/kardianos/govendor
    /*
    		-v:打印出被构建的代码包的名字
    		-u:已存在相关的代码包,强行更新代码包及其依赖包
    */
    
    常用命令
    /*
    		命令 功能
        init 初始化 vendor 目录
        list 列出所有的依赖包
        add 添加包到 vendor 目录,如 govendor add +external 添加所有外部包
        add PKG_PATH 添加指定的依赖包到 vendor 目录
        update 从 $GOPATH 更新依赖包到 vendor 目录
        remove 从 vendor 管理中删除依赖
        status 列出所有缺失、过期和修改过的包
        fetch 添加或更新包到本地 vendor 目录
        sync 本地存在 vendor.json 时候拉去依赖包,匹配所记录的版本
        get 类似 go get 目录,拉取依赖包到 vendor 目录
    */
    
    初始化项目并添加 gin
    govendor init
    
    /*
    		此时会在当前工程目录(顶层)下面生成一个子目录vendor,里面有个文件vendor.json记录了一些模板信息。
    */
    
    govendor add + external
    
    /*
    		它会将你之前单独安装到$GOPATH/src目录下的所有依赖包都复制到本项目的vendor下面,
    		并且在vendor.json列出详细的清单。如果$GOPATH/src目录下面没有依赖包,
    		会解析当前项目中的go文件来将所有的依赖库先go get下来,存放到vendor下面。
    */
    
    
    govendor fetch github.com/gin-gonic/gin@v1.6.3
    // 添加或更新包到本地vendor目录
    
    
    govendor list -v fmt
    // 可以更详细地查看各种包的依赖关系,但是有些包未显示出来。
    
    bee热编译

    安装

    go get -u github.com/beego/bee
    

    使用

    # 进入你的项目目录,注意:使用bee 项目必须要在GOPATH目录下
    $ cd /your_project
    # 运行程序
    $ bee run
    
    gin热编译

    gin是用于实时重新加载Go Web应用程序的简单命令行实用程序。只需gin在您的应用程序目录中运行,您的网络应用程序将 gin作为代理提供。gin检测到更改后,将自动重新编译您的代码。您的应用在下次收到HTTP请求时将重新启动。

    gin 坚持“沉默就是黄金”的原则,因此,只有在出现编译器错误或在错误发生后成功进行编译时,它才会抱怨。

    安装

    go get github.com/codegangsta/gin
    

    使用

    gin run main.go
    
    cd 项目目录
    gin -p 3000 -a 9090 -b test.bin --all run
    // 表示监听虚拟机的3000端口,将请求转发给9000端口,生成的二进制执行文件 test.bin,所有文件的改动都会引起项目编译
    

    安装Gin

     go get -u github.com/gin-gonic/gin
    
    
    第一个Gin程序
    package main
    
    import "github.com/gin-gonic/gin"
    
    func main()  {
    	// 创建一个默认的路由引擎,携带基础中间件启动
    	r := gin.Default()
    
    	// GET:  请求方式: /hello:  请求的路径
    	// 当客户端以GET的方法请求/hello路径时,会执行后面的匿名函数
    	r.GET("/hello", func(c *gin.Context) {
    		
    		// c.JSON:  返回JSON格式的数据
    		c.JSON(200,gin.H{
    			"message": "Hello World",
    		})
    	})
    	
    	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
    	r.Run()
    }
    
    /*
    		1. 首先我们使用gin.Default()生成了一个实例,这个实例即WSGI应用程序.
    		2. 接下来, 我们使用r.Get("/",...)声明了一个路由,告诉Gin什么样的URL能触发传入的函数,
    				这个函数返回我们想要显示在用户浏览器中的信息.
    				
    		3. 最后用r.Run()函数让应用运行在本地服务器上,默认监听端口是_8080_, 可以传入参数,
    				例如: r.Run(":9999")即运行在9999端口.
    */
    

    路由

    /*
    		GET    参数挂在url中,uri中传参
    		POST   参数在form body中或者uri
    		DELETE  一般情况为uri,同样也可以用Body
    		PUT   参数在form body或者uri中
    */
    

    路由方法有GET, POST, PUT, PATCH, DELETEOPTIONS,还有Any,可匹配以上任意类型的请求

    无参数
    r.GET("/", func(c *gin.Context) {
    	c.String(http.StatusOK,"wunai")
    })
    
    /*
    	curl http://127.0.0.1:8080
    	wunai
    */
    
    解析路径参数

    有时候我们需要动态的路由,如/user/:name, 通过调用不同的url来传入不同的Name, /user/:name/*role, *代表可选

    	// 匹配/user/youmen
    	r.GET("/user/:name", func(c *gin.Context) {
    		name := c.Param("name")
    		c.String(http.StatusOK,"Hello %s",name)
    	})
    
    
    /*
    		curl http://127.0.0.1:8080/user/youmen
    		Hello youmen
    */
    
    获取Query参数
    // 匹配users?name=xxx&role=xxx, role可选
    r.GET("/users", func(c *gin.Context) {
      name := c.Query("name")
      role := c.DefaultQuery("role","teacher")
      c.String(http.StatusOK,"%s is a %s",name,role)
    })
    
    
    /*
      curl http://127.0.0.1:8080/users?name=youmen&role=student
      youmen is a student
    */
    
    http常见传输格式
    /*
        application/json
        application/x-www-form-urlencoded 
        application/xml
        multipart/form-data 
        
        表单参数可以通过PostForm()方法获取,该方法默认解析的是x-www-form-urlencoded或from-data格式的参数
    */
    
    获取POST参数

    参数一般在form body或者url中

    // POST
    r.POST("/form", func(c *gin.Context) {
      username := c.PostForm("username")
      password := c.DefaultPostForm("password","123") // 可设置默认值
    
      c.JSON(http.StatusOK,gin.H{
        "username":username,
        "password":password,
      })
    })
    
    /*
    		curl http://localhost:8080/form  -X POST -d 'username=youmen&password=1234' 
    
    		{"password":"1234","username":"youmen"}%  
    */
    

    Example2

    gin_demo1.go

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    func main() {
    	// 创建一个默认的路由引擎
    	r := gin.Default()
    
    	// GET:  请求方式: /hello:  请求的路径
    	// 当客户端以GET的方法请求/hello路径时,会执行后面的匿名函数
    	r.GET("/hello", func(c *gin.Context) {
    
    		// c.JSON:  返回JSON格式的数据
    		c.JSON(200, gin.H{
    			"message": "Hello World",
    		})
    	})
    
    	r.POST("/form", func(c *gin.Context) {
    		// 表单参数设置默认值
    		type1 := c.DefaultPostForm("type","alert")
    
    		// 接受其他的
    		username := c.PostForm("username")
    		password := c.PostForm("password")
    
    		// 多选框
    		hobbys := c.PostFormArray("hobby")
    		c.String(http.StatusOK,fmt.Sprintf("type is %s, username is %s, password is %s, habbys is %v",
    			type1,username,password,hobbys))
    	})
    	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
    	r.Run()
    }
    
    

    register.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        <form action="http://127.0.0.1:8080/form" method="post" enctype="application/x-www-form-urlencoded">
            用户名: <input type="text" name="username">
            密码: <input type="password" name="password">
    
            <input type="checkbox" value="run" name="hobby"> 跑步
            <input type="checkbox" value="Weightlifting" name="hobby"> 举重
            <input type="checkbox" value="money" name="hobby"> 金钱
    
            <input type="submit" value="注册">
        </form>
    
    </body>
    </html>
    
    Query和POST混合参数
    	// GET和POST混合
    	r.POST("/posts", func(c *gin.Context) {
    		id := c.Query("id")
    		page := c.DefaultQuery("page", "0")
    		username := c.PostForm("username")
    		password := c.DefaultPostForm("username", "0000")
    
    		c.JSON(http.StatusOK, gin.H{
    			"id":       id,
    			"page":     page,
    			"username": username,
    			"password": password,
    		})
    	})
    
    /*
    		curl "http://localhost:8080/posts?id=9876&page=7"  -X POST -d 'username=geektutu&password=1234'
    
    				{"id":"9876","page":"7","password":"geektutu","username":"geektutu"}%                            
    */
    
    Map参数(字典参数)
    // Map参数(字典参数)
    r.POST("/post", func(c *gin.Context) {
      ids := c.QueryMap("ids")
      names := c.PostFormMap("names")
    
      c.JSON(http.StatusOK,gin.H{
        "ids": ids,
        "names": names,
      })
    })                       
    
    /*
    		curl -g "http://localhost:8080/post?ids[Jack]=001&ids[Tom]=002" -X POST -d 'names[a]=Sam&names[b]=David'
    {"ids":{"Jack":"001","Tom":"002"},"names":{"a":"Sam","b":"David"}}   
    */
    
    重定向(Redirect)
    r.GET("/redirect", func(c *gin.Context) {
      c.Redirect(http.StatusMovedPermanently,"/index")
    })
    
    r.GET("/index", func(c *gin.Context) {
      c.Request.URL.Path = "/"
      r.HandleContext(c)
    })
    
    /*
    		curl http://127.0.0.1:8080/redirect -i
    		HTTP/1.1 301 Moved Permanently
    		Content-Type: text/html; charset=utf-8
    		Location: /index
    		Date: Tue, 27 Oct 2020 07:40:25 GMT
    		Content-Length: 41
    */
    
    分组路由

    对router创建Group就是分组,同一分组拥有同一前缀和同一中间件

    为什么要分组

    /*
    		路由结构更加清晰
    		更加方便管理路由
    */
    

    如果有一组路由,前缀都是/api/v1开头,是否每个路由都需要加上/api/v1这个前缀呢?答案是不需要,分组路由可以解决这个问题。利用分组路由还可以更好地实现权限控制,例如将需要登录鉴权的路由放到同一分组中去,简化权限控制。

    // group routes 分组路由
    defaultHandler := func(c *gin.Context) {
    	c.JSON(http.StatusOK, gin.H{
    		"path": c.FullPath(),
    	})
    }
    // group: v1
    v1 := r.Group("/v1")
    {
    	v1.GET("/posts", defaultHandler)
    	v1.GET("/series", defaultHandler)
    }
    // group: v2
    v2 := r.Group("/v2")
    {
    	v2.GET("/posts", defaultHandler)
    	v2.GET("/series", defaultHandler)
    }
    
    
    /*
    		curl http://localhost:8080/v1/posts
      	{"path":"/v1/posts"}
      	curl http://localhost:8080/v2/posts
      	{"path":"/v2/posts"}
    */
    

    上传文件

    multipart/form-data格式用于文件上传

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

    上传单个文件

    upload.go

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"log"
    )
    
    func main() {
    	// 创建一个默认的路由引擎
    	r := gin.Default()
    
    	// api参数
    	r.POST("/upload", func(c *gin.Context) {
    		// 表单取文件
    		file, _ := c.FormFile("file")
    		log.Println(file.Filename)
    
    		// 传到项目根目录,名字就用本身的
    		c.SaveUploadedFile(file,file.Filename)
    
    		// 打印信息
    		c.String(200,fmt.Sprintf("'%s' upload!",file.Filename))
    	})
    	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
    	r.Run()
    }
    
    

    Example1

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"io"
    	"os"
    )
    
    func main()  {
    	r := gin.Default()
    	r.POST("/upload", func(c *gin.Context) {
    		file,_ := c.FormFile("file")
    		in,_ := file.Open()
    		defer in.Close()
    
    		out,_ := os.Create("./"+file.Filename)
    		defer out.Close()
    
    		io.Copy(out,in)
    		c.Writer.Header().Add("Content-Disposttion",fmt.Sprintf("attachment; filename=%s",file.Filename))
    		c.File("./"+file.Filename)
    	})
    	r.Run(":8080")
    }
    

    upload.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        <form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
            头像
            <input type="file" name="file">
    
            <input type="submit" value="提交">
        </form>
    </body>
    </html>
    
    多个文件

    upload.go

    package main
    
    import (
    	"fmt"
    	"github.com/gin-gonic/gin"
    	"net/http"
    )
    
    func main() {
    	// 创建一个默认的路由引擎
    	r := gin.Default()
    	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)))
    	})
    
    	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
    	r.Run()
    }
    

    upload.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        <form action="http://127.0.0.1:8080/upload" method="post" enctype="multipart/form-data">
            头像
            <input type="file" name="files" multiple>
    
            <input type="submit" value="提交">
        </form>
    </body>
    </html>
    
  • 相关阅读:
    Cannot assign requested address问题总结
    Trying to connect an http1.x server
    从 0 到 1 搭建技术中台之推送平台实践:高吞吐、低延迟、多业务隔离的设计与实现
    思考gRPC :为什么是HTTP/2
    HTTP/1HTTP/2HTTP/3
    get_or_create update_or_create
    死锁案例 GAP 锁 没有就插入,存在就更新
    死锁产生必要条件
    京东零售mockRpc实践
    Certbot CA 证书 https
  • 原文地址:https://www.cnblogs.com/you-men/p/13887436.html
Copyright © 2020-2023  润新知