11.1 Go gin

框架一直是敏捷开发中的利器,能让开发者很快的上手并做出应用。

成长总不会一蹴而就,从写出程序获取成就感,再到精通框架,快速构造应用。

Gin是一个golang的微框架,封装比较优雅,API友好,源码注释比较明确。

具有快速灵活,容错方便等特点。

其实对于golang而言,web框架的依赖要远比Python,Java之类的要小。

自身的net/http足够简单,性能也非常不错

框架更像是一些常用函数或者工具的集合。

借助框架开发,不仅可以省去很多常用的封装带来的时间,也有助于团队的编码风格和形成规范。

1.1. 使用gin

安装gin

go get github.com/gin-gonic/gin

ginhello world

package main

import (
"net/http" "github.com/gin-gonic/gin"
) func main() {
//gin.Default方法创建路由handler
router := gin.Default()
//绑定路由规则和路由函数,gin支持restful方法
//gin把request和response都封装到了gin.Context的上下文环境中
router.GET("/", func(c *gin.Context) {
//返回字符串
c.String(http.StatusOK, "Hello World")
})
//监听端口
router.Run(":8000")
}

运行代码,即可访问http://0.0.0.0:8000/寻找hello world页面

1.2. restful api

借助于postman测试restful api

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
  • DELETE(DELETE):从服务器删除资源。
  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

restful案例

  • GET /zoos:列出所有动物园
  • POST /zoos:新建一个动物园
  • GET /zoos/ID:获取某个指定动物园的信息
  • PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
  • PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
  • DELETE /zoos/ID:删除某个动物园
  • GET /zoos/ID/animals:列出某个指定动物园的所有动物
  • DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

状态码

  • 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
  • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
  • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
  • 204 NO CONTENT - [DELETE]:用户删除数据成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
  • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
  • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

gin代码示例

用户信息接口设计 restful风格

package main

import "github.com/gin-gonic/gin"

func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
//匿名函数可以用有名函数,也可以匿名函数
r.GET("/user/info", func(c *gin.Context) {
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "get user info succ",
})
})
r.POST("/user/info", func(c *gin.Context) {
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "create user info succ",
})
})
r.PUT("/user/info", func(c *gin.Context) {
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "update user info succ",
})
})
r.DELETE("/user/info", func(c *gin.Context) {
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "delete user info succ ",
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}

非restful风格的API

/user/info
/user/create
/user/delete
/user/update

1.3. Gin框架参数传递

实例代码,传入2个get参数

querystring传递参数

package main

import "github.com/gin-gonic/gin"

func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.GET("/user/search", func(c *gin.Context) {
//username := c.DefaultQuery("username", "少林")
username := c.Query("username")
address := c.Query("address")
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "pong",
"username": username,
"address": address,
})
}) r.Run() // listen and serve on 0.0.0.0:8080
}

通过postman发送请求,携带参数

127.0.0.1:8080/user/search?username=超哥&address=沙河

DefaultQuery返回URL参数

package main

import "github.com/gin-gonic/gin"

func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.GET("/user/search", func(c *gin.Context) {
//使用默认值参数
username := c.DefaultQuery("username", "超老板")
//username := c.Query("username")
address := c.Query("address")
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "pong",
"username": username,
"address": address,
})
}) r.Run() // listen and serve on 0.0.0.0:8080
}

访问结果

127.0.0.1:8080/user/search?address=沙河

1.4. 获取路径中的参数

不建议使用,使用表单或者querystring方式获取参数

package main

import "github.com/gin-gonic/gin"

func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.GET("/user/search/:username/:address", func(c *gin.Context) {
username := c.Param("username")
address := c.Param("address")
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "pong",
"username": username,
"address": address,
})
}) r.Run(":8080") // listen and serve on 0.0.0.0:8080
}

访问方式

127.0.0.1:8080/user/search/chao/beijing/

1.5. 获取表单的参数

package main

import "github.com/gin-gonic/gin"

