做电影网站如何规避版权,装修公司哪家好广州市,自助网站免费,搜狗快速收录方法文章目录 一、Gin框架介绍二、RESTful API三、Gin渲染1. HTML渲染2. 自定义模板函数3. 静态文件处理4. 使用模板继承5. 补充文件路径处理6. JSON渲染7. XML渲染8. YMAL渲染9. protobuf渲染 四、Gin获取各种方式传递过来的参数1、获取querystring参数2、获取form参数3、获取path… 文章目录 一、Gin框架介绍二、RESTful API三、Gin渲染1. HTML渲染2. 自定义模板函数3. 静态文件处理4. 使用模板继承5. 补充文件路径处理6. JSON渲染7. XML渲染8. YMAL渲染9. protobuf渲染 四、Gin获取各种方式传递过来的参数1、获取querystring参数2、获取form参数3、获取path参数 三、参数绑定四、文件上传1、单个文件上传2、多个文件上传 五、Gin中间件六、重定向1. HTTP重定向2. 路由重定向 七、Gin路由1. 普通路由2. 路由组 一、Gin框架介绍
Gin是基于httprouter开发的Web框架,也是当前Go世界里最流行的Web框架。
Gin框架安装与使用 下载并安装Gin:
go get -u github.com/gin-gonic/gin第一个Gin示例
package mainimport (github.com/gin-gonic/gin
)func main() {// 创建一个默认的路由引擎r : gin.Default()// GET请求方式/hello请求的路径// 当客户端以GET方法请求/hello路径时会执行后面的匿名函数r.GET(/hello, func(c *gin.Context) {// c.JSON返回JSON格式的数据c.JSON(200, gin.H{message: Hello world!,})})// 启动HTTP服务默认在0.0.0.0:8080启动服务r.Run()
}将上面的代码保存并编译执行然后使用浏览器打开127.0.0.1:8080/hello就能看到一串JSON字符串。
二、RESTful API
REST与技术无关代表的是一种软件架构风格REST是Representational State Transfer的简称中文翻译为“表征状态转移”或“表现层状态转化”。
简单来说REST的含义就是客户端与Web服务器之间进行交互的时候使用HTTP协议中的4个请求方法代表不同的动作。 GET用来获取资源 POST用来新建资源 PUT用来更新资源 DELETE用来删除资源。
只要API程序遵循了REST风格那就可以称其为RESTful API。目前在前后端分离的架构中前后端基本都是通过RESTful API来进行交互。
例如我们现在要编写一个管理书籍的系统我们可以查询对一本书进行查询、创建、更新和删除等操作我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。按照经验我们通常会设计成如下模式
请求方法URL含义GET/book查询书籍信息POST/create_book创建书籍记录POST/update_book更新书籍信息POST/delete_book删除书籍信息
同样的需求我们按照RESTful API设计如下
请求方法URL含义GET/book查询书籍信息POST/book创建书籍记录Put/book更新书籍信息Delete/book删除书籍信息
Gin框架支持开发RESTful API的开发。
func main() {r : gin.Default()r.GET(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: GET,})})r.POST(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: POST,})})r.PUT(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: PUT,})})r.DELETE(/book, func(c *gin.Context) {c.JSON(200, gin.H{message: DELETE,})})
}此外还有一个可以匹配所有请求方法的Any方法如下
r.Any(/test, func(c *gin.Context) {...})为没有配置处理函数的路由添加处理程序默认情况下它返回404代码下面的代码为没有匹配到路由的请求都返回views/404.html页面。
r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, views/404.html, nil)})开发RESTful API的时候我们通常使用Postman来作为客户端的测试工具。
三、Gin渲染
1. HTML渲染
我们首先定义一个存放模板文件的templates文件夹然后在其内部按照业务分别定义一个posts文件夹和一个users文件夹。
posts/index.html文件的内容如下
{{define posts/index.html}}
!DOCTYPE html
html langenheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0meta http-equivX-UA-Compatible contentieedgetitleposts/index/title
/head
body{{.title}}
/body
/html
{{end}}users/index.html文件的内容如下
{{define users/index.html}}
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0meta http-equivX-UA-Compatible contentieedgetitleusers/index/title
/head
body{{.title}}
/body
/html
{{end}}Gin框架中使用LoadHTMLGlob()或者LoadHTMLFiles()方法进行HTML模板渲染。
func main() {r : gin.Default()r.LoadHTMLGlob(43-gin-demo/templates/**/*)//r.LoadHTMLFiles(templates/posts/index.html, templates/users/index.html)r.GET(/posts/index, func(c *gin.Context) {c.HTML(http.StatusOK, posts/index.html, gin.H{title: posts/index,})})r.GET(users/index, func(c *gin.Context) {c.HTML(http.StatusOK, users/index.html, gin.H{title: users/index,})})r.Run(:8080)
}启动后访问如下
2. 自定义模板函数
定义一个不转义相应内容的safe模板函数如下
func main() {router : gin.Default()router.SetFuncMap(template.FuncMap{safe: func(str string) template.HTML{return template.HTML(str)},})router.LoadHTMLFiles(./index.tmpl)router.GET(/index, func(c *gin.Context) {c.HTML(http.StatusOK, index.tmpl, a hrefhttps://www.baidu.com百度官方网址/a)})router.Run(:8080)
}在index.tmpl中使用定义好的safe模板函数
!DOCTYPE html
html langzh-CN
headtitle修改模板引擎的标识符/title
/head
body
div{{ . | safe }}/div
/body
/html启动后访问
3. 静态文件处理
当我们渲染的HTML文件中引用了静态文件时我们只需要按照以下方式在渲染页面前调用gin.Static方法即可。
func main() {r : gin.Default()r.Static(/static, ./static)r.LoadHTMLGlob(templates/**/*)// ...r.Run(:8080)
}4. 使用模板继承
Gin框架默认都是使用单模板如果需要使用block template功能可以通过github.com/gin-contrib/multitemplate库实现具体示例如下
首先假设我们项目目录下的templates文件夹下有以下模板文件其中home.tmpl和index.tmpl继承了base.tmpl
templates
├── includes
│ ├── home.tmpl
│ └── index.tmpl
├── layouts
│ └── base.tmpl
└── scripts.tmpl然后我们定义一个loadTemplates函数如下
func loadTemplates(templatesDir string) multitemplate.Renderer {r : multitemplate.NewRenderer()layouts, err : filepath.Glob(templatesDir /layouts/*.tmpl)if err ! nil {panic(err.Error())}includes, err : filepath.Glob(templatesDir /includes/*.tmpl)if err ! nil {panic(err.Error())}// 为layouts/和includes/目录生成 templates mapfor _, include : range includes {layoutCopy : make([]string, len(layouts))copy(layoutCopy, layouts)files : append(layoutCopy, include)r.AddFromFiles(filepath.Base(include), files...)}return r
}我们在main函数中
func indexFunc(c *gin.Context){c.HTML(http.StatusOK, index.tmpl, nil)
}func homeFunc(c *gin.Context){c.HTML(http.StatusOK, home.tmpl, nil)
}func main(){r : gin.Default()r.HTMLRender loadTemplates(./templates)r.GET(/index, indexFunc)r.GET(/home, homeFunc)r.Run()
}5. 补充文件路径处理
关于模板文件和静态文件的路径我们需要根据公司/项目的要求进行设置。可以使用下面的函数获取当前执行程序的路径。
func getCurrentPath() string {if ex, err : os.Executable(); err nil {return filepath.Dir(ex)}return ./
}6. JSON渲染
func main() {r : gin.Default()// gin.H 是map[string]interface{}的缩写r.GET(/someJSON, func(c *gin.Context) {// 方式一自己拼接JSONc.JSON(http.StatusOK, gin.H{message: Hello world!})})r.GET(/moreJSON, func(c *gin.Context) {// 方法二使用结构体var msg struct {Name string json:userMessage stringAge int}msg.Name 小王子msg.Message Hello world!msg.Age 18c.JSON(http.StatusOK, msg)})r.Run(:8080)
}7. XML渲染
注意需要使用非匿名结构体类型。
func main() {r : gin.Default()// gin.H 是map[string]interface{}的缩写r.GET(/someXML, func(c *gin.Context) {// 方式一自己拼接JSONc.XML(http.StatusOK, gin.H{message: Hello world!})})r.GET(/moreXML, func(c *gin.Context) {// 方法二使用结构体type MessageRecord struct {Name stringMessage stringAge int}var msg MessageRecordmsg.Name 小王子msg.Message Hello world!msg.Age 18c.XML(http.StatusOK, msg)})r.Run(:8080)
}8. YMAL渲染
r.GET(/someYAML, func(c *gin.Context) {c.YAML(http.StatusOK, gin.H{message: ok, status: http.StatusOK})
})9. protobuf渲染
r.GET(/someProtoBuf, func(c *gin.Context) {reps : []int64{int64(1), int64(2)}label : test// protobuf 的具体定义写在 testdata/protoexample 文件中。data : protoexample.Test{Label: label,Reps: reps,}// 请注意数据在响应中变为二进制数据// 将输出被 protoexample.Test protobuf 序列化了的数据c.ProtoBuf(http.StatusOK, data)
})四、Gin获取各种方式传递过来的参数
1、获取querystring参数
querystring指的是URL中?后面携带的参数例如/user/search?usernamelymaddress北京。
获取请求的querystring参数的方法如下
func main() {//Default返回一个默认的路由引擎r : gin.Default()r.GET(/user/search, func(c *gin.Context) {// DefaultPostForm取不到值时会返回指定的默认值username : c.DefaultQuery(username, lym)//username : c.Query(username)address : c.Query(address)//输出json结果给调用方c.JSON(http.StatusOK, gin.H{message: ok,username: username,address: address,})})r.Run()
}2、获取form参数
请求的数据通过form表单来提交例如向/user/search发送一个POST请求
获取请求数据的方式如下
func main() {//Default返回一个默认的路由引擎r : gin.Default()r.POST(/user/search, func(c *gin.Context) {// DefaultPostForm取不到值时会返回指定的默认值//username : c.DefaultPostForm(username, lym)username : c.PostForm(username)address : c.PostForm(address)//输出json结果给调用方c.JSON(http.StatusOK, gin.H{message: ok,username: username,address: address,})})r.Run(:8080)
}3、获取path参数
请求的参数通过URL路径传递例如/user/search/lym/北京。
获取请求URL路径中的参数的方式如下。
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(http.StatusOK, gin.H{message: ok,username: username,address: address,})})r.Run(:8080)
}三、参数绑定
为了能够更方便的获取请求相关参数提高开发效率我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON、XML等参数到结构体中。
下面的示例代码演示了.ShouldBind()强大的功能它能够基于请求自动提取JSON、form表单和QueryString类型的数据并把值绑定到指定的结构体对象。
// Binding from JSON
type Login struct {User string form:user json:user binding:requiredPassword string form:password json:password binding:required
}func main() {router : gin.Default()// 绑定JSON的示例 ({user: lym, password: 123456})router.POST(/loginJSON, func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err : c.ShouldBind(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()})}})// 绑定form表单示例 (userlympassword123456)router.POST(/loginForm, func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器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()})}})// 绑定QueryString示例 (/loginQuery?userq1mipassword123456)router.GET(/loginQuery, func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器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:8080router.Run(:8080)
}ShouldBind会按照下面的顺序解析请求中的数据完成绑定 如果是 GET 请求只使用 Form 绑定引擎query。 如果是 POST 请求首先检查 content-type 是否为 JSON 或 XML然后再使用 Formform-data。
四、文件上传
1、单个文件上传
func main() {router : gin.Default()// 处理multipart forms提交文件时默认的内存限制是32 MiB// 可以通过下面的方式修改// router.MaxMultipartMemory 8 20 // 8 MiBrouter.POST(/upload, func(c *gin.Context) {// 单个文件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(C:/tmp/%s, file.Filename)// 上传文件到指定的目录c.SaveUploadedFile(file, dst)c.JSON(http.StatusOK, gin.H{message: fmt.Sprintf(%s uploaded!, file.Filename),})})router.Run()
}2、多个文件上传
func main() {router : gin.Default()// 处理multipart forms提交文件时默认的内存限制是32 MiB// 可以通过下面的方式修改// router.MaxMultipartMemory 8 20 // 8 MiBrouter.POST(/upload, func(c *gin.Context) {// Multipart formform, _ : c.MultipartForm()files : form.File[file]for index, file : range files {log.Println(file.Filename)dst : fmt.Sprintf(C:/tmp/%s_%d, file.Filename, index)// 上传文件到指定的目录c.SaveUploadedFile(file, dst)}c.JSON(http.StatusOK, gin.H{message: fmt.Sprintf(%d files uploaded!, len(files)),})})router.Run()
}五、Gin中间件
Gin框架允许开发者在处理请求的过程中加入用户自己的钩子Hook函数。这个钩子函数就叫中间件中间件适合处理一些公共的业务逻辑比如登录校验、日志打印、耗时统计等。
Gin中的中间件必须是一个gin.HandlerFunc类型。例如我们像下面的代码一样定义一个中间件。
// StatCost 是一个统计耗时请求耗时的中间件
func StatCost() gin.HandlerFunc {return func(c *gin.Context) {start : time.Now()c.Set(name, 小王子)// 执行其他中间件c.Next()// 计算耗时cost : time.Since(start)log.Println(cost)}
}然后注册中间件的时候可以在全局注册。
func main() {// 新建一个没有任何默认中间件的路由r : gin.New()// 注册一个全局中间件r.Use(StatCost())r.GET(/test, func(c *gin.Context) {name : c.MustGet(name).(string)log.Println(name)c.JSON(http.StatusOK, gin.H{message: Hello world!,})})r.Run()
}也可以给某个路由单独注册中间件。
// 给/test2路由单独注册中间件可注册多个r.GET(/test2, StatCost(), func(c *gin.Context) {name : c.MustGet(name).(string)log.Println(name)c.JSON(http.StatusOK, gin.H{message: Hello world!,})})六、重定向
1. HTTP重定向
HTTP 重定向很容易。内部、外部重定向均支持。
r.GET(/test, func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, http://www.sogo.com/)
})2. 路由重定向
路由重定向使用HandleContext
r.GET(/test, func(c *gin.Context) {// 指定重定向的URLc.Request.URL.Path /test2r.HandleContext(c)
})
r.GET(/test2, func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{hello: world})
})七、Gin路由
1. 普通路由
r.GET(/index, func(c *gin.Context) {...})
r.GET(/login, func(c *gin.Context) {...})
r.POST(/login, func(c *gin.Context) {...})此外还有一个可以匹配所有请求方法的Any方法如下
r.Any(/test, func(c *gin.Context) {...})为没有配置处理函数的路由添加处理程序默认情况下它返回404代码下面的代码为没有匹配到路由的请求都返回views/404.html页面。
r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, views/404.html, nil)})2. 路由组
我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性一对{}包裹同组的路由这只是为了看着清晰你用不用{}包裹功能上没什么区别。
func main() {r : gin.Default()userGroup : r.Group(/user){userGroup.GET(/index, func(c *gin.Context) {...})userGroup.GET(/login, func(c *gin.Context) {...})userGroup.POST(/login, func(c *gin.Context) {...})}shopGroup : r.Group(/shop){shopGroup.GET(/index, func(c *gin.Context) {...})shopGroup.GET(/cart, func(c *gin.Context) {...})shopGroup.POST(/checkout, func(c *gin.Context) {...})}r.Run()
}通常我们将路由分组用在划分业务逻辑或划分API版本时。