转载

golang 1. go 使用gin 框架 安装与配置

安装与配置
安装:

$ go get gopkg.in/gin-gonic/gin.v1
注意:确保 GOPATH GOROOT 已经配置

导入:

import "gopkg.in/gin-gonic/gin.v1"

框架架构

HTTP 服务器
1.默认服务器

router.Run()
2.HTTP 服务器

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

func main() {
router := gin.Default()
http.ListenAndServe(":8080", router)
}
或者自定义 HTTP 服务器的配置:

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()

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

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

router := gin.Default()
router.GET("/", handler)
// [...]
endless.ListenAndServe(":4242", router)
除了 endless 还可以用manners:

manners 兼容windows

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

srv := http.Server{
Addr: ":8080",
Handler: router,
}

go func() {
if err :+ srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}

// 其他代码, 等待关闭信号
...

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown: ", err)
}
log.Println("Server exiting")
完整的代码见 graceful-shutdown.

生命周期

Context

路由

基本路由 gin 框架中采用的路由库是 httprouter。
// 创建带有默认中间件的路由:
// 日志与恢复中间件
router := gin.Default()
//创建不带中间件的路由:
//r := gin.New()

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)

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

router.GET("/string/:name", func(c *gin.Context) {
name := c.Param("name")
fmt.Println("Hello %s", name)
})
URL 参数通过 DefaultQuery 或 Query 方法获取

// url 为 http://localhost:8080/welcome?name=ningskyer时
// 输出 Hello ningskyer
// url 为 http://localhost:8080/welcome时
// 输出 Hello Guest
router.GET("/welcome", func(c *gin.Context) {
name := c.DefaultQuery("name", "Guest") //可设置默认值
// 是 c.Request.URL.Query().Get("lastname") 的简写
lastname := c.Query("lastname")
fmt.Println("Hello %s", name)
})
表单参数通过 PostForm 方法获取

//form
router.POST("/form", func(c *gin.Context) {
type := c.DefaultPostForm("type", "alert")//可设置默认值
msg := c.PostForm("msg")
title := c.PostForm("title")
fmt.Println("type is %s, msg is %s, title is %s", type, msg, title)
})

路由群组
someGroup := router.Group("/someGroup")
{
someGroup.GET("/someGet", getting)
someGroup.POST("/somePost", posting)
}

控制器

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

// 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()

// 绑定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"})
        }
    }
})

// 绑定普通表单的例子 (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
    var form Login
    // 根据请求头中 content-type 自动推断.
    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"})
        }
    }
})
// 绑定多媒体表单的例子 (user=manu&password=123)
router.POST("/login", func(c *gin.Context) {
    var form LoginForm
    // 你可以显式声明来绑定多媒体表单:
    // c.BindWith(&form, binding.Form)
    // 或者使用自动推断:
    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"})
        }
    }
})
// Listen and serve on 0.0.0.0:8080
router.Run(":8080")

}

请求

请求头

请求参数

Cookies

上传文件
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)
}   

})

响应

响应头

附加Cookie

字符串响应
c.String(http.StatusOK, "some string")

JSON/XML/YAML响应
r.GET("/moreJSON", func(c *gin.Context) {
// You also can use a struct
var msg struct {
Name string json:"user" xml:"user"
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// 注意 msg.Name 变成了 "user" 字段
// 以下方式都会输出 : {"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123})
c.XML(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123})
c.YAML(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123})
c.JSON(http.StatusOK, msg)
c.XML(http.StatusOK, msg)
c.YAML(http.StatusOK, msg)
})

视图响应
先要使用 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")
}
模板结构定义



{{ .title }}



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

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

}
templates/posts/index.tmpl

{{ define "posts/index.tmpl" }}


{{ .title }}



{{ end }}

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

```go
import "html/template"

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

文件响应
//获取当前文件的相对路径
router.Static("/assets", "./assets")
//
router.StaticFS("/more_static", http.Dir("my_file_system"))
//获取相对路径下的文件
router.StaticFile("/favicon.ico", "./resources/favicon.ico")

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

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

func main() {
r := gin.Default()
//1. 异步
r.GET("/long_async", func(c gin.Context) {
// goroutine 中只能使用只读的上下文 c.Copy()
cCp := c.Copy()
go func() {
time.Sleep(5
time.Second)

        // 注意使用只读上下文
        log.Println("Done! in path " + cCp.Request.URL.Path)
    }()
})
//2. 同步
r.GET("/long_sync", func(c *gin.Context) {
    time.Sleep(5 * time.Second)

    // 注意可以使用原始上下文
    log.Println("Done! in path " + c.Request.URL.Path)
})

// Listen and serve on 0.0.0.0:8080
r.Run(":8080")

}

视图

传参

视图组件

中间件

分类使用方式
// 1.全局中间件
router.Use(gin.Logger())
router.Use(gin.Recovery())

// 2.单路由的中间件,可以加任意多个
router.GET("/benchmark", MyMiddelware(), benchEndpoint)

// 3.群组路由的中间件
authorized := router.Group("/", MyMiddelware())
// 或者这样用:
authorized := router.Group("/")
authorized.Use(MyMiddelware())
{
authorized.POST("/login", loginEndpoint)
}

自定义中间件
//定义
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()

    // 在gin上下文中定义变量
    c.Set("example", "12345")

    // 请求前

    c.Next()//处理请求

    // 请求后
    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) {
    //获取gin上下文中的变量
    example := c.MustGet("example").(string)

    // 会打印: "12345"
    log.Println(example)
})

// 监听运行于 0.0.0.0:8080
r.Run(":8080")

}

中间件参数

内置中间件 1.简单认证BasicAuth

// 模拟私有数据
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()

// 使用 gin.BasicAuth 中间件,设置授权用户
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
    "foo":    "bar",
    "austin": "1234",
    "lena":   "hello2",
    "manu":   "4321",
}))

// 定义路由
authorized.GET("/secrets", func(c *gin.Context) {
    // 获取提交的用户名(AuthUserKey)
    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 serve on 0.0.0.0:8080
r.Run(":8080")

}

正文到此结束
本文目录