Gin是一个golang的微框架,基于httprouter,封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点
速度快:基于基数树的路由,内存占用小,没有反射,可预测的APi性能
传入的http请求可以有中间件链和最终操作处理,例如:Logger,Authorization,GZip最后在Db中发布以条消息
Gin可以捕获Http请求期间发生的panic并恢复它,这样你的服务器始终都可用
Gin可以解析和验证请求的JSON——例如,检查所需的值是否存在
更好的组织您的路线,需要授权与不需要授权,不同的API版本,此外,组可以无限嵌套,而不会降低性能
Gin提供了一种方便的方法用来收集HTTP请求期间发生的所有错误,最终 ,中间件可以将它们写入日志,数据并通过网络发送他们
GIn为JSon,XML,HTML渲染提供了简单易用·的API
创建一个新的中间件非常简单,只需要查看实例代码就可以
安装Gin
go get -u github.com/gin-gonic/gin
导入项目
import "github.com/gin-gonic/gin"
实现代码
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
实现步骤
在项目根目录下边创建文件夹templtes,用来保存静态文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
<form action="/login" method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
</body>
</html>
创建一个欢迎html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome</title>
</head>
<body>
Welcome, {{.username}}
</body>
</html>
package main
import "github.com/gin-gonic/gin"
func MyHandler(c *gin.Context) {
c.JSON(200, gin.H{
"hello": "hello world",
})
}
func Login(c *gin.Context) {
c.HTML(200, "login.html", nil)
}
func DoLogin(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
c.HTML(200, "welcome.html", gin.H{
"username": username,
"password": password,
})
}
func main() {
e := gin.Default()
e.LoadHTMLGlob("templates/*")
e.GET("/login", Login)
e.POST("/login", DoLogin)
e.Run()
}
Get请求参数
使用c.Query(“key”),或者c.DefaultQuery(”key”)方法
package main
import "github.com/gin-gonic/gin"
func TestQueryString(c *gin.Context) {
username := c.Query("username")
site := c.DefaultQuery("site", "www.duoke360.com")
c.String(200, "username:%s, site:%s", username, site)
}
func main() {
e := gin.Default()
// url : http://localhost:8080/testQueryString?username=郭宏志&site=多课网
e.GET("/testQueryString", TestQueryString)
e.Run()
}
package main
import (
"github.com/gin-gonic/gin"
)
type User struct {
Username string `form:"username"`
Password string `form:"password"`
Hobby []string `form:"hobby"`
Gender string `form:"gender"`
City string `form:"city"`
}
func Regsiter(c *gin.Context) {
var user User
c.ShouldBind(&user)
c.String(200, "User:%s", user)
}
func GoRegister(c *gin.Context) {
c.HTML(200, "register.html", nil)
}
func main() {
e := gin.Default()
e.LoadHTMLGlob("templates/*")
e.POST("/register", Regsiter)
e.GET("/register", GoRegister)
e.Run()
}
package main
import (
"log"
"github.com/gin-gonic/gin"
)
type User struct {
Username string `form:"username"`
Password string `form:"password"`
}
func TestGetBind(c *gin.Context) {
var user User
err := c.ShouldBind(&user)
if err != nil {
log.Fatal(err)
}
c.String(200, "User:%s", user)
}
func main() {
e := gin.Default()
// http://localhost:8080/testGetBind?username=ghz&password=123
e.GET("/testGetBind", TestGetBind)
e.Run()
}
package main
import (
"log"
"github.com/gin-gonic/gin"
)
type User struct {
Username string `uri:"username"`
Password string `uri:"password"`
}
func TestGetBind(c *gin.Context) {
var user User
err := c.ShouldBindUri(&user)
if err != nil {
log.Fatal(err)
}
c.String(200, "User:%s", user)
}
func main() {
e := gin.Default()
// http://localhost:8080/testGetBind/ghz/123
e.GET("/testGetBind/:username/:password", TestGetBind)
e.Run()
}
下载bootstrap
下载地址:
https://getbootstrap.com/
创建一个assets文件夹 将 css和js文件添加到该文件夹中
创建html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/assets/css/bootstrap.min.css">
<title>Login</title>
</head>
<body>
<div class="container">
<form>
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="exampleCheck1">
<label class="form-check-label" for="exampleCheck1">Check me out</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</body>
</html>
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func Login(c *gin.Context) {
c.HTML(200, "login.html", nil)
}
func main() {
e := gin.Default()
e.LoadHTMLGlob("templates/*")
e.Static("/assets", "./assets")
e.StaticFS("/croot", http.Dir("c:/"))
e.StaticFile("/favicon.ico", "./assets/favicon.ico")
e.GET("/login", Login)
e.POST("/login", DoLogin)
e.Run()
}
中间件听起来非常高大上的名字,实际上非常简单,就是在请求中间其拦截作用的处理函数
如果你使用Gin.Default实例化gin引擎,默认有俩个中间件,LOgger和Recover,分别用来处理日志和处理错误,如果使用Gin.New需要重新添加
// 新建一个没有任何默认中间件的路由
r := gin.New()
// 全局中间件
// Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())
// Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500。
r.Use(gin.Recovery())
1.自定义中间件非常简单,定义一个符合下边格式的处理函数
type HandlerFunc func(*Context)
2.使用Use方法调用
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func TestMW(c *gin.Context) {
c.String(200, "hello,%s", "ghz")
}
func MyMiddleware1(c *gin.Context) {
fmt.Println("我的第一个中间件")
}
func MyMiddleware2(c *gin.Context) {
fmt.Println("我的第二个中间件")
}
func main() {
/* func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
} */
// e := gin.Default()
// e := gin.New()
e := gin.Default()
e.Use(MyMiddleware1, MyMiddleware2)
e.GET("testmw", TestMW)
e.Run()
}
Gin提供了BasicAuth的中间件,用来对网络资源的访问保护
实例:
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
// 模拟一些私人数据
var secrets = gin.H{
"foo": gin.H{"email": "[email protected]", "phone": "123433"},
"austin": gin.H{"email": "[email protected]", "phone": "666"},
"lena": gin.H{"email": "[email protected]", "phone": "523443"},
}
func main() {
r := gin.Default()
// 路由组使用 gin.BasicAuth() 中间件
// gin.Accounts 是 map[string]string 的一种快捷方式
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
"foo": "bar",
"austin": "1234",
"lena": "hello2",
"manu": "4321",
}))
// /admin/secrets 端点
// 触发 "localhost:8080/admin/secrets
authorized.GET("/secrets", func(c *gin.Context) {
// 获取用户,它是由 BasicAuth 中间件设置的
user := c.MustGet(gin.AuthUserKey).(string)
fmt.Println(user)
if secret, ok := secrets[user]; ok {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
} else {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
}
})
// 监听并在 0.0.0.0:8080 上启动服务
r.Run(":8080")
}
在浏览器中输入localhost:8080/admin/secrets时,会弹出一个对话框,要求输入正确的用户名和密码,才能访问资源。
cookie是服务器向客户端写的一些数据,可以实现像自动登陆等功能
Gin cookie的使用
package main
import "github.com/gin-gonic/gin"
func Handler(c *gin.Context) {
// 获得cookie
s, err := c.Cookie("username")
if err != nil {
s = "ghz"
// 设置cookie
c.SetCookie("username", s, 60*60, "/", "localhost", false, true)
}
c.String(200, "测试cookie")
}
func main() {
e := gin.Default()
e.GET("/test", Handler)
e.Run()
}
因为http是无状态短连接 ,如何保存客户端和服务器直接的会话状态呢?可以使用session
gin本身没有对session的支持,可以使用第三方中间件
go get github.com/gin-contrib/sessions
import "github.com/gin-contrib/sessions"
该中间件提更了很多后端支持:
cookie-based
redis
memcached
MongoDB
memstore
PostgreSQL
实例:
package main
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.GET("/hello", func(c *gin.Context) {
session := sessions.Default(c)
if session.Get("hello") != "world" {
session.Set("hello", "world")
session.Save()
}
c.JSON(200, gin.H{"hello": session.Get("hello")})
})
r.Run(":8000")
}
package main
import (
"fmt"
"strconv"
"github.com/gin-gonic/gin"
)
type User struct {
UId int `json:"uid"`
Name string `json:"name"`
Age int `json:"age"`
}
var users = make([]User, 3)
func init() {
u1 := User{1, "tom", 20}
u2 := User{2, "kite", 30}
u3 := User{3, "rose", 40}
users = append(users, u1)
users = append(users, u2)
users = append(users, u3)
fmt.Println(users)
}
func find(uid int) (*User, int) {
for i, u := range users {
if u.UId == uid {
return &u, i
}
}
return nil, -1
}
func AddUser(c *gin.Context) {
u4 := User{4, "Joe", 50}
users = append(users, u4)
c.JSON(200, users)
}
func DelUser(c *gin.Context) {
uid := c.Param("uid")
id, _ := strconv.Atoi(uid)
_, i := find(id)
users = append(users[:i], users[i+1:]...)
c.JSON(200, users)
}
func UpdateUser(c *gin.Context) {
uid := c.Param("uid")
id, _ := strconv.Atoi(uid)
u, _ := find(id)
u.Name = "修改的Name"
c.JSON(200, u)
}
func FindUser(c *gin.Context) {
uid := c.Param("uid")
id, _ := strconv.Atoi(uid)
u, _ := find(id)
c.JSON(200, u)
}
func main() {
e := gin.Default()
e.GET("/user/:uid", FindUser)
e.PUT("/user/:uid", UpdateUser)
e.DELETE("/user/:uid", DelUser)
e.POST("/user/", AddUser)
e.Run()
}
假如你的网站上有很多个模块:博客,教程,视频,回答,每个模块又有很多个路由,这样就可以进行路由分组,使用的方法是router.Group(“分组名称”)
package main
import "github.com/gin-gonic/gin"
func F1(c *gin.Context) {}
func F2(c *gin.Context) {}
func F3(c *gin.Context) {}
func F4(c *gin.Context) {}
func F5(c *gin.Context) {}
func F6(c *gin.Context) {}
func main() {
router := gin.Default()
// 博客
// 访问:http://localhost:8080/blog/list
v1 := router.Group("/blog")
{
v1.POST("/list", F1)
v1.POST("/post", F2)
v1.POST("/add", F3)
}
// 视频
// 访问:http://localhost:8080/video/list
v2 := router.Group("/video")
{
v2.POST("/list", F4)
v2.POST("/post", F5)
v2.POST("/add", F6)
}
router.Run(":8080")
}
Gin支持很多种输出渲染,可以是简单的字符串,Json,xml,html,protoBuf。使用的方法如下:
c.JSON(200, nil)
c.XML(200, nil)
c.HTML(200, "", nil)
c.String(200, "")
c.ProtoBuf(200, nil)
这里像字符串,json,html我们都用过,这里我们再总结一下:
package main
import "github.com/gin-gonic/gin"
func TestJson(c *gin.Context) {
c.JSON(200, gin.H{
"name": "多课网",
"site": "www.duoke360.com",
})
}
func TestXML(c *gin.Context) {
c.XML(200, gin.H{
"name": "多课网",
"site": "www.duoke360.com",
})
}
func TestHtml(c *gin.Context) {
c.HTML(200, "login.html", nil)
}
func TestString(c *gin.Context) {
c.String(200, "多课网,老郭讲golang")
}
func main() {
e := gin.Default()
e.GET("/test_json", TestJson)
e.GET("/test_xml", TestXML)
e.LoadHTMLGlob("templates/*")
e.GET("/test_html", TestHtml)
e.GET("/test_string", TestString)
> 这里是引用
e.Run()
}
创建xml文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
请选择上传文件:<input type="file" name="file" id=""><br>
<input type="submit" value="上传">
</form>
</body>
</html>
package main
import (
"fmt"
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func Upload(c *gin.Context) {
// 单文件
file, _ := c.FormFile("file")
log.Println(file.Filename)
// 上传文件到项目根目录,使用原文件名
c.SaveUploadedFile(file, file.Filename)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
}
func GoUpload(c *gin.Context) {
c.HTML(200, "upload.html", nil)
}
func main() {
router := gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.LoadHTMLGlob("templates/*")
router.GET("/upload", GoUpload)
router.POST("/upload", Upload)
router.Run(":8080")
}