静态文件服务
当我们渲染的 HTML 文件中引用了静态文件时,像这样,
我们需要配置静态 web 服务
r.Static("/static", "./static")
配置静态web目录,第一个参数"/static"表示路由,第二个参数"./static"表示映射的目录
在main文件中配置
func main() {
r := gin.Default()
//自定义模板函数 注意要把这个函数放在加载模板前
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
//配置模板目录,加载模板
r.LoadHTMLGlob("templates/**/*") // /**表示一个目录
//配置静态web目录,第一个参数表示路由,第二个参数表示映射的目录
r.Static("/static", "./static")
...
r.Run(":8080")
}
在html文件中引入静态文件
<link rel="stylesheet" href="static/css/base.css">
<img src="/static/images/1.jpg" alt="">
路由详解
路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问。
1、GET POST 以及获取 Get Post 传值
1.1、GET请求传值
在输入地址栏中自行输入参数,如下图所示:
localhost:8080/?username=zhangsan&age=12
//GET请求传值
r.GET("/", func(c *gin.Context) {
username := c.Query("username")
age := c.Query("age")
page := c.DefaultQuery("page", "1")
c.JSON(http.StatusOK, gin.H{
"username": username,
"age": age,
"page": page,
})
})
1.2、POST请求传值 获取form表单数据
定义一个提交表单的页面,如下:
{{ define "default/user.html"}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/doAddUser" method="post">
用户名:<input type="text" name="username" /><br>
密码:<input type="password" name="password"/> <br>
<input type="submit" value="提交">
</form>
</body>
</html>
{{end}}
通过 c.PostForm 接收表单传过来的数据
//post演示
r.GET("/user", func(c *gin.Context) {
c.HTML(http.StatusOK, "default/user.html", gin.H{})
})
//获取表单post过来的数据
r.POST("/doAddUser", func(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
age := c.DefaultPostForm("age", "20")
c.JSON(http.StatusOK, gin.H{
"username": username,
"age": age,
"page": page,
})
})
1.3、获取 GET POST 传递的数据绑定到结构体
为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的 Content-Type识别请求数据类型并利用反射机制自动提取请求中 QueryString、form 表单、JSON、XML 等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提 取 JSON、form 表单和 QueryString 类型的数据,并把值绑定到指定的结构体对象。
定义结构体
//注意首字母大写
type Userinfo struct {
Username string `form:"username" json:"user"`
Password string `form:"password" json:"password"`
}
Get 传值绑定到结构体
/?username=zhangsan&password=123456
//获取GET POST 传递的数据绑定到结构体
r.GET("/getUser", func(c *gin.Context) {
user := &UserInfo{}
if err := c.ShouldBind(&user); err == nil {
c.JSON(http.StatusOK, user)
} else {
c.JSON(http.StatusOK, gin.H{
"err": err.Error(),
})
}
})
返回数据 {"user":"zhangsan","password":"123456"}
Post 传值绑定到结构体
r.POST("/doAddUser2", func(c *gin.Context) {
user := &UserInfo{}
if err := c.ShouldBind(&user); err == nil {
c.JSON(http.StatusOK, user)
} else {
c.JSON(http.StatusOK, gin.H{
"err": err.Error(),
})
}
})
返回数据 {"user":"zhangsan","password":"123456"}
1.4、获取 Post Xml 数据
在 API 的开发中,我们经常会用到 JSON 或 XML 来作为数据交互的格式,这个时候我们可以在 gin 中使用c.GetRawData()获取数据
r.POST("/xml", func(c *gin.Context) {
article := &Article{}
xmlSliceData, _ := c.GetRawData() //获取c.Request.Body读取数据请求
if err := xml.Unmarshal(xmlSliceData, &article); err == nil {
c.JSON(http.StatusOK, article)
} else {
c.JSON(http.StatusBadRequest, gin.H{
"err": err.Error(),
})
}
})
2、路由分组
当项目比较大时,将所有路由全部配置到main.go中显然是不合理的,这时我们就需要将路由分组抽离出来。
defaultRouters := r.Group("/")
{
defaultRouters.GET("/", func(c *gin.Context) {
c.String(200, "首页")
})
defaultRouters.GET("/news", func(c *gin.Context) {
c.String(200, "新闻")
})
}
apiRouters := r.Group("/api")
{
apiRouters.GET("/userlist", func(c *gin.Context) {
c.String(200, "我是一个api接口")
})
apiRouters.GET("/plist", func(c *gin.Context) {
c.String(200, "我是一个api接口")
})
}
adminRouters := r.Group("/admin")
{
adminRouters.GET("/", func(c *gin.Context) {
c.String(200, "后台首页")
})
adminRouters.GET("/user", func(c *gin.Context) {
c.String(200, "用户列表")
})
adminRouters.GET("/user/add", func(c *gin.Context) {
c.String(200, "增加用户")
})
adminRouters.GET("/user/edit", func(c *gin.Context) {
c.String(200, "修改用户")
})
}
前台分组 defaultRouters := r.Group("/")
后台分组 adminRouters := r.Group("/admin")
api接口分组 apiRouters := r.Group("/api")
分组抽离
新建 routes 文件夹,routes 文件下面新建adminRoutes.go、apiRoutes.go、defaultRoutes.go
1、新建adminRouters.go
package routers
import "github.com/gin-gonic/gin"
func AdminRoutersInit(r *gin.Engine) {
adminRouters := r.Group("/admin")
{
adminRouters.GET("/", func(c *gin.Context) {
c.String(200, "后台首页")
})
adminRouters.GET("/user", func(c *gin.Context) {
c.String(200, "用户列表")
})
adminRouters.GET("/user/add", func(c *gin.Context) {
c.String(200, "增加用户")
})
adminRouters.GET("/user/edit", func(c *gin.Context) {
c.String(200, "修改用户")
})
}
}
2、新建defaultRouters.go
package routers
import "github.com/gin-gonic/gin"
func DefaultRoutersInit(r *gin.Engine) {
defaultRouters := r.Group("/api")
{
defaultRouters.GET("/", func(c *gin.Context) {
c.String(200, "首页")
})
defaultRouters.GET("/news", func(c *gin.Context) {
c.String(200, "新闻")
})
}
}
3、新建apiRouters.go
package routers
import "github.com/gin-gonic/gin"
func ApiRoutersInit(r *gin.Engine) {
apiRouters := r.Group("/api")
{
apiRouters.GET("/userlist", func(c *gin.Context) {
c.String(200, "我是一个api接口")
})
apiRouters.GET("/plist", func(c *gin.Context) {
c.String(200, "我是一个api接口")
})
}
}
4、配置main.go
package main
import (
"fmt"
"gindemo04/routers"
"text/template"
"time"
"github.com/gin-gonic/gin"
)
type UserInfo struct {
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
}
func main() {
//创建一个默认的路由引擎
r := gin.Default()
//自定义模板函数 注意要把这个函数放在加载模板前
r.SetFuncMap(template.FuncMap{
"UnixToTime": UnixToTime,
})
//配置模板目录,加载模板
r.LoadHTMLGlob("templates/**/*") // /**表示一个目录
//配置静态web目录,第一个参数表示路由,第二个参数表示映射的目录
r.Static("/static", "./static")
routers.AdminRoutersInit(r)
routers.ApiRoutersInit(r)
routers.DefaultRoutersInit(r)
r.Run(":8080")
}
此时分组路由就建立完成啦,访问试试吧!
gin中自定义控制器
控制器分组
当我们的项目比较大的时候有必要对我们的控制器进行分组
新建 controller/admin/userController.go
package admain
import "github.com/gin-gonic/gin"
func UserIndex(c *gin.Context) {
c.String(200, "用户列表--")
}
func UserAdd(c *gin.Context) {
c.String(200, "用户列表-add")
}
在adminRouters.go中引入controller/admin,配置对应路由
package routers
import (
"gindemo05/controllers/admin"
"github.com/gin-gonic/gin"
)
func AdminRoutersInit(r *gin.Engine) {
adminRouters := r.Group("/admin")
{
adminRouters.GET("/", func(c *gin.Context) {
c.String(200, "后台首页")
})
adminRouters.GET("/user", admin.UserIndex)
adminRouters.GET("/user/add", admin.UserAdd)
adminRouters.GET("/user/edit", func(c *gin.Context) {
c.String(200, "修改用户")
})
}
}
Gin中间件
Gin 框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
通俗的讲:中间件就是匹配路由前和匹配路由完成后执行的一系列操作。
路由中间件
1、初识中间件
Gin 中的中间件必须是一个 gin.HandlerFunc 类型,配置路由的时候可以传递多个 func 回调函数,最后一个 func 回调函数前面触发的方法都可以称为中间件。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func initMiddleware(ctx *gin.Context) {
fmt.Println("我是一个中间件")
}
func main() {
r := gin.Default()
r.GET("/", initMiddleware, func(ctx *gin.Context) {
ctx.String(200, "首页--中间件演示")
})
r.GET("/news", initMiddleware, func(ctx *gin.Context) {
ctx.String(200, "新闻页面--中间件演示")
})
r.Run(":8080")
}
2、ctx.Next()调用该请求的剩余处理程序
中间件里面加上 ctx.Next()可以让我们在路由匹配完成后执行一些操作。比如我们统计一个请求的执行时间。
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
func initMiddleware(ctx *gin.Context) {
fmt.Println("1-执行中中间件")
start := time.Now().UnixNano()
// 调用该请求的剩余处理程序
ctx.Next()
fmt.Println("3-程序执行完成 计算时间")
// 计算耗时 Go 语言中的 Since()函数保留时间值,并用于评估与实际时间的差异
end := time.Now().UnixNano()
fmt.Println(end - start)
}
func main() {
r := gin.Default()
r.GET("/", initMiddleware, func(ctx *gin.Context) {
fmt.Println("2-执行首页返回数据")
ctx.String(200, "首页--中间件演示")
})
r.GET("/news", initMiddleware, func(ctx *gin.Context) {
ctx.String(200, "新闻页面--中间件演示")
})
r.Run(":8080")
}
3、一个路由配置多个中间件的执行顺序
func initMiddlewareOne(ctx *gin.Context) {
fmt.Println("initMiddlewareOne--1-执行中中间件")
// 调用该请求的剩余处理程序
ctx.Next()
fmt.Println("initMiddlewareOne--2-执行中中间件")
}
func initMiddlewareTwo(ctx *gin.Context) {
fmt.Println("initMiddlewareTwo--1-执行中中间件")
// 调用该请求的剩余处理程序
ctx.Next()
fmt.Println("initMiddlewareTwo--2-执行中中间件")
}
func main() {
r := gin.Default()
r.GET("/", initMiddlewareOne, initMiddlewareTwo, func(ctx *gin.Context) {
fmt.Println("执行路由里面的程序")
ctx.String(200, "首页--中间件演示")
})
r.Run(":8080")
}
控制台内容:
initMiddlewareOne--1-执行中中间件
initMiddlewareTwo--1-执行中中间件
执行路由里面的程序
initMiddlewareTwo--2-执行中中间件
initMiddlewareOne--2-执行中中间件
4、 c.Abort()--(了解)
Abort 是终止的意思, c.Abort() 表示终止调用该请求的剩余处理程序。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func initMiddlewareOne(ctx *gin.Context) {
fmt.Println("initMiddlewareOne--1-执行中中间件")
// 调用该请求的剩余处理程序
ctx.Next()
fmt.Println("initMiddlewareOne--2-执行中中间件")
}
func initMiddlewareTwo(ctx *gin.Context) {
fmt.Println("initMiddlewareTwo--1-执行中中间件")
// 终止调用该请求的剩余处理程序
ctx.Abort()
fmt.Println("initMiddlewareTwo--2-执行中中间件")
}
func main() {
r := gin.Default()
r.GET("/", initMiddlewareOne, initMiddlewareTwo, func(ctx *gin.Context) {
fmt.Println("执行路由里面的程序")
ctx.String(200, "首页--中间件演示")
})
r.Run(":8080")
}
initMiddlewareOne--1-执行中间件
initMiddlewareTwo--1-执行中间件
initMiddlewareTwo--2-执行中间件
initMiddlewareOne--2-执行中间件