func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.POST("/user/search", func(c *gin.Context) {
//username := c.DefaultPostForm("username", "少林")
username := c.PostForm("username")
address := c.PostForm("address")
//输出json结果给调用方
c.JSON(200, gin.H{
"message": "pong",
"username": username,
"address": address,
})
}) r.Run(":8080") // listen and serve on 0.0.0.0:8080
}

运行方式,使用post方式提交form

1.6. Gin文件上传

上传文件的名字应该由服务端统一文件名规则,防止非法字符

package main

import (
"fmt"
"log"
"net/http" "github.com/gin-gonic/gin"
) func main() {
router := gin.Default()
// Set a lower memory limit for multipart forms (default is 32 MiB)
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// single file
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(),
})
return
}
//
log.Println(file.Filename)
dst := fmt.Sprintf("文件夹路径/%s", file.Filename)
// Upload the file to specific dst.
c.SaveUploadedFile(file, dst)
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
})
})
router.Run(":8080")
}

上传单个文件

上传多个文件

package main

import (
"fmt"
"log"
"net/http" "github.com/gin-gonic/gin"
) func main() {
router := gin.Default()
//文件比较大,默认文件存在内中占32M,大于32M就写入到磁盘
// Set a lower memory limit for multipart forms (default is 32 MiB)
//设置上传文件的内存大小
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["file"] for index, file := range files {
log.Println(file.Filename)
dst := fmt.Sprintf("/Users/yuchao/go/src/gostudy/gobook/%s_%d", file.Filename, index)
// Upload the file to specific dst.
c.SaveUploadedFile(file, dst)
}
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("%d files uploaded!", len(files)),
})
})
router.Run(":8080")
}

上传方式

1.7. 路由分组

把前缀一样的url,放入一个组

package main

import "github.com/gin-gonic/gin"

func login(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "success",
})
} func read(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "success",
})
} func submit(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "success",
})
} func main() {
//Default返回一个默认的路由引擎
router := gin.Default() // Simple group: v1
// /v1/login
// /v1/submit
// /v1/read
v1 := router.Group("/v1")
{
v1.POST("/login", login)
v1.POST("/submit", submit)
v1.POST("/read", read)
} // Simple group: v2
// /v2/login
// /v2/submit
// /v2/read
v2 := router.Group("/v2")
{
v2.POST("/login", login)
v2.POST("/submit", submit)
v2.POST("/read", read)
} router.Run(":8080")
}

运行方式

1.8. Gin参数绑定

使用方便,提高开发效率

通过反射机制,自动提取querystring,from表单,json,xml等参数到struct中

通过http协议中的context type,识别json、xml或是表单

package main

import (
"fmt"
"net/http" "github.com/gin-gonic/gin"
) // 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 login Login if err := c.ShouldBindJSON(&login); err == nil {
fmt.Printf("login info:%#v\n", login)
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}) // Example for binding a HTML form (user=manu&password=123)
router.POST("/loginForm", func(c *gin.Context) {
var login Login
// This will infer what binder to use depending on the content-type header.
if err := c.ShouldBind(&login); err == nil {
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}) // Example for binding a HTML querystring (user=manu&password=123)
router.GET("/loginForm", func(c *gin.Context) {
var login Login
// This will infer what binder to use depending on the content-type header.
if err := c.ShouldBind(&login); err == nil {
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}) // Listen and serve on 0.0.0.0:8080
router.Run(":8080")
}

json方式提交数据

form方式提交

1.9. gin框架渲染

json渲染

package main

import (
"net/http" "github.com/gin-gonic/gin"
) func main() {
r := gin.Default() // gin.H is a shortcut for map[string]interface{}
r.GET("/someJSON", func(c *gin.Context) {
//第一种方式,自己拼json
c.JSON(http.StatusOK, gin.H{"message": "你大爷", "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 = "大王"
msg.Message = "大王,唐僧给你捉来了"
msg.Number = 123
// Note that msg.Name becomes "user" in the JSON
c.JSON(http.StatusOK, msg)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}

xml渲染

package main

import (
"net/http" "github.com/gin-gonic/gin"
) func main() {
r := gin.Default()
r.GET("/moreXML", func(c *gin.Context) {
// You also can use a struct
type MessageRecord struct {
Name string
Message string
Number int
} var msg MessageRecord
msg.Name = "二王"
msg.Message = "你大爷"
msg.Number = 123
c.XML(http.StatusOK, msg)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}

HTML 模板渲染

package main

import (
"net/http" "github.com/gin-gonic/gin"
) func main() {
router := gin.Default()
//模板路径,一层就是templates/*
//两层就是templates/**/*
router.LoadHTMLGlob("templates/**/*")
router.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "我是标题",
})
})
router.GET("/users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "我是标题",
})
})
router.Run(":8080")
}

访问路径

注意先go build main.go 再运行

http://127.0.0.1:8080/users/index
http://127.0.0.1:8080/posts/index

1.10. Gin 静态文件服务器

package main

import (
"github.com/gin-gonic/gin"
) func main() {
r := gin.Default()
//在main.go同级准备static文件夹,内含图片
r.Static("/static", "./static")
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}

注意先go build main.go 再运行

go build main.go
./main.go http://127.0.0.1:8080/static/1.png

1.11. Go中间件

golang的net/http设计的一大特点就是特别容易构建中间件。

gin也提供了类似的中间件。需要注意的是中间件只对注册过的路由函数起作用。

对于分组路由,嵌套使用中间件,可以限定中间件的作用范围。

中间件分为全局中间件,单个路由中间件和群组中间件。

Gin框架允许请求处理过程中,加入用户自己的钩子函数。这个函数就是中间件
利用中间件可以处理例如耗时统计,日志打印,登录校验等

计算耗时的中间件

package main

import (
"log"
"time" "net/http" "github.com/gin-gonic/gin"
) func StatCost() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now() //可以设置一些公共参数,
c.Set("example", "12345")
//等其他中间件先执行
c.Next()
//获取耗时
latency := time.Since(t)
//打印花费的时间 319微秒
log.Printf("total cost time:%d us", latency/1000)
}
} func main() {
//新建一个路由 gin.New(),没有中间件
// gin.Default()最常用,包含了中间件
//r := gin.New()
r := gin.Default()
//Use是全局中间件,传入一个
r.Use(StatCost()) r.GET("/test", func(c *gin.Context) {
example := c.MustGet("example").(string) // it would print: "12345"
log.Println(example) //
c.JSON(http.StatusOK, gin.H{
"message": "success",
})
}) // Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}

1.12. Gin框架路由原理

gin路由部分用的是

https://github.com/julienschmidt/httprouter

package main

import "github.com/gin-gonic/gin"

func index(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "index",
})
} func main() {
//Defatult返回一个默认的路由引擎
router := gin.Default()
router.POST("/", index)
router.POST("/search", index)
router.POST("/support", index)
router.POST("/blog/:post", index)
router.POST("/about", index)
router.POST("/contact", index)
router.POST(":8080", index)
}

共用url前缀

11.2Go gin的更多相关文章

  1. linux的sed命令(一)

    转自:https://www.cnblogs.com/ginvip/p/6376049.html Sed 简介 sed 是一种新型的,非交互式的编辑器.它能执行与编辑器 vi 和 ex 相同的编辑任务 ...

  2. 【liunx】date命令总结

    命令简介: date 根据给定格式显示日期或设置系统日期时间.print or set the system date and time 指令所在路径:/bin/date 命令语法: date [OP ...

  3. tr命令详解

    基础命令学习目录 原文链接:https://www.cnblogs.com/ginvip/p/6354440.html 什么是tr命令?tr,translate的简写,translate的翻译: [t ...

  4. linux命令总结之date命令

    命令简介: date 根据给定格式显示日期或设置系统日期时间.print or set the system date and time 指令所在路径:/bin/date 命令语法: date [OP ...

  5. linux---(6/27)tr命令和sed命令详解

    Tr命令: tr是简单的单个“字符”处理工具,而sed是功能非常强大的“字符串”处理工具. 用于查询,字符串2用于处理各种转换.tr刚执行时,字符串1中的字符被映射到字符串2中的字符,然后转换操作开始 ...

  6. sed命令用法

    Sed 简介 sed 是一种新型的,非交互式的编辑器.它能执行与编辑器 vi 和 ex 相同的编辑任务.sed 编辑器没有提供交互式使用方式,使用者只能在命令行输入编辑命令.指定文件名,然后在屏幕上查 ...

  7. linux sed命令(擅长输出行)(转)

    linux命令总结sed命令详解 Sed 简介 sed 是一种新型的,非交互式的编辑器.它能执行与编辑器 vi 和 ex 相同的编辑任务.sed 编辑器没有提供交互式使用方式,使用者只能在命令行输入编 ...

  8. 地区sql

    /*Navicat MySQL Data Transfer Source Server : localhostSource Server Version : 50136Source Host : lo ...

  9. Introdution to 3D Game Programming With DirectX11 第11章 习题解答

    11.1 这道题要注意使用了line strip,由于曾经一直用triangle list,所以在几何渲染的时候easy算错定点描绘的顺序. 贴一些代码,大概就能把这个问题解释清楚了,由于框架还不是特 ...

随机推荐

  1. thinkphp--create()的使用方法(个人感悟)

    M方法和D方法的区别 ThinkPHP 中M方法和D方法都用于实例化一个模型类,M方法 用于高效实例化一个基础模型类,而 D方法 用于实例化一个用户定义模型类. 使用M方法 如果是如下情况,请考虑使用 ...

  2. PHP实现MySQL并发查询

    一般的,一个看似很简单的页面,一次http请求后,到达服务端,穿过Cache层,落到后台后,实际可能会有很多很多的数据查询逻辑!而这些查询实际是不相互依赖的,也即可以同时查询.比如各种用户信息,用户的 ...

  3. (第六篇)vim编辑器的使用

    什么是 vim(window文本文档) Vim是从 vi 发展出来的一个文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.简单的来说, vi 是老式的字处理器,不过功 ...

  4. Manjaro Linux 入门使用教程

    Manjaro 初体验 Manjaro 是一款基于 Arch LInux 的自由开源发行版,它吸收了 Arch Linux 优秀丰富的软件管理,同时提供了稳定流畅的操作体验.优雅简单是它的追求,稳定实 ...

  5. 如何用hugo 搭建博客

    1,Hugo 简介 搭建个人博客有很多开源的博客框架,我们要介绍的框架叫作Hugo.Hugo 是一个基于Go 语言的框架,可以快速方便的创建自己的博客. Hugo 支持Markdown 语法,我们可以 ...

  6. 解决layui动态追加的点击事件不起作用问题

    2019独角兽企业重金招聘Python工程师标准>>> //不起作用 $('#demo').on('click', function() { layer.msg('响应点击事件'); ...

  7. 码云git本地仓库链接远程仓库

    原文链接: 点我 git提交时,仓库是空的,本地有源码   应该打开cmd   归到项目路径 然后输入git push -u origin master -f 是把本地的项目强制推送到空的仓库 git ...

  8. RF(控制台及日志输出中文乱码)

    1.查看 ride 版本,我这里是 RIDE 1.7.4.1 running on Python 3.6.0. 2.修改文件 D:\python3.6\Lib\site-packages\roboti ...

  9. RF(元素定位)

    1.id 定位 Open Browser https://www.baidu.com gc Input Text id=kw selenium #注意 id 的值 kw 没有用引号括起来 Close ...

  10. Java——多线程超详细总结

    该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.线程概述 几乎所 ...