回顾

上一节我们用Gin框架快速搭建了一个GET请求的接口,今天来学习路由和参数的获取。

请求动词

熟悉RESTful的同学应该知道,RESTful是网络应用程序的一种设计风格和开发方式,每一个URI代表一种资源,客户端通过POSTDELETEPUTGET四种请求方式来对资源做增删改查的操作。

同样的,Gin框架给我们提供的除这4种动词外,还有PATCHOPTIONHEAD等,详细内容可以查看rentergroup.go文件的IRoutes接口。

type IRoutes interface {
Use(...HandlerFunc) IRoutes Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes StaticFile(string, string) IRoutes
Static(string, string) IRoutes
StaticFS(string, http.FileSystem) IRoutes
}

因为RenterGroup实现了IRoutes定义的所有请求动词,而且gin.Default返回的Engine类型继承了RenterGroup,所以使用起来非常简单,只需要通过gin.Default实例化对象,接下来所有的路由操作都通过该对象使用即可。

func main() {
router := gin.Default()
router.POST("/article", func(c *gin.Context) {
c.String(200, "article post")
})
router.DELETE("/article", func(c *gin.Context) {
c.String(200, "article delete")
})
router.PUT("/article", func(c *gin.Context) {
c.String(200, "article put")
})
router.GET("/article", func(c *gin.Context) {
c.String(200, "article get")
})
router.Run()
}

请求动词的第一个参数是请求路径,第二个参数是用于逻辑处理的函数,可以是匿名的或是其他地方定义的函数名。不同的请求动词可以定义相同的路径,只需要切换动词就可以进入对应的处理逻辑。

curl -X PUT http://localhost:8080/article
curl -X POST http://localhost:8080/article
curl -X GET http://localhost:8080/article
curl -X DELETE http://localhost:8080/article

路由参数

GET请求有两种,一种是在URL后加上?name=pingye,这种是有参数名的,另一种是在路径中直接加上参数值/article/1,这种没有参数名,需要在代码中解析参数。

protocol://hostname:[port]/path/[query]#fragment

我们先来看路由携带参数值的玩法,这里有一道题,怎么利用Gin获取下面链接的参数值1

实现方式非常简单,只需要在路由中设置好占位符:id,冒号为占位符的标志,冒号后面的参数名可以自定义,Gin会将路由与请求地址进行匹配,若匹配成功会将1赋值为占位符:id,只需调用c.Param就可以获取id的值。

router.GET("/article/:id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, id)
})

但是,:id占位符会存在一个问题,如果id参数值传空就会有404的错误提示。

于是Gin提供了另一种占位符*id,使用它就可以达到取空值的目的。

router.GET("/article/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(200, id)
})

普通参数

除了路由携带参数值外,接下来看比较传统的GET传参方式。

http://localhost:8080/welcome?firstname=Jane&lastname=Doe

可以通过c.Queryc.DefaultQuery方法获取问号后的参数。

router.GET("/welcome", func(c *gin.Context) {
firstname := c.DefaultQuery("firstname", "pingyeaa")
lastname := c.Query("lastname")
c.String(200, firstname+" "+lastname)
})

这两者最终都调用了GetQuery方法,唯一的区别是DefaultQuery做了默认值处理。

func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok {
return value
}
return defaultValue
} func (c *Context) Query(key string) string {
value, _ := c.GetQuery(key)
return value
}

表单参数

从HTML提交过来的表单form内容同样也可以轻松获取。

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,
})
})
curl -d "message=pingye" http://localhost:8080/form_post
{"message":"pingye","nick":"anonymous","status":"posted"}

数组类型参数

有时候(例如复选框)前端页面会传来数组类型的值,这种类型name相同,但存储的内容不同。

POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded

依然是一个QueryMap方法就搞定,该方法默认返回map类型。

router.GET("/post", func(c *gin.Context) {
ids := c.QueryMap("ids")
c.String(200, ids["a"]+" "+ids["b"])
})
curl http://localhost:8080/post?ids[a]=pingye&ids[b]=hehe
pingye hehe

文件上传

一般情况下,文件上传会由前端直接传给云存储服务商,比如阿里云、七牛云等,比较少的场景会传给自己的服务器。为了避免书到用时方恨少的情况发生,我们来了解一下。

Gin提供了FormFile方法获取文件流,这个方法返回了一个FileHeader类型的变量,可以调用Filename属性来查看文件名。

type FileHeader struct {
Filename string
Header textproto.MIMEHeader
Size int64 content []byte
tmpfile string
}
router.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.String(200, file.Filename)
})

通过curl请求接口,可以看到轻松获取文件名称。

curl -X POST http://localhost:8080/upload \
-F "file=@/Users/enoch/Downloads/IMG_9216.JPG" \
-H "Content-Type: multipart/form-data"
IMG_9216.JPG

当然不止可以拿到文件名,我们还可以使用SaveUploadedFile方法将文件保存到某个地方,文件保存时要确保有目标目录的操作权限。

router.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.String(200, file.Filename)
err := c.SaveUploadedFile(file, "/Users/enoch/Desktop/ab.png")
if err != nil {
c.String(500, err.Error())
}
})

路由分组

当接口发生重大变更(比如入参出参)时,考虑到向下兼容,一般会新增一个接口,但是又希望新接口的名称显而易见地看出是老接口的升级版,那么就可以在接口名前加上版本号v1/article这种形式。

v1 := r.Group("v1")
{
v1.POST("/login", func(c *gin.Context) {
c.String(200, "v1/login")
})
v1.POST("/submit", func(c *gin.Context) {
c.String(200, "v1/submit")
})
} v2 := r.Group("v2")
{
v2.POST("/login", func(c *gin.Context) {
c.String(200, "v2/login")
})
v2.POST("/submit", func(c *gin.Context) {
c.String(200, "v2/submit")
})
}
curl -X POST http://localhost:8080/v1/login
curl -X POST http://localhost:8080/v2/login

Go语言库代码示例,欢迎star

https://github.com/pingyeaa/golang-examples

感谢大家的观看,如果觉得文章对你有所帮助,欢迎关注公众号「平也」,聚焦Go语言与技术原理。

Gin框架系列02:路由与参数的更多相关文章

  1. Gin 框架 - 安装和路由配置

    目录 概述 Gin 安装 路由配置 推荐阅读 概述 看下 Gin 框架的官方介绍: Gin 是一个用 Go (Golang) 编写的 web 框架. 它是一个类似于 martini 但拥有更好性能的 ...

  2. JavaScript进阶系列02,函数作为参数以及在数组中的应用

    有时候,把函数作为参数可以让代码更简洁. var calculator = { calculate: function(x, y, fn) { return fn(x, y); } }; var su ...

  3. gin框架中请求路由组的使用

    1. gin框架中可以使用路由组来实现对路由的分类 package main import "github.com/gin-gonic/gin" func main() { rou ...

  4. Gin框架系列01:极速上手

    Gin是什么? Gin是Go语言编写的web框架,具备中间件.崩溃处理.JSON验证.内置渲染等多种功能. 准备工作 本系列演示所有代码都在Github中,感兴趣的同学可以自行查阅,欢迎大家一起完善. ...

  5. go的gin框架从请求中获取参数的方法

    前言: go语言的gin框架go里面比较好的一个web框架, github的start数超过了18000.可见此框架的可信度 如何获取请求中的参数 假如有这么一个请求: POST   /post/te ...

  6. Gin框架系列03:换个姿势理解中间件

    什么是中间件 中间件,英译middleware,顾名思义,放在中间的物件,那么放在谁中间呢?本来,客户端可以直接请求到服务端接口. 现在,中间件横插一脚,它能在请求到达接口之前拦截请求,做一些特殊处理 ...

  7. Gin框架04:趣谈参数绑定与校验

    导读 在第二节,我们学习了Gin框架的路由定义与参数接收,今天应一位同学的要求,来讲解一下参数的绑定与校验. 为什么校验参数? 本不必抛出这个问题的,但顾及到初出茅庐的同学,这里解释一下. 假设做一个 ...

  8. gin框架中的路由

    基本路由 gin框架中采用的路由库是基于httrouter做的 地址为:https://github.com/julienschmidt/httprouter httprouter路由库 点击查看代码 ...

  9. gin框架中的路由拆分与注册

    基本的路由注册 下面最基础的gin路由注册方式,适用于路由条目比较少的简单项目或者项目demo. package main import ( "net/http" "gi ...

随机推荐

  1. js 面向对象中,定义一个函数的过程

    定义一个函数做的两件事:1: 实例化一个Function对象:2: 实例化一个Object对象,并给该函数扩展prototype属性指向这个构造函数 大致过程如图所示: 每一种引用类型(函数,对象,数 ...

  2. javascript设计模式和开发实践(阶段一)

    1,设计模式的作用:让人们写出可复用和可维护性高的程序,代价可能是,额外增加代码量: 比较形象的例子:空房间里面放东西,如果日复一日的往里面扔东西,时间久了,很难找到自己想要的东西,调整也不容易,但是 ...

  3. seo搜索优化教程05-SEO常用专业术语

    SEO常用的专业术语很多,星辉信息科技专门抽空进行了整理,主要如下:. SEO 根据搜索引擎规则来进行搜索引擎优化,进而使得在搜索结果中获得较好的排名 关键词 关键词也叫keywords,表示在搜索引 ...

  4. 前端BOM和DOM

      前端基础之BOM和DOM   前戏 到目前为止,我们已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些 ...

  5. Mac Maven安装与配置

    下载 官网地址:http://maven.apache.org/download.cgi 配置环境变量 总步骤 编辑.bash_profile文件 vim ~/.bash_profile 配置mave ...

  6. button控件根据文本自适应

    2020-03-12 每日一例第5天 1.添加按钮1和label.textbox控件,并修改相应的text值: 2.修改textBox1的TextChanged事件并输入代码: button1.Tex ...

  7. js 实现简易留言板功能

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. javaScript 基础知识汇总 (十五)

    1.模块简介 什么是模块: 模块就是一个文件,一个脚本,通过关键字export 和 import 交换模块之间的功能. export 关键字表示在当前模块之外可以访问的变量和功能. import 关键 ...

  9. Python基础 | 日期时间操作

    目录 获取时间 时间映射 格式转换 字符串转日期 日期转字符串 unixtime 时间计算 时间偏移 时间差 "日期时间数据"作为三大基础数据类型之一,在数据分析中会经常遇到. 本 ...

  10. swagger 报 i.s.m.parameters.AbstractSerializableParameter - Illegal DefaultValue null for parameter type integer java.lang.NumberFormatException: For input string

    解决 方法 添加这两个依赖....别问我有啥子用....我也不知道..能解决问题 <dependency> <groupId>io.swagger</groupId> ...