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. Adobe Flash player 过期

    完美解决问题的办法,在百度中输入 "adobe flash player debugger",如图进入官网 选择对应操作系统的对应版本,下载安装,重启浏览器,一切ok IE内核浏览 ...

  2. Qt 与 .Net 为何不兼容

    哪怕是非Qt的静态库里用了 .Net 也不行.

  3. 在Spring Boot中配置web app

    文章目录 添加依赖 配置端口 配置Context Path 配置错误页面 在程序中停止Spring Boot 配置日志级别 注册Servlet 切换嵌套服务器 在Spring Boot中配置web a ...

  4. @SessionAttributes 和 @SessionAttribute的区别

    @SessionAttributes 和 @SessionAttribute的区别 Spring MVC中有两个长得非常像的注解:@SessionAttributes 和 @SessionAttrib ...

  5. 计算2的n次幂htm代码

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. canvas 绘图api的位置问题

    很久没碰canvas了,今天因为canvas绘图的为之问题浪费了一些时间. 我们知道canvas的默认宽高是300X150嘛. 实际使用的时候当然是自定义一个高宽啦. 通常我们会习惯性地在js中通过c ...

  7. Linux运维面试题:请简要说明Linux系统在目标板上的启动过程?

    Linux运维面试题:请简要说明Linux系统在目标板上的启动过程? 该问题是Linux运维面试最常见的问题之一,问题答案如下: 1.用户打开PC的电源,BIOS开机自检,按BIOS中设置的启动设备( ...

  8. 谈谈JavaScript中的变量、指针和引用

    1.变量 我们可能产生这样一个疑问:编程语言中的变量到底是什么意思呢? 事实上,当我们定义了一个变量a时,就是在存储器中指定了一组存储单元,并将这组存储单元命名为a.变量a的值实际上描述的是这组存储单 ...

  9. MySQL重新初始化安装数据库

     删除./mysql/var下的所有数据后,怎么重新安装初始数据库? (1)进入./mysql/bin目录下,执行脚本./mysql_install_db: (2)执行完(1)后,此时会在./mysq ...

  10. 图论--割点--Tarjan模板

    #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> ...