gin 订阅
琴酒,日本漫画《名侦探柯南》及其衍生作品中的角色。黑衣组织的重要成员,经常和伏特加一起出现。身穿黑色风衣,银色(早期动画为金色)长发,脸总被帽子和刘海半遮掩着,性格冷酷残忍,头脑冷静,似乎可以毫不犹豫地杀死任何人。经常执行各种暗杀和清除组织叛徒的任务,是给工藤新一灌下APTX4869使其身体变小的罪魁祸首,亦是柯南所面对的主要敌人之一。 展开全文
琴酒,日本漫画《名侦探柯南》及其衍生作品中的角色。黑衣组织的重要成员,经常和伏特加一起出现。身穿黑色风衣,银色(早期动画为金色)长发,脸总被帽子和刘海半遮掩着,性格冷酷残忍,头脑冷静,似乎可以毫不犹豫地杀死任何人。经常执行各种暗杀和清除组织叛徒的任务,是给工藤新一灌下APTX4869使其身体变小的罪魁祸首,亦是柯南所面对的主要敌人之一。
信息
座    驾
黑色保时捷356A(德国生产的古董车)
真实姓名
黑泽阵
饰    演
佐佐木藏之介
搭    档
伏特加
配    音
日语原版:堀之纪  、、大陆版:王明军、文涛、冯盛
身    份
黑衣组织成员
别    名
杜松子酒、金恩、阿进等
中文名
琴酒
性    别
外文名
Gin ジン(Jin)
年    龄
不详
惯用手
左手
登场作品
《名侦探柯南》及其衍生系列
琴酒人物信息
组织代号:Gin日文假名:ジン罗马音:Jin中文译名:琴酒其他译名:金恩、吉恩、阿进、杜松子酒等真实姓名:黑泽 阵 [1]  [2]  (日文姓名:黑泽 阵;日文假名:くろさわ じん;罗马音:Kurosawa Jin) 在小学馆出版的《LOVE·Conan-名侦探柯南Official fanbook》中,作者青山刚昌透露内部设定琴酒的名字为“黑泽阵”,原因是“阵”的日文片假名写法“ジン”与日文中的“琴酒”相同。 [1]  [2]  File.281中的名册上,宫野志保的名字旁边的名字为“鱼冢三郎”,疑为伏特加的名字,而“鱼冢三郎”边的名字被部分遮挡,仅露出“泽”一字;而在对应动画TV 223《人鱼失踪记(推理篇)》中,也只露出了“黑”字的一部分。不过据青山刚昌透露,“鱼冢三郎”的旁边写着的名字的确为“黑泽阵”。 [1]  另外在英文版漫画里,Gin和Vodka的真名被透露,分别是Melkior和Kaspar,不过后来这两个名字就没有下文了。性别:男年龄:不详出生地:不详血统:疑似混血儿身份:黑衣组织重要成员座驾:黑色保时捷356A(早期TV版中的车牌号为“新宿34-4869”,TV 345后的剧场版与TV版统一使用“新宿54 み43-68”)手机型号:iPhone 4S黑款(File.820)武器:伯莱塔M92F手枪爱好:开车时抽烟,并喜欢用汽车点烟器来点烟。 [3]  烟的品牌为“JILOISES”(虚构品牌)。 [4]  初登场:配音演员:真人版演员:佐佐木藏之介(真人版单发第二部)
收起全文
精华内容
下载资源
问答
  • Gin

    2021-04-02 14:39:38
    Gin闪亮登场 简介 Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。 Gin特点主要有: 速度快、性能优;支持中间件操作,方便编码处理;非常简单的实现路由等。 安装gin框架库 go get -u github....

    Gin闪亮登场

    简介

    Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。
    Gin特点主要有: 速度快、性能优;支持中间件操作,方便编码处理;非常简单的实现路由等。

    安装gin框架库

    go get -u github.com/gin-gonic/gin
    

    基架搭建

    package main
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	// 创建一个 默认的 路由引擎
    	r := gin.Default() 
    
      // 路由  和  控制器
    	r.GET("/hello", func(c *gin.Context) {
    		c.JSON(200, gin.H{
    			"hello": "world",
    		})
    	})
    
    	// 服务监听
    	err := r.Run("localhost:8080") // 默认 监听并在 0.0.0.0:8080 上启动服务
      
    	if err != nil {
    		panic(err)
    	}
    }
    

    路由引擎

    在gin框架中, 路由引擎 是一个 一个结构体,其中包含了路由组、中间件、页面渲染接口、等相关内容。

    engine1 = gin.Default() // 创建默认的 路由引擎
    engine2 = gin.New() // 创建一个新的 路由引擎
    
    • 说明:
      gin.Default其实也使用gin.New()创建engine实例,但是会默认使用LoggerRecovery中间件。
    • Logger是负责进行打印并输出日志的中间件,方便开发者进行程序调试;
    • Recovery中间件的作用是如果程序执行过程中遇到panic中断了服务,则Recovery会恢复程序执行,并返回服务器500内部错误。
    • 通常情况下,我们使用默认的gin.Default创建Engine实例。

    1. 项目配置

    这是一种 通用的 项目配置文件 的手段。

    1. 配置文件: config/app.json

    {
      "app_name": "cloudrestaurant",
      "app_mode": "debug",
      "app_host": "localhost",
      "app_port": "8090",
      "database": {
        "dbsize": "mysql",
        "username": "root",
        "password": "123456",
        "host": "localhost",
        "port": "3679",
        "dbname": "gin",
        "charset": "utf8mb4"
      },
      "redis_config": {
        "addr": "127.0.0.1",
        "port": "6379",
        "password": "",
        "db": 0
      }
    }
    

    2. 解析文件: app/app.go

    package app
    
    import (
    	"bufio"
    	"encoding/json"
    	"os"
    )
    
    type Config struct {
    	AppName     string      `json:"app_name"`
    	AppMode     string      `json:"app_mode"`
    	AppHost     string      `json:"app_host"`
    	AppPort     string      `json:"app_port"`
    	Database    Database    `json:"database"`
    	RedisConfig RedisConfig `json:"redis_config"`
    }
    
    type Database struct {
    	DBsize   string `json:"dbsize"`
    	Username string `json:"username"`
    	Password string `json:"password"`
    	Host     string `json:"host"`
    	Port     string `json:"port"`
    	DBname   string `json:"dbname"`
    	Charset  string `json:"charset"`
    }
    
    type RedisConfig struct {
    	Addr     string `json:"addr"`
    	Port     string `json:"port"`
    	Password string `json:"password"`
    	Db       int    `json:"db"`
    }
    
    var AppConfig *Config = nil
    
    func ParseConfig(path string) {
    	file, err := os.Open(path)
    	if err != nil {
    		panic(err)
    	}
    	defer file.Close()
    
    	reader := bufio.NewReader(file)
    	decoder := json.NewDecoder(reader)
    	err = decoder.Decode(&AppConfig)
    
    	if err != nil {
    		panic(err)
    	}
    }
    
    func init() {
    	ParseConfig("./config/app.json")
    }
    
    • 强调:

    在Gin中,项目开发完最终所有的包 都会打包成一个可执行文件,这个文件和main包同目录,所以 项目中凡是设计 相对路径的 都要站在main包的角度去写相对路径。

    3. 激活使用

    package main
    
    import (
    	// 在main文件引入,即 激活配置
    	"项目名称/app"
    	"fmt"
    )
    
    func main() {
    	// 使用配置中的数据
    	fmt.Println(app.AppConfig.AppName)
    	fmt.Println(app.AppConfig.Database.DBname)
    }
    

    2. 控制器

    外观描述

    是一个函数, 只有一个参数, 参数类型为:*gin.Context, 没有返回值,就是这么简单。
    *gin.Context 参数是一个结构体, 是对 请求、响应报文 的封装,是Gin框架的主要内容,是 上下文 !!!

    上下文(gin.Context) 上 的 api

    • Next()

    将处理权 移交到 下一棒 中间件(或控制器) 手中

    • Abort()

    终断该请求的处理

    • Set(“name”, “tom”)、Get(“name”)

    在上下文中 写入数据,可以在 下一棒 中间件(或控制器) 手中 通过 Get()获取到对应数据

    • 重定向

    Redirect(302, “https://www.baidu.com”)

    • 请求报文: Request
    • c.Request.Proto => 请求协议
    • c.Request.Host => 请求协议
    • c.Request.URL => 请求路径
    • c.Request.Method => 请求方法
    • c.Request.Header[“Content-Type”] => 请求头相关

    控制器 的实现

    package controller
    
    import (
    	"github.com/gin-gonic/gin"
    )
    
    // 将 控制器绑在 结构体上 是 模块化 思想的体现
    // 很好的 避免了 不同路由 的 控制器 重名的问题。
    type Testing struct {
    }
    
    func (that *Testing) HandGetTest(context *gin.Context) {
    	...
    }
    
    func (that *Testing) HandPostTest(context *gin.Context) {
    	...
    }
    

    3. 路由

    单路由

    使用 路由引擎 上封装 路由相关的api 可以很方便地实现 路由功能

    package router
    
    import (
    	"testGin/controller"
    
    	"github.com/gin-gonic/gin"
    )
    
    // 路由表
    func ActiveRouterList(e *gin.Engine) {
    	e.Handle("GET", "/test", new(controller.Testing).HandGetTest)
    
      // "语法糖"
    	e.GET("/test", new(controller.Testing).HandGetTest)
    	e.POST("/test", new(controller.Testing).HandPostTest)
    
    }
    
    • 在 main函数 中 激活🔥路由表
    package main
    
    import (
    	"testGin/router"
    
    	"github.com/gin-gonic/gin"
    )
    
    func main() {
    	app := gin.Default()
    
    	// 激活🔥路由表 
    	router.ActiveRouterList(app)
    
    	app.Run()
    }
    

    路由组

    使用 路由引擎 的 Group 方法

    // 激活 路由表
    func ActiveRouterList(e *gin.Engine) {
    
    	group := e.Group("/aaa")
    	{ // 加这个 块级括号 ,是为了 彰显 路由组 层次感
    		group.GET("/b1", new(controller.Testing).HandGetTest)
    		group.GET("/b2", new(controller.Testing).HandGetTest)
    
    		group2 := group.Group("b3")
    		{
    			group2.GET("/c1", new(controller.Testing).HandGetTest)
    			group2.GET("/c2", new(controller.Testing).HandGetTest)
    		}
    	}
    
    	// 路由组访问地址: /aaa/b1   和  /aaa/b3/c1 
    }
    

    其他路由

    404路由

    e.NoRoute(控制器)
    

    广角路由

    // 可以匹配 任何请求方法
    e.Any("test", 控制器)
    

    静态路由

    上面的路由都是动态路由,下面介绍一下 静态路由。
    静态路由,为静态文件 指定 的访问路径。

    工程目录下 的 static文件夹下的images文件夹下存放这aaa.png图片文件,
    写一个静态路由来访问这个文件。

    // 路由表
    func ActiveRouterList(e *gin.Engine) {
    
    	e.Static("/img", "./static/images/")
    	// 图片的访问路径: /img/aaa.png
    }
    

    4. 中间件

    gin框架允许开发者在 处理请求 的过程中,加入用户自己的 钩子函数。这个 钩子函数 就是 中间件。
    中间件适合处理一些 公共的 业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
    Gin 的中间件 是 洋葱模型的 运行流程。

    定义一个中间件

    // 设计一个 统计 请求耗时 的中间件(默认路由引擎 中已经实现,这里重点定义中间件)
    package midlleware
    
    import (
    	"fmt"
    	"time"
    
    	"github.com/gin-gonic/gin"
    )
    // 中间件 的 本质上 就是 控制器 生产函数
    // 所以 返回值 是一个 控制器
    func TimeCost() (gin.HandlerFunc) {
    	return func(c *gin.Context) {
    		start := time.Now()
    		c.Next() // 将控制权 主动交给 下一棒
        cost := time.Since(start)
    		fmt.Println(cost)
    	}
    }
    

    注册 使用 中间件

    单路由 注册

    // 在路由表中,找到 对应的路由 加入即可
    e.GET("/test", midlleware.TimeCost(), new(controller.Testing).HandGetTest)
    // 注意,要以调用的方式 放入 中间件
    

    全局 注册

    // 在路由表中
    e.Use(midlleware.TimeCost())
    

    路由组 注册

    // 方式一, 单路由注册 的方式
    group := e.Group("aaa", midlleware.TimeCost())
    {
    	...
    }
    
    // 方式二, 全局 注册 的方式
    group := e.Group("aaa")
    group.Use(midlleware.TimeCost())
    {
    	...
    }
    

    5. 前后交互

    接收数据

    api直取

    获取get 请求的 Param参数(url参数)

    e.GET("/test/:name", func(c *gin.Context){
    	name := c.Param("name")
    })
    

    获取get 的 请求参数

    e.GET("/test", func(c *gin.Context){
    	name := c.Query("name")
    })
    

    说明:
    如果 请求参数 name是必须的,但是客户访问的时候又不一定传时,我们可以为 name设置一个
    默认值:
    name := c. DefaulQuery("name", " 默认值 ")

    获取 POST 以 表单的形式 提交数据

    engine.Handle("POST", "/login", func(c *gin.Context) {
    	username := c.PostForm("username")
    
    	password := c.PostForm("pwd")
    })
    
    • 说明:

    post请求参数 也可以设置默认值:
    name := c. DefaultPostForm ("name ", "默认值")

    对应绑定

    api直取 的 方式 获取数据 有两方面的 缺点:

    • 无法获取 POST 以 json形式 提交的数据
    • 一个一个获取单个属性键值对,如果要获取的键值对多了,再一个个获取,就有无语了。

    获取 GET 的 请求参数 (ShouldBindQuery)

    /aaa?name=tom&age=10
    通过 ShouldBindQuery()方法实现

    package router
    
    import (
    	"fmt"
    
    	"github.com/gin-gonic/gin"
    )
    
    //form之后的名 和 提交 数据 的键名 对应不起来,
    //则无法做到对应赋值
    type man struct {
    	Name string `form:"name"` 
    	Age  int    `form:"age"`
    }
    
    // 路由表
    func ActiveRouterList(e *gin.Engine) {
    	e.GET("/aaa", func(c *gin.Context) {
    		var peop man
    
    		err := c.ShouldBindQuery(&peop)
    
    		if err != nil {
    			fmt.Println(err)
    		}
    		fmt.Printf("%T >>---> %+v\n", peop, peop)
    		//router.man >>---> {Name:tom Age:10}
    		c.JSON(200, gin.H{"hi": "hi"})
    	})
    }
    

    获取 POST 以 json形式 提交数据 (ShouldBindJSON)

    通过 ShouldBindJSON()方法实现
    只需将上面的 ShouldBindQuery 改为 BindJSON 即可,其他都不用变。

    获取 POST 以 表单的形式 提交数据 (ShouldBind)

    通过 ShouldBind()方法实现
    只需将上面的 ShouldBindQuery 改为 ShouldBind 即可,其他都不用变。

    获取上传文件

    单文件

    e.POST("/aaa", func(c *gin.Context) {
    	file, err := c.FormFile("avatar")
    
    	if err != nil {
    		fmt.Println(err)
    	}
    
    	// SaveUploadedFile 不会创建文件夹,
    	// 所以 确保 filePath 上的文件夹都是已经存在的。
    	// 这里最好 用一下uuid
    	var filePath = "./uploadfile/" + file.Filename
    	err = ctx.SaveUploadedFile(file, filePath)
    }
    

    多文件

    e.POST("/aaa", func(c *gin.Context) {
    
    	form, _ := c.MultipartForm()
    	files := form.File["file"]
    
    	for index, file := range files {
    
    	  var filePath = "./uploadfile/" + file.Filename
    		// 上传文件到指定的目录
    		c.SaveUploadedFile(file, filePath)
    	}
    }
    

    响应数据

    返回普通 字符串

    涉及到的 api是 gin.Context 上的 Writer 实例

    []byte

    engine.GET("/hello", func (c *gin.Context) {
    	var s []byte = []byte{'a', 'b', 88}
    	c.Writer.Write(s)
    	// 前端收到的是: abX
    })
    

    string

    var s string = "abc"
    c.Writer.WriteString(s)
    // 前端收到的是: abc
    

    返回 json 字符串

    涉及到的 api是 gin.Context 上的 JOSN 方法

    map

    e.POST("/aaa", func(c *gin.Context) {
    	m := map[string]interface{}{
    		"name": "tom",
    		"age":  11,
    	}
    
    	c.JSON(200, m)
    	// 返回给前端的数据: `{ "age": 11,"name": "tom"}`
    })
    

    struct

    e.POST("/aaa", func(c *gin.Context) {
    	obj := struct {
    		Name string `json:"name"`
    		Age  int    `json:"old"`
    	}{"tom", 8}
    
    	c.JSON(200, obj)
    	// 返回给前端的数据: `{"name": "tom", "old": 8}`
    })
    

    6. 数据模型

    请转到我的GORM那一篇。

    7. 会话管理

    获取cookie

    e.GET("/get", func(c *gin.Context) {
    
    	cookie, _ := c.Cookie("cookie_key")
    	fmt.Println(cookie)
    
    })
    

    建议: token + cookie

    8. 日志记录

    通过使用logrus日志框架,以中间件的方式 实现日志记录

    package midlleware
    
    import (
    	"fmt"
    	"os"
    	"path"
    	"time"
    
    	"github.com/gin-gonic/gin"
    	"github.com/sirupsen/logrus"
    )
    
    // 判断目录是否存在
    func isExist(filePath string) (bool, error, string) {
    	_, err := os.Stat(filePath)
    	if err == nil {
    		return true, nil, "“文件存在”"
    	} else {
    		if os.IsNotExist(err) {
    			return false, nil, "“文件不存在”"
    		} else {
    			return false, err, "“包错原因不是因为文件不存在导致的”"
    		}
    	}
    }
    
    //日志记录到文件
    func LoggerToFile() gin.HandlerFunc {
    
    	logFilePath := "./logFile"
    	logFileName := "logName"
    
    	//文件路径
    	fileName := path.Join(logFilePath, logFileName)
    
    	res, _, _ := isExist(fileName)
    
    	if !res {
    		err := os.Mkdir("./logFile", os.ModePerm)
    		if err != nil {
    			fmt.Println(err)
    		}
    	}
    
    	// 写入文件
    	file, err := os.OpenFile(fileName, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0700)
    	if err != nil {
    		fmt.Println("err", err)
    	}
    
    	logger := logrus.New()
    
    	// 设置输出
    	logger.Out = file
    
    	// 设置日志级别
    	logger.SetLevel(logrus.DebugLevel)
    
    	return func(c *gin.Context) {
    		startTime := time.Now()
    
    		c.Next()
    
    		endTime := time.Now()
    
    		// 下面是 要写入日志的 字段
    		// 执行时间
    		allTime := endTime.Sub(startTime)
    
    		// 请求方式
    		reqMethod := c.Request.Method
    
    		// 请求路由
    		reqUrl := c.Request.RequestURI
    
    		// 状态码
    		statusCode := c.Writer.Status()
    
    		// 请求ip
    		clientIP := c.ClientIP()
    
    		// 换一下日期格式
    		logger.SetFormatter(&logrus.TextFormatter{
    			TimestampFormat: "2006-01-02 15:04:05",
    		})
    
    		// 换成json格式
    		logger.SetFormatter(&logrus.JSONFormatter{
    			TimestampFormat: "2006-01-02 15:04:05",
    		})
    
    		// 日志格式一: 日志变成json
    		entry := logger.WithFields(logrus.Fields{
    			"status_code": statusCode,
    			"all_time":    allTime,
    			"client_ip":   clientIP,
    			"req_method":  reqMethod,
    			"req_url":     reqUrl,
    		})
    		entry.Info()
    
    		// //日志格式二: 日志是字符串
    		// logger.Infof("| %3d | %13v | %15s | %s | %s |",
    		// 	statusCode,
    		// 	allTime,
    		// 	clientIP,
    		// 	reqMethod,
    		// 	reqUrl,
    		// )
    	}
    }
    
    • 注意:

    日志的文件存储路径 通过配置文件来决定,这里只是简单的演示。

    9. 错误处理

    404 路由 + 重定向 or 制定404控制器

    10. 安全防护

    展开全文
  • gin

    2020-05-06 18:56:22
    安装gin框架时出现链接超时的问题 描述 安装gin 框架时:go get github.com/gin-gonic/gin出现unrecognized import path "google.golang.org/protobuf/encoding/prototext": ...

    安装gin框架时出现链接超时的问题

    描述

      安装gin 框架时:go get github.com/gin-gonic/gin出现unrecognized import path "google.golang.org/protobuf/encoding/prototext": https fetch: Get "https://google.golang.org/protobuf/encoding/prototext?go-get=1": dial tcp 216.239.37.1:443: i/o timeout等问题
    

    解决方案:

    由于无法翻墙,我们可以设置通过设置代理来实现https://goproxy.cn/
    如下,在命令行输入:
        $ go env -w GO111MODULE=on
        $ go env -w GOPROXY=https://goproxy.cn,direct
    
        然后执行go get就可以快速下载了
    
    展开全文
  • Gin Web Framework Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. If you need performance ...
  • gin.v1 instead of gin

    2020-12-04 11:44:51
    <div><p>In this new update you change the gin framework version "github.com/gin-gonic/gin" instead of "gopkg.in/gin-gonic/gin.v1" <p>So I'm getting this error: cannot use cors....
  • window 上安装gin,需要先安装git ,go 环境,然后打开git,在你想要的地方安装go就可以 比如在 /e/golang/gin 安装gin 在git命令行中输入mkdir -p $GOPATH/gin1 然后没有报错的话就说明创建成功了,然后进入该文件下...
  • Gin 创建项目脚本 gin

    2020-10-07 20:39:19
    golang web框架——gin 脚本名称 : gin #!/bin/bash mkdir $GOPATH/src/$1 && cd $_ govendor init govendor fetch github.com/gin-gonic/gin vendor目录下就是本项目所需要的依赖。

    golang web框架——gin

    脚本名称 : gin

    #!/bin/bash
    
    mkdir $GOPATH/src/$1 && cd $_
    govendor init
    govendor fetch github.com/gin-gonic/gin
    

    vendor目录下就是本项目所需要的依赖。

    展开全文
  • Gin support

    2020-11-30 09:08:52
    <p>Implements a <code>wrapper/gin</code> subpackage as drop-in replacement for <a href="https://gin-gonic.github.io/gin">Gin Gonic</a> users. <p>Example and simple tests are included.</p><p>该提问来源...
  • <div><p>Bumps <a href="https://github.com/gin-gonic/gin">github.com/gin-gonic/gin</a> from 1.4.0 to 1.5.0. Release notes *Sourced from [github.com/gin-gonic/gin's releases]...
  • Gin Web框架简介

    万次阅读 2016-04-14 18:01:07
    翻译自: https://github.com/gin-gonic/gin/blob/develop/README.mdGin Web框架 Gin是用Golang实现的一种Web框架。基于 httprouter,它提供了类似martini但更好性能(路由性能约快40倍)的API服务. 如果你希望...

    翻译自: https://github.com/gin-gonic/gin/blob/develop/README.md

    Gin Web框架

    Gin是用Golang实现的一种Web框架。基于 httprouter,它提供了类似martini但更好性能(路由性能约快40倍)的API服务. 如果你希望构建一个高性能的生产环境,你会喜欢上使用 Gin。

    $ cat test.go
    
    package main
    
    import "github.com/gin-gonic/gin"
    
    func main() {
    	r := gin.Default()
    	r.GET("/ping", func(c *gin.Context) {
    		c.JSON(200, gin.H{
    			"message": "pong",
    		})
    	})
    	r.Run() // listen and server on 0.0.0.0:8080
    }
    

    基准测试

    Gin基于HttpRouter的这个定制版本来构建。

    查看全部测试

    Benchmark name (1) (2) (3) (4)
    BenchmarkAce_GithubAll 10000 109482 13792 167
    BenchmarkBear_GithubAll 10000 287490 79952 943
    BenchmarkBeego_GithubAll 3000 562184 146272 2092
    BenchmarkBone_GithubAll 500 2578716 648016 8119
    BenchmarkDenco_GithubAll 20000 94955 20224 167
    BenchmarkEcho_GithubAll 30000 58705 0 0
    BenchmarkGin_GithubAll 30000 50991 0 0
    BenchmarkGocraftWeb_GithubAll 5000 449648 133280 1889
    BenchmarkGoji_GithubAll 2000 689748 56113 334
    BenchmarkGoJsonRest_GithubAll 5000 537769 135995 2940
    BenchmarkGoRestful_GithubAll 100 18410628 797236 7725
    BenchmarkGorillaMux_GithubAll 200 8036360 153137 1791
    BenchmarkHttpRouter_GithubAll 20000 63506 13792 167
    BenchmarkHttpTreeMux_GithubAll 10000 165927 56112 334
    BenchmarkKocha_GithubAll 10000 171362 23304 843
    BenchmarkMacaron_GithubAll 2000 817008 224960 2315
    BenchmarkMartini_GithubAll 100 12609209 237952 2686
    BenchmarkPat_GithubAll 300 4830398 1504101 32222
    BenchmarkPossum_GithubAll 10000 301716 97440 812
    BenchmarkR2router_GithubAll 10000 270691 77328 1182
    BenchmarkRevel_GithubAll 1000 1491919 345553 5918
    BenchmarkRivet_GithubAll 10000 283860 84272 1079
    BenchmarkTango_GithubAll 5000 473821 87078 2470
    BenchmarkTigerTonic_GithubAll 2000 1120131 241088 6052
    BenchmarkTraffic_GithubAll 200 8708979 2664762 22390
    BenchmarkVulcan_GithubAll 5000 353392 19894 609
    BenchmarkZeus_GithubAll 2000 944234 300688 2648

    (1): 总重复次数

    (2): 单次请求耗时 (ns/op)

    (3): 堆内存大小 (B/op)

    (4): 单次请求内存分配数 (allocs/op)

    ##Gin v1. stable

    • 零分配路由.
    • 从路由到写请求, 依然为最快的路由器和框架.
    • 完备的单元测试套件.
    • Battle tested.(?)
    • API冻结, 新的release版不会影响现有的代码.

    快速开始

    1. 下载并安装Gin:

      $ go get github.com/gin-gonic/gin
      
    2. 在代码中import进来:

      import "github.com/gin-gonic/gin"
      
    3. (可选) Import net/http. 如果用到诸如 http.StatusOK的常量, 需要引入该包:

      import "net/http"
      

    ##API示例

    使用 GET, POST, PUT, PATCH, DELETE 以及 OPTIONS

    func main() {
    	// Creates a gin router with default middleware:
    	// logger and recovery (crash-free) middleware
    	router := gin.Default()
    
    	router.GET("/someGet", getting)
    	router.POST("/somePost", posting)
    	router.PUT("/somePut", putting)
    	router.DELETE("/someDelete", deleting)
    	router.PATCH("/somePatch", patching)
    	router.HEAD("/someHead", head)
    	router.OPTIONS("/someOptions", options)
    
    	// By default it serves on :8080 unless a
    	// PORT environment variable was defined.
    	router.Run()
    	// router.Run(":3000") for a hard coded port
    }
    

    路径参数

    func main() {
    	router := gin.Default()
    
    	// This handler will match /user/john but will not match neither /user/ or /user
    	router.GET("/user/:name", func(c *gin.Context) {
    		name := c.Param("name")
    		c.String(http.StatusOK, "Hello %s", name)
    	})
    
    	// However, this one will match /user/john/ and also /user/john/send
    	// If no other routers match /user/john, it will redirect to /user/john/
    	router.GET("/user/:name/*action", func(c *gin.Context) {
    		name := c.Param("name")
    		action := c.Param("action")
    		message := name + " is " + action
    		c.String(http.StatusOK, message)
    	})
    
    	router.Run(":8080")
    }
    

    查询字符串参数

    func main() {
    	router := gin.Default()
    
    	// Query string parameters are parsed using the existing underlying request object.
    	// The request responds to a url matching:  /welcome?firstname=Jane&lastname=Doe
    	router.GET("/welcome", func(c *gin.Context) {
    		firstname := c.DefaultQuery("firstname", "Guest")
    		lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname")
    
    		c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
    	})
    	router.Run(":8080")
    }
    

    Multipart/Urlencoded表单提交

    func main() {
    	router := gin.Default()
    
    	router.POST("/form_post", func(c *gin.Context) {
    		message := c.PostForm("message")
    		nick := c.DefaultPostForm("nick", "anonymous")
    
    		c.JSON(200, gin.H{
    			"status":  "posted",
    			"message": message,
    			"nick":    nick,
    		})
    	})
    	router.Run(":8080")
    }
    

    更多示例: 查询参数 + POST表单提交

    POST /post?id=1234&page=1 HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    name=manu&message=this_is_great
    
    func main() {
    	router := gin.Default()
    
    	router.POST("/post", func(c *gin.Context) {
    
    		id := c.Query("id")
    		page := c.DefaultQuery("page", "0")
    		name := c.PostForm("name")
    		message := c.PostForm("message")
    
    		fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message)
    	})
    	router.Run(":8080")
    }
    
    id: 1234; page: 1; name: manu; message: this_is_great
    

    更多示例: 上传文件

    参考问题 #548.

    func main() {
    	router := gin.Default()
    
    	router.POST("/upload", func(c *gin.Context) {
    
    	        file, header , err := c.Request.FormFile("upload")
    	        filename := header.Filename
    	        fmt.Println(header.Filename)
    	        out, err := os.Create("./tmp/"+filename+".png")
    	        if err != nil {
    	            log.Fatal(err)
    	        }
    	        defer out.Close()
    	        _, err = io.Copy(out, file)
    	        if err != nil {
    	            log.Fatal(err)
    	        }
    	})
    	router.Run(":8080")
    }
    

    分组路由

    func main() {
    	router := gin.Default()
    
    	// Simple group: v1
    	v1 := router.Group("/v1")
    	{
    		v1.POST("/login", loginEndpoint)
    		v1.POST("/submit", submitEndpoint)
    		v1.POST("/read", readEndpoint)
    	}
    
    	// Simple group: v2
    	v2 := router.Group("/v2")
    	{
    		v2.POST("/login", loginEndpoint)
    		v2.POST("/submit", submitEndpoint)
    		v2.POST("/read", readEndpoint)
    	}
    
    	router.Run(":8080")
    }
    

    不使用中间件, 使用Gin默认配置

    使用

    r := gin.New()
    

    来代替

    r := gin.Default()
    

    使用中间件

    func main() {
    	// Creates a router without any middleware by default
    	r := gin.New()
    
    	// Global middleware
    	r.Use(gin.Logger())
    	r.Use(gin.Recovery())
    
    	// Per route middleware, you can add as many as you desire.
    	r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
    
    	// Authorization group
    	// authorized := r.Group("/", AuthRequired())
    	// exactly the same as:
    	authorized := r.Group("/")
    	// per group middleware! in this case we use the custom created
    	// AuthRequired() middleware just in the "authorized" group.
    	authorized.Use(AuthRequired())
    	{
    		authorized.POST("/login", loginEndpoint)
    		authorized.POST("/submit", submitEndpoint)
    		authorized.POST("/read", readEndpoint)
    
    		// nested group
    		testing := authorized.Group("testing")
    		testing.GET("/analytics", analyticsEndpoint)
    	}
    
    	// Listen and server on 0.0.0.0:8080
    	r.Run(":8080")
    }
    

    model binding与验证

    要绑定一个请求body到某个类型, 可以使用model binding。 目前支持JSON, XML 以及标准from格式 (foo=bar&boo=baz)的绑定。

    所有你想要绑定的域(field), 需要你设置对应的绑定标识。 例如, 要绑定到JSON, 则这样声明json:"fieldname"

    使用Bind方法时, Gin会尝试通过Content-Type头部来推定绑定的类型(如json还是form)。而如果你明确知道要绑定的类型, 可以使用BindWith方法。

    你也可以指定哪些filed需要绑定。 如果某个filed像这样声明: binding:"required", 那么在进行绑定时如果发现是空值(注: 是请求中不存在该field名?), 当前的请求会失败并收到错误提示。

    // Binding from JSON
    type Login struct {
    	User     string `form:"user" json:"user" binding:"required"`
    	Password string `form:"password" json:"password" binding:"required"`
    }
    
    func main() {
    	router := gin.Default()
    
    	// Example for binding JSON ({"user": "manu", "password": "123"})
    	router.POST("/loginJSON", func(c *gin.Context) {
    		var json Login
    		if c.BindJSON(&json) == nil {
    			if json.User == "manu" && json.Password == "123" {
    				c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
    			} else {
    				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
    			}
    		}
    	})
    
    	// Example for binding a HTML form (user=manu&password=123)
    	router.POST("/loginForm", func(c *gin.Context) {
    		var form Login
    		// This will infer what binder to use depending on the content-type header.
    		if c.Bind(&form) == nil {
    			if form.User == "manu" && form.Password == "123" {
    				c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
    			} else {
    				c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
    			}
    		}
    	})
    
    	// Listen and server on 0.0.0.0:8080
    	router.Run(":8080")
    }
    

    ###Multipart/Urlencoded表单请求方式的绑定

    package main
    
    import (
    	"github.com/gin-gonic/gin"
    	"github.com/gin-gonic/gin/binding"
    )
    
    type LoginForm struct {
    	User     string `form:"user" binding:"required"`
    	Password string `form:"password" binding:"required"`
    }
    
    func main() {
    	router := gin.Default()
    	router.POST("/login", func(c *gin.Context) {
    		// you can bind multipart form with explicit binding declaration:
    		// c.BindWith(&form, binding.Form)
    		// or you can simply use autobinding with Bind method:
    		var form LoginForm
    		// in this case proper binding will be automatically selected
    		if c.Bind(&form) == nil {
    			if form.User == "user" && form.Password == "password" {
    				c.JSON(200, gin.H{"status": "you are logged in"})
    			} else {
    				c.JSON(401, gin.H{"status": "unauthorized"})
    			}
    		}
    	})
    	router.Run(":8080")
    }
    

    使用以下命令测试:

    $ curl -v --form user=user --form password=password http://localhost:8080/login
    

    XML和JSON的渲染

    func main() {
    	r := gin.Default()
    
    	// gin.H is a shortcut for map[string]interface{}
    	r.GET("/someJSON", func(c *gin.Context) {
    		c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
    	})
    
    	r.GET("/moreJSON", func(c *gin.Context) {
    		// You also can use a struct
    		var msg struct {
    			Name    string `json:"user"`
    			Message string
    			Number  int
    		}
    		msg.Name = "Lena"
    		msg.Message = "hey"
    		msg.Number = 123
    		// Note that msg.Name becomes "user" in the JSON
    		// Will output  :   {"user": "Lena", "Message": "hey", "Number": 123}
    		c.JSON(http.StatusOK, msg)
    	})
    
    	r.GET("/someXML", func(c *gin.Context) {
    		c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
    	})
    
    	// Listen and server on 0.0.0.0:8080
    	r.Run(":8080")
    }
    

    ####处理静态文件的请求

    func main() {
    	router := gin.Default()
    	router.Static("/assets", "./assets")
    	router.StaticFS("/more_static", http.Dir("my_file_system"))
    	router.StaticFile("/favicon.ico", "./resources/favicon.ico")
    
    	// Listen and server on 0.0.0.0:8080
    	router.Run(":8080")
    }
    

    ####HTML模板渲染

    Using LoadHTMLTemplates()

    func main() {
    	router := gin.Default()
    	router.LoadHTMLGlob("templates/*")
    	//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
    	router.GET("/index", func(c *gin.Context) {
    		c.HTML(http.StatusOK, "index.tmpl", gin.H{
    			"title": "Main website",
    		})
    	})
    	router.Run(":8080")
    }
    

    templates/index.tmpl

    <html>
    	<h1>
    		{{ .title }}
    	</h1>
    </html>
    

    使用不同路径下但相同文件名的模板

    func main() {
    	router := gin.Default()
    	router.LoadHTMLGlob("templates/**/*")
    	router.GET("/posts/index", func(c *gin.Context) {
    		c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
    			"title": "Posts",
    		})
    	})
    	router.GET("/users/index", func(c *gin.Context) {
    		c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
    			"title": "Users",
    		})
    	})
    	router.Run(":8080")
    }
    

    templates/posts/index.tmpl

    {{ define "posts/index.tmpl" }}
    <html><h1>
    	{{ .title }}
    </h1>
    <p>Using posts/index.tmpl</p>
    </html>
    {{ end }}
    

    templates/users/index.tmpl

    {{ define "users/index.tmpl" }}
    <html><h1>
    	{{ .title }}
    </h1>
    <p>Using users/index.tmpl</p>
    </html>
    {{ end }}
    

    你也可以使用自己的渲染模板

    import "html/template"
    
    func main() {
    	router := gin.Default()
    	html := template.Must(template.ParseFiles("file1", "file2"))
    	router.SetHTMLTemplate(html)
    	router.Run(":8080")
    }
    

    重定向

    实现HTTP重定向并不麻烦:

    r.GET("/test", func(c *gin.Context) {
    	c.Redirect(http.StatusMovedPermanently, "http://www.google.com/")
    })
    

    内部或外部的地址都是支持的。

    定制中间件

    func Logger() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		t := time.Now()
    
    		// Set example variable
    		c.Set("example", "12345")
    
    		// before request
    
    		c.Next()
    
    		// after request
    		latency := time.Since(t)
    		log.Print(latency)
    
    		// access the status we are sending
    		status := c.Writer.Status()
    		log.Println(status)
    	}
    }
    
    func main() {
    	r := gin.New()
    	r.Use(Logger())
    
    	r.GET("/test", func(c *gin.Context) {
    		example := c.MustGet("example").(string)
    
    		// it would print: "12345"
    		log.Println(example)
    	})
    
    	// Listen and server on 0.0.0.0:8080
    	r.Run(":8080")
    }
    

    使用 BasicAuth() 中间件

    // simulate some private data
    var secrets = gin.H{
    	"foo":    gin.H{"email": "foo@bar.com", "phone": "123433"},
    	"austin": gin.H{"email": "austin@example.com", "phone": "666"},
    	"lena":   gin.H{"email": "lena@guapa.com", "phone": "523443"},
    }
    
    func main() {
    	r := gin.Default()
    
    	// Group using gin.BasicAuth() middleware
    	// gin.Accounts is a shortcut for map[string]string
    	authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
    		"foo":    "bar",
    		"austin": "1234",
    		"lena":   "hello2",
    		"manu":   "4321",
    	}))
    
    	// /admin/secrets endpoint
    	// hit "localhost:8080/admin/secrets
    	authorized.GET("/secrets", func(c *gin.Context) {
    		// get user, it was set by the BasicAuth middleware
    		user := c.MustGet(gin.AuthUserKey).(string)
    		if secret, ok := secrets[user]; ok {
    			c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
    		} else {
    			c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
    		}
    	})
    
    	// Listen and server on 0.0.0.0:8080
    	r.Run(":8080")
    }
    

    中间件中的Goroutines

    在一个middleware或handler中使用goroutine时, 你不能直接使用源gin.Context, 而只能使用它的一份只读拷贝。

    func main() {
    	r := gin.Default()
    
    	r.GET("/long_async", func(c *gin.Context) {
    		// create copy to be used inside the goroutine
    		cCp := c.Copy()
    		go func() {
    			// simulate a long task with time.Sleep(). 5 seconds
    			time.Sleep(5 * time.Second)
    
    			// note that you are using the copied context "cCp", IMPORTANT
    			log.Println("Done! in path " + cCp.Request.URL.Path)
    		}()
    	})
    
    	r.GET("/long_sync", func(c *gin.Context) {
    		// simulate a long task with time.Sleep(). 5 seconds
    		time.Sleep(5 * time.Second)
    
    		// since we are NOT using a goroutine, we do not have to copy the context
    		log.Println("Done! in path " + c.Request.URL.Path)
    	})
    
    	// Listen and server on 0.0.0.0:8080
    	r.Run(":8080")
    }
    

    自定义HTTP配置

    直接使用http.ListenAndServe(), 示例:

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

    或者

    func main() {
    	router := gin.Default()
    
    	s := &http.Server{
    		Addr:           ":8080",
    		Handler:        router,
    		ReadTimeout:    10 * time.Second,
    		WriteTimeout:   10 * time.Second,
    		MaxHeaderBytes: 1 << 20,
    	}
    	s.ListenAndServe()
    }
    

    平滑重启或关闭

    是否需要平滑重启或关闭你的服务?有以下这些方式可以实现。

    我们可以用 fvbock/endless来替换默认的方法 ListenAndServe,详情请参考#296

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

    或者, 除使用endless之外的方法:

    • manners: 一种平滑关闭自己的Go HTTP服务。
    展开全文
  • Gin middleware

    2020-12-27 10:12:50
    <div><p>I would very much like to see some middleware for this that works with the <a href="https://github.com/gin-gonic/gin">gin</a> framework.</p><p>该提问来源于开源项目:auth0/go-jwt-...
  • 开源项目-gin-gonic-gin.zip,What's this line in the Gin source code doing
  • gin version

    2020-12-09 00:25:51
    <p>Would it be ok to reuse/tweak the code here to produce a separate gin version? <p>The gin middleware specific part would look as follows: <pre><code> // Middleware is a gin native version of the ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,700
精华内容 3,480
关键字:

gin