Gin是一个用Go语言编写的web框架。它是一个类似于martini但拥有更好性能的 API框架, 由于使用了httprouter,速度提高了近40倍。具有封装比较优雅,API友好,源码注释比较明确,具有快速灵活,容错方便等特点.
Gin特征:
1.速度快:基于基数树的路由,内存占用少。没有反射。可预测的API性能。
中间件支持:传入的HTTP请求可以由中间件链和最终操作处理。
2。Crash—free
Gin可以捕获HTTP请求期间发生的panic并恢复它。这样,你的服务器将始终可用。
3.JSON验证
Gin可以解析和验证请求的JSON-例如,检查所需值的存在
4.路由分组
更好地组织您的路线。需要授权与不需要授权,不同的API版本…此外,组可以无限嵌套,而不会奖低性能。
错误管理
5.Gin提供了一种方便的方法来收集HTTP请求期间发生的所有错误。最终,中间件可以将它们写入日志文件、数据库并通过网络发送它们。
6.内置渲染
Gin为JSON、XML和HTML渲染提供了一个易于使用的API
7.可扩展
创建一个新的中间件非常简单,只需查看示例代码即可
gin的安装
go get -u github.com/gin-gonic/gin
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func Hello(c *gin.Context) {
c.String(200, "hello %s", "world")
c.JSON(http.StatusOK, gin.H{ //以json格式输出
"name": "tom",
"age": "20",
}) //200代表请求成功,http.StatusOK代表请求成功,gin.H是map的一个快捷方式
}
func main() {
e := gin.Default() //创建一个默认的路由引擎
e.GET("/hello", Hello)
e.Run()
}
简单的用户登录实现:
package main
//gin实现简单的登录程序
import "github.com/gin-gonic/gin"
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("views/*") //获取当前路径下的views文件夹下的所以html文件
e.GET("/login", Login)
e.POST("/login", DoLogin) //post请求交给DoLogin处理
e.Run()
}
welcome.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
head>
<body>
欢迎,{{.Username}}
{{.Password}}
body>
html>
login.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>Logintitle>
head>
<body>
<form action="/login" method="post">
用户名称:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="Login">
form>
body>
html>
//获取param参数
//router.Get("/:id",Param1)
func Param1(ctx *gin.Context){
id:=ctx.Param("id")
}
//router.Get("/*id",Param2)
func Param2(ctx *gin.Context){
id:=ctx.Param("id")
}
//这种情况下,连id前面的/也会获取到,而且id也可以为空
//获取query参数
//router.Get("/",Query1)
func Query1(ctx *gin.Context){
id:=ctx.Query("id")
id2:=ctx.DefaultQuery("id","0")//获取不到id时默认为0
ids:=ctx.QueryArray("ids")//获取ids的多个参数
idm:=ctx.QueryMap("idm")//获取map类型的参数idm
}
//获取post参数
//router.Post("/",Post1)
func Post1(ctx *gin.context){
id:=ctx.PostForm("id")
id2:=ctx.DefaultPostForm("id","10")//id获取不到的话默认为10
ids:=ctx.PostFormArray("ids")
idm:=ctx.PostFormMap("idm")
}
//获取提交的单文件
func GetFile(ctx *gin.context){
file,_:=ctx.FormFile("file")
fmt.Println(file.Filename)
dst:=path+file.Filename
ctx.SaveUploadedFile(file,dst)//文件保存
}
//获取提交的多文件
func GetMulFile(ctx *gin.context){
file,_:=ctx.MultipartForm()
files:=form.File["file"]
for _,file:=range files{
dst:=path+file.Filename
ctx.SavaUploadFile(file,dst)
}
}
package main
import "github.com/gin-gonic/gin"
func TestGet(c *gin.Context) {
s := c.Query("username")
pwd := c.DefaultQuery("password", "123") //如果没有输入密码给它一个默认值“123”
c.String(200, "username:%s password:%s", s, pwd)
//http://localhost:8888/testget?username=zyj&password=1234
}
func TestGetParam(c *gin.Context) {
s := c.Param("username")
s2 := c.Param("age")
c.String(200, "Username:%s", s)
c.String(200, "Age:%s", s2)
//http://localhost:8888/hello/zyj/22
}
func GoSearch(c *gin.Context) {
c.HTML(200, "query.html", nil)
}
func Search(c *gin.Context) {
page := c.DefaultQuery("page", "0")
key := c.PostForm("key")
c.String(200, "page:%s,key:%s", page, key)
}
func main() {
e := gin.Default()
e.LoadHTMLGlob("views/*")
e.GET("/testget", TestGet)
e.GET("/hello/:username/:age", TestGetParam)
e.GET("/goSearch", GoSearch)
e.POST("/search", Search)
e.Run(":8888")
}
//post的运用
query.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
head>
<body>
<form action="/search?page=1" method="post">
请输入查询关键字:<input type="text" name="key"><br>
<input type="submit" value="查询">
form>
body>
html>
1.ShouldBind() 根据标记进行数据绑定
2.ShouldBindWith() 接受两个参数,第二个参数来指名绑定参数类型,例:表单类型binding.Form
3.ShouldBindQuery()对query参数的绑定
4.ShouldBindUri()对param参数进行绑定
package main
import (
"fmt"
"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"`
}
type User2 struct {
Username string `uri:"username"`
Password string `uri:"password""`
}
func Register(c *gin.Context) {
var user User
err := c.ShouldBind(&user) //form绑定数据
if err != nil {
fmt.Println("绑定失败")
}
c.String(200, "User:%s", user)
//http://localhost:8080/testGetBind?username=zyj&password=123
//这种连接也会将其进行绑定
}
func GoRegister(c *gin.Context) {
c.HTML(200, "login.html", nil)
}
func TestGetBind(c *gin.Context) {
var user User2
err := c.ShouldBind(&user) //form绑定数据
if err != nil {
fmt.Println("绑定失败")
}
c.String(200, "User:%s", user)
//http://localhost:8080/testGetBind?username=zyj&password=123
//通过uri绑定
}
func main() {
e := gin.Default()
e.LoadHTMLGlob("views/*")
e.GET("/testFrom", GoRegister)
e.POST("/register", Register)
e.GET("/testGetBind", TestGetBind)
e.Run()
}
login.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>Logintitle>
head>
<body>
<form action="/register" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:
<input type="checkbox" name="hobby" value="swimming">游泳
<input type="checkbox" name="hobby" value="basketball">篮球
<br>
性别:<input type="radio" name="gender" id="1" value="m">男
<input type="radio" name="gender" id="2" value="f">女
<br>
城市:<select name="city">
<option value="beijing">北京option>
<option value="shanghai">上海option>
<br>
select>
<input type="submit" value="注册">
form>
body>
html>
使用go-playground/validator.v8进行验证
type Article struct{
Id int `form:"id"`
Title string `form:"title" binding:"required"`
}
自定义验证器:
安装包:
go get github.com/go-playgroud/validator
func Len6Valid validator.Func=func(f1 validator.FieldLevel) bool{
data:=f1.Field().Interface().(string)
if len(data)>6{
return false
}else{
return true
}
}
func main(){
v,ok:=binding.Validator.Engine().(*validator.Validate)
if ok{
v.RegisterValidation("len_valid",Len6Valid) //第一个参数是绑定的tag标记,第二个是绑定的函数
validation.SetDefaultMessage(map[string]string{}) //设置验证不通过返回的信息
}
}
boostrap下载网站
下载完成后将其中的js和css放入到assets中
简单的集成实例
package main
import "github.com/gin-gonic/gin"
func GoStatic(e *gin.Context) {
e.HTML(200, "test_static.html", nil)
}
func main() {
e := gin.Default()
e.Static("/assets", "./assets") //第一个是访问的路径,第二个路径是css、js存放的路径
e.LoadHTMLGlob("views/*")
e.GET("/gostatic", GoStatic)
e.Run()
}
test_static.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
<link rel="stylesheet" href="/gin/asset/css/bootstrap.min.css">
<script src="/gin/asset/js/bootstrap.min.js">script>
head>
<body>
<div class="container">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="button" class="btn btn-primary">Leftbutton>
<button type="button" class="btn btn-primary">Middlebutton>
<button type="button" class="btn btn-primary">Rightbutton>
div>
div>
body>
html>
gin的内置中间件
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
func Testmw(c *gin.Context) {
c.String(200, "hello %s", "world!")
}
func MyMiddeleware1(c *gin.Context){
fmt.Println("我的第一个中间件")
}
func MyMiddeleware2(c *gin.Context){
fmt.Println("我的第二个中间件")
}
func main() {
e := gin.Default()
e.Use(MyMiddeleware1, MyMiddeleware2)
e.GET("/testmw", Testmw)
e.Run()
}
// 局部使用中间价
chap05.GET("/basic",gin.BasicAuth(gin.Accounts{
"zs":"123456",
"ls":"123",
"ww":"1234",
}),BasicAuthTest)
// 私有数据
var map_data map[string]interface{} = map[string]interface{}{
"zs":gin.H{"age":18,"addr":"zs-xx"},
"ls":gin.H{"age":19,"addr":"ls-xx"},
"ww":gin.H{"age":20,"addr":"ww-xx"},
}
// 获取私有数据。如果没有权限则获取不到
func BasicAuthTest(ctx *gin.Context) {
user_name := ctx.Query("user_name")
data ,ok := map_data[user_name]
if ok{
ctx.JSON(http.StatusOK,gin.H{"user_name":user_name,"data":data})
}else {
ctx.JSON(http.StatusOK,gin.H{"user_name":user_name,"data":"没有权限"})
}
}
一文读懂HTTP Basic身份认证:链接
使用日志文件:
// 1.创建日志文件
f, _ := os.Create("gin.log")
// 2.重新赋值DefaultWriter
gin.DefaultWriter = io.MultiWriter(f)
// 同时在控制台打印信息
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
logrus中间件
func LoggerFile(c *gin.Context) {
file_dir := "./gin.log"
src, err := os.OpenFile(file_dir, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
panic(err)
}
logger := logrus.New()
logger.Out = src
logger.SetLevel(logrus.DebugLevel)
logger.SetFormatter(&logrus.TextFormatter{TimestampFormat: "2006-01-02 15:04:05"})
startTime := time.Now()
c.Next()
long := time.Since(startTime)
reqMothod := c.Request.Method
reqUri := c.Request.RequestURI
statusCode := c.Writer.Status()
clientIp := c.ClientIP()
logger.Infof("|%3s|%13v|%15s|%s|%s|",
statusCode,
long,
clientIp,
reqMothod,
reqUri,
)
}
Session的使用:
func SessionTest(ctx *gin.Context) {
// 初始化session对象
session := sessions.Default(ctx)
// 设置session
session.Set("name","hallen")
session.Set("age",18)
session.Set("addr","xxx")
// 获取session
name := session.Get("name")
fmt.Println("================++++++++++")
fmt.Println(name)
// 删除指定key的session
session.Delete("name")
name2 := session.Get("name")
fmt.Println("================++++++++++")
fmt.Println(name2)
fmt.Println(session.Get("age"))
// 删除所有的session
session.Clear()
fmt.Println(session.Get("age"))
fmt.Println(session.Get("addr"))
// 保存session
session.Save()
}
基于redis的存储引擎
安装:
go get github.com/gin-contrib/sessions/redis
使用:
store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("hallen"))
router.Use(sessions.Sessions("session_test",store))
参数说明:
第1个参数 - redis最大的空闲连接数
第2个参数 - 数通信协议tcp或者udp
第3个参数 - redis地址, 格式,host:port
第4个参数 - redis密码,如果没有密码则为空字符串
第5个参数 - session加密密钥
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
//模拟一些私人数据
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 Handler(c *gin.Context) {
//获取用户,它是由BasicAuth中间件设置
user := c.MustGet(gin.AuthUserKey).(string) //获得当前用户,AuthUserKey就是user快捷键
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:("})
}
}
func main() {
e := gin.Default()
//这些用户数据也可以从数据库查询
//路由使用gin.BasicAuth()中间件
//gin.Accounts是map[string]string的一种快捷方式
e1 := e.Group("/admin", gin.BasicAuth(
gin.Accounts{
"foo": "bar",
"austin": "1234",
"lena": "hello2",
"manu": "4321",
})) //授权登入/admin的用户由以上那些
//http://localhost:8080/admin/secrets
e1.GET("/secrets", Handler)
e.Run()
}
package main
import "github.com/gin-gonic/gin"
func Handler(c *gin.Context) {
s, err := c.Cookie("username") //取username的cookie值
if err != nil {
s = "zyj"
c.SetCookie("username", s, 60*60, "/", "localhost", false, true)
//参数分别为name,value,时间,路径,域,https是否可以访问,是否是只有http请求能够访问
}
c.String(200, "测试cookie")
}
func main() {
e := gin.Default()
e.GET("test_cookie", Handler)
e.Run()
}
session库的下载
go get github.com/gin-contrib/sessions
session库的使用
package main
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
func Handle(c *gin.Context) {
session := sessions.Default(c)
//获得session值 Get
if session.Get("hello") != "world" {
//设置
session.Set("hello", "world")
//保存
session.Save()
}
c.JSON(200, gin.H{"hello": session.Get("hello")})
}
func main() {
r := gin.Default()
store := cookie.NewStore([]byte("secret")) //创建一个存储cookie的存储器
r.Use(sessions.Sessions("mysession", store)) //注入中间件,mysession是session名称
r.GET("/hello", Handle)
r.Run(":8000")
}
REST与技术无关,代表的是一种软件架构风格。简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作:
1.GET用来获取资源
2.POST用来新建资源
3.PUT用来更新资源
4.DELETE用来删除资源。
只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"strconv"
)
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, u2, 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) //GET获取请求
e.PUT("/user/:uid", UpdateUser) //PUT修改请求
e.DELETE("/user/:uid", DelUser) //DELETE删除请求
e.POST("/user/", AddUser) //POST添加请求
e.Run()
}
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")
}
LoadHTMLGlob:通过通配符来进行模板文件夹指定,例:router.LoadHTMLGlob(“template/"),二级目录时:router.LoadHTMLGlob("template/**/”)
LoadHTMLFiles:指定html文件,例:router.LoadHTMLFiles(“index.html”,“news.html”)
HTML:接受三个参数,第一个是满足条件的状态码,第二个是html文件的名称,第三个是给模板传递的参数
Static:第一个参数指定访问路径的url,第二个参数是静态资源的路径
package main
import (
"github.com/gin-gonic/gin"
)
func TestJson(e *gin.Context) {
e.JSON(200, gin.H{
"name": "zyj",
"site": "www.baidu.com",
})
}
func TestXMl(e *gin.Context) {
e.XML(200, gin.H{
"name": "lcc",
"site": "www.sougou.com",
})
}
func TestString(e *gin.Context) {
e.String(200, "zyj")
}
func TestHtml(e *gin.Context) {
e.HTML(200, "login.html", nil)
}
func main() {
e := gin.Default()
e.GET("/test_json", TestJson)
e.GET("/test_xml", TestXMl)
e.LoadHTMLGlob("views/*")
e.GET("/test_html", TestHtml)
e.GET("/test_string", TestString)
e.Run()
}
其他的一些模板玩法参考博客
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"net/http"
)
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' upload!", file.Filename))
}
func GoUpload(c *gin.Context) {
c.HTML(200, "put_file.html", nil)
}
func main() {
e := gin.Default()
e.MaxMultipartMemory = 8 << 20 //设置上传文件的最大内存8MB
e.LoadHTMLGlob("views/*")
e.GET("/upload", GoUpload)
e.POST("/upload", Upload)
e.Run()
}
put_file.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Documenttitle>
head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
请选择上传文件:<input type="file" name="file"><br>
<input type="submit" value="上传">
form>
body>
html>
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func Hello(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com")
}
func main() {
e := gin.Default() //创建一个默认的路由引擎
e.GET("/hello", Hello)
e.GET("/a", func(c *gin.Context) {
c.Request.URL.Path = "/hello" //设置转交地址的URL
e.HandleContext(c) //转交
})
e.Run()
}
s:=&http.Server{
Addr:":8080",
Handler:router,
ReadTimeout:10*time.Second,
WithTimeout:5*time.Second,
}
s.ListenAndServe()