Gin 是一个 go 写的 Web 框架,具有高性能的优点。官方地址:https://github.com/gin-gonic/gin
安装:
- $ go get gopkg.in/gin-gonic/gin.v1
注意:确保 GOPATH GOROOT 已经配置
导入:
- import "gopkg.in/gin-gonic/gin.v1"
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.
- // 创建带有默认中间件的路由:
- // 日志与恢复中间件
- 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")
- }
- 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)
- }
- })
-
- c.String(http.StatusOK, "some string")
-
- 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")
- }
模板结构定义
- <html>
- <h1>
- {{ .title }}
- </h1>
- </html>
不同文件夹下模板名字可以相同,此时需要 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 与结尾 end 不可少 -->
- {{ define "posts/index.tmpl" }}
- <html><h1>
- {{ .title }}
- </h1>
- </html>
- {{ 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")
- }
- // 模拟私有数据
- 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")
- }
Golang常用的Mongodb驱动为 mgo.v2, 查看文档
mgo 使用方式如下:
- //定义 Person 结构,字段须为首字母大写
- type Person struct {
- Name string
- Phone string
- }
-
- router.GET("/mongo", func(context *gin.Context){
- //可本地可远程,不指定协议时默认为http协议访问,此时需要设置 mongodb 的nohttpinterface=false来打开httpinterface。
- //也可以指定mongodb协议,如 "mongodb://127.0.0.1:27017"
- var MOGODB_URI = "127.0.0.1:27017"
- //连接
- session, err := mgo.Dial(MOGODB_URI)
- //连接失败时终止
- if err != nil {
- panic(err)
- }
- //延迟关闭,释放资源
- defer session.Close()
- //设置模式
- session.SetMode(mgo.Monotonic, true)
- //选择数据库与集合
- c := session.DB("adatabase").C("acollection")
- //插入文档
- err = c.Insert(&Person{Name:"Ale", Phone:"+55 53 8116 9639"},
- &Person{Name:"Cla", Phone:"+55 53 8402 8510"})
- //出错判断
- if err != nil {
- log.Fatal(err)
- }
- //查询文档
- result := Person{}
- //注意mongodb存储后的字段大小写问题
- err = c.Find(bson.M{"name": "Ale"}).One(&result)
- //出错判断
- if err != nil {
- log.Fatal(err)
- }
- fmt.Println("Phone:", result.Phone)
- })
-