赞
踩
gin是用go语言开发的一个web框架,简单易用,是一个轻量级框架。
1.运行响应非常快
2.快速开发
3.文档齐全
4.社区活跃
1.快速:基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。
2.支持中间件:传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。
3.Crash 处理:Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic!
4.JSON 验证:Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。
5.路由组:更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。
6.错误管理:Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
7.内置渲染:Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。
8.可扩展性:新建一个中间件非常简单
1.go安装
双击exe文件安装即可
1.src:用于以代码包的形式组织并保存go源码文件,
2.pkg:用于存放经由go install命令构建的安装后的代码包,不需要手动创建
3.bin:与pkg目录类似,在通过go install命令完成安装后,保存由go命令源码间生成的可执行文件
go get -u github.com/gin-gonic/gin
package main
import "github.com/gin-gonic/gin"
func main() {
//engine := gin.New()
engine := gin.Default()
engine.GET("/", func(context *gin.Context) {
context.String(200,"hello gin")
})
engine.Run(":9000")
}
初始化一个引擎,是gin.New()的升级
1.RESTFUL风格的请求方法(method)
2.有两个参数:
3.可以使用router.Handle代替,多了个的method参数(字符串),method参数必须是大写的,如:GET
1.必须有个参数是gin.Context指针类型的
注意:context是gin的一个重要组成部分。用来在中间层传递数据流。
2.函数是个参数,不能调用
启动http监听,有个address参数,字符串类型的,可以指定host和port
注意:
go版本>=1.11
go中包管理工具
使用环境变量中的GO111MODULE控制是否使用mod
1.开启mod:go env -w GO111MODULE=on,会将包下载到gopath下的pkg下的mod文件夹中
2.关闭mod:go env -w GO111MODULE=off,会将包下载到gopath下的src下
3.go env GO111MODULE=auto,只有当当前目录在GOPATH/src目录之外而且当前目录包含go.mod文件或者其子目录包含go.mod文件才会启用。
项目可以不用建在src下了,任何非中文路径下都可以,建议有个统一的代码路径
go help go.mod 查看帮助
示例:
module my/thing
go 1.13.4
require (
new/thing v2.3.4
old/thing v1.2.3
)
1.module:指明根目录
2.go 后面跟版本号是指定go的版本
2.require是个动作指令,对依赖包起作用,比如require(依赖),还有exclude(排除),replace(替代),相同动作的可以放在一个动词+括号组成的结构中,如下:
require ( new/thing v2.3.4 old/thing v1.2.3 ) require new/thing v2.3.4 require old/thing v1.2.3 // 排除 exclude old/thing v1.2.3 // 替换,使用箭头后的替换前面的 replace bad/thing v1.4.5 => good/thing v1.4.5
注意:
3.注释:使用//,没有/* xxx */这种块注释
go mod help 查看帮助
download 下载模块到本地缓存,go env中的GOCACHE路径,可以通过go clean -cache清空缓存 多个项目可以共享缓存的包 edit 在工具或脚本中编辑go.mod文件 graph 打印模块需求图 init 在当前目录下初始化新的模块 go mod init 【项目名】 默认使用当前路径的项目名称 tidy 添加缺失的模块以及移除无用的模块,生成go.sum文件 vendor 会自动下载项目中依赖的包到项目根目录下的vendor文件夹下,并写入go.mod文件,同时生成 modules.txt文件 go mod vender -v verify 检查当前模块的依赖是否全部下载下来,是否下载下来被修改过 why 解释为什么需要包或模块 注意:-v参数可以查看执行的详细信息
已经完成的项目可以这样操作来使用mod
项目中可以是这样的执行顺序:
注意:项目中引入该项目下的任何路径都要是绝对路径,也就是以改项目名开头的路径
使用mod的步骤:
1.开启mod:go111module=on
2.进入项目,执行go mod init (在项目根目录生成go.mod文件)
3.启动项目(go.mod添加依赖的包)
只有一个参数,通配符,如:template/* 意思是找当前项目路径下template文件夹下所有的html文件
e.g.:engine.LoadHTMLGlob(“templates/*”)
不定长参数,可以传多个字符串,使用这个方法需要指定所有要使用的html文件路径
e.g.:engine.LoadHTMLFiles(“templates/index.html”,“template/user.html”)
// 使用*gin.Context下的HTML方法
func Hello(context *gin.Context) {
name := "zhiliao"
context.HTML(http.StatusOK,"index.html",name)
}
注意:不要使用goland里面run,否则会报错
panic: html/template: pattern matches no files: `templates/*`
在cmd运行即可
如果有多级目录,比如templates下有user和article两个目录,如果要使用里面的html文件,必须得在Load的时候指定多级才可以,比如:engine.LoadHTMLGlob(“templates/**/*”)
1.有几级目录,得在通配符上指明
两级:engine.LoadHTMLGlob("templates/**/*")
三级:engine.LoadHTMLGlob("templates/**/**/*")
2.指定html文件
// 除了第一级的templates路径不需要指定,后面的路径都要指定
e.g.:context.HTML(http.StatusOK,"user/index.html","zhiliao")
3.在html中
必须使用
{{ define "user/index.html" }}
html内容
{{ end }}
包起来
engine.Static(“/static”, “static”)
第一个参数是url,第二个参数是url对应的文件夹
engine.StaticFS(“/static”, http.Dir(“static”))
<link rel="stylesheet" href="/static/user/index.css">
要指定所有的html路径,不推荐
router := gin.Default()
html := template.Must(template.ParseFiles("test1.html", "test2.html"))
router.SetHTMLTemplate(html)
router.Run(":8080")
后端:
type User struct {
Id int
Name string
}
func Hello(context *gin.Context) {
user := User{Id:1,Name:"hallen"}
context.HTML(http.StatusOK,"user/index.html",user)
}
前端:
{{.Id}}
{{.Name}}
后端: func Hello(context *gin.Context) { arr := [5]int{1,2,3,4,5} context.HTML(http.StatusOK,"user/index.html",arr) } 前端: {{range .}} {{.}} {{end}} 或者: {{range $i,$v := .}} {{$i}} {{$v}} {{end}} 注意:range后面有两个变量,那就是角标和对应的元素值 如果只有一个值,就是数组的元素值
后端 type User struct { Id int Name string } func Hello(context *gin.Context) { arr_struct := [2]User{{Id:1,Name:"hallen1"},{Id:2,Name:"hallen2"}} context.HTML(http.StatusOK,"user/index.html",arr_struct) } 前端: {{range $v := .}} {{$v.Id}} {{$v.Name}} <br> {{end}}
后端:
func Hello(context *gin.Context) {
map_data := map[string]string{
"name":"hallen",
"age":"18",
}
context.HTML(http.StatusOK,"user/index.html",map_data)
}
前端:
{{.name}}
{{.age}}
后端: type User struct { Id int Name string } func Hello(context *gin.Context) { map_struct_data := map[string]User{ "user":User{Id:1,Name:"hallen"}, } context.HTML(http.StatusOK,"user/index.html",map_struct_data ) } 前端: {{.user.Id}} {{.user.Name}}
和数组类似,唯一的区别是不用指定长度了,长度是可变的
e.g. http://127.0.0.1:8080/user/hallen
1.第一种情况:使用占位符: ,必须得指定这个路径
2.第二种情况:使用占位符*,可以不用匹配这个路径
区别:参数前面是使用冒号还是使用通配符,冒号的比如指定路径,通配符的可以不用
代码示例:
func Index(c *gin.Context) {
name := c.Param("name")
c.String(http.StatusOK, "Hello %s", name)
}
// engine.GET("/user/*name",Index)
engine.GET("/user/:name",Index)
1.contxt.Query
2.contxt.DefaultQuery
区别:DefaultQuery比Query多了个默认值,如果没有获取到会使用默认值
3.contxt.QueryArray
4.contxt.QueryMap
注意:是post请求
1.contxt.PostForm(“username”) 获取表单中的name属性对应的值
示例代码:
前端:submit提交 <form action="/hello_add" method="post"> <input type="text" name="username"><br> <input type="text" name="age"><br> <input type="submit" value="提交"> </form> 后端: func IndexAdd(contxt *gin.Context) { name := contxt.PostForm("username") age := contxt.PostForm("age") contxt.String(200,"hello,%s,年龄为:%s",name,age) } func main() { engine := gin.Default() engine.LoadHTMLGlob("templates/**/*") engine.Static("/static","static") engine.POST("/hello_add",IndexAdd) engine.Run() }
2.contxt.DefaultPostForm(“username”, “hallen”) 如果没有获取到则使用指定的默认值
3.contxt.PostFormArray(“love”) 如果提交的数据有多个相同的name,获取数组
前端:
<form action="/hello_add" method="post">
<input type="text" name="username"><br>
<input type="text" name="age"><br>
ck1:<input type="checkbox" name="ck" value="1">
ck2:<input type="checkbox" name="ck" value="2">
ck3:<input type="checkbox" name="ck" value="3">
<input type="submit" value="提交">
</form>
后端:
arr_ck := contxt.PostFormArray("ck")
前端代码:
<form action="/hello_add" method="post">
<input type="text" name="username[1]"><br>
<input type="text" name="username[2]"><br>
<input type="submit" value="提交">
</form>
后端代码:
map_name := contxt.PostFormMap("username")
数据结构:map[1:xx1 2:xx2]
注意:name要以map的格式定义,指定key,用户输入value,
前端使用ajax提交,后端和form表单的获取方式一样,唯一的区别就是返回的是json
前端: <script src="/static/js/jquery.min.js"></script> <form> 姓名:<input type="text" id="name"> 年龄:<input type="text" id="age"> <input type="button" value="提交" id="btn_add"> </form> <script> var btn_add = document.getElementById("btn_add"); btn_add.onclick = function (ev) { var name = document.getElementById("name").value; var age = document.getElementById("age").value; $.ajax({ url:"/hello3_add", type:"POST", data:{ "name":name, "age":age }, success:function (data) { alert(data["code"]); alert(data["msg"]); }, fail:function (data) { } }) } </script> 注意:引入jquery.min.js: 后端: name := context.PostForm("name") age := context.PostForm("age") fmt.Println(name) fmt.Println(age) messgae_map := map[string]interface{}{ "code":200, "msg":"提交成功", } context.JSON(http.StatusOK,messgae_map) //context.JSON(http.StatusOK,gin.H{ // "code":200, // "msg":"提交成功", //})
它能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象
示例代码:
type User struct { Id int `form:"id" json:"id"` Name string `form:"name" json:"name"` } structTag:指定字段名称,不用使用首字母大写的 func Index(contxt *gin.Context) { var u User err := contxt.ShouldBind(&u) fmt.Println(err) fmt.Println(u) contxt.String(http.StatusOK, "Hello %s", u.Name) }
可以使用显式绑定声明绑定 multipart form:
c.ShouldBindWith(&form, binding.Form)
或者简单地使用 ShouldBind 方法自动绑定
ShouldBindQuery函数只绑定 url 查询参数而忽略 post 数据
1.单文件
前端: <form action="/upload2" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="提交"> </form> 注意:设置enctype参数 后端: func Upload2(context *gin.Context) { fmt.Println("+++++++++++++++") file,_ := context.FormFile("file") // 获取文件 fmt.Println(file.Filename) file_path := "upload/" + file.Filename // 设置保存文件的路径,不要忘了后面的文件名 context.SaveUploadedFile(file, file_path) // 保存文件 context.String(http.StatusOK,"上传成功") } 防止文件名冲突,使用时间戳命名: unix_int := time.Now().Unix() // 时间戳,int类型 time_unix_str := strconv.FormatInt(unix_int,10) // 讲int类型转为string类型,方便拼接,使用sprinf也可以 file_path := "upload/" + time_unix_str + file.Filename // 设置保存文件的路径,不要忘了后面的文件名 context.SaveUploadedFile(file, file_path) // 保存文件
2.多文件上传
前端: <form action="/upload2" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="file" name="file"> <input type="submit" value="提交"> </form> 注意:不要忘了enctype参数 后端: func Upload2(context *gin.Context) { form,_ := context.MultipartForm() files := form.File["file"] for _,file := range files { // 循环 fmt.Println(file.Filename) unix_int := time.Now().Unix() // 时间戳,int类型 time_unix_str := strconv.FormatInt(unix_int,10) // 讲int类型转为string类型,方便拼接,使用sprinf也可以 file_path := "upload/" + time_unix_str + file.Filename // 设置保存文件的路径,不要忘了后面的文件名 context.SaveUploadedFile(file, file_path) // 保存文件 } context.String(http.StatusOK,"上传成功") } 注意:form.File["file"] 这里是中括号,不是小括号
二、ajax方式上传文件
后端代码和form表单方式一样的
1.单文件
前端: <script src="/static/js/jquery.min.js"></script> <form> {{/*<input type="file" name="file">*/}} 用户名:<input type="test" id="name"><br> <input type="file" id="file"> <input type="button" value="提交" id="btn_add"> </form> <script> var btn_add = document.getElementById("btn_add"); btn_add.onclick = function (ev) { var name = document.getElementById("name").value; var file = $("#file")[0].files[0]; var form_data = new FormData(); form_data.append("name",name); form_data.append("file",file); $.ajax({ url:"/upload2", type:"POST", data:form_data, contentType:false, processData:false, success:function (data) { console.log(data); }, fail:function (data) { console.log(data); } }) } </script> 注意: 1.引入juery.min.js文件 2.ajax中需要加两个参数: contentType:false, processData:false,
processData:false 默认为true,当设置为true的时候,jquery ajax 提交的时候不会序列化 data,而是直接使用data
contentType: false 不使用默认的application/x-www-form-urlencoded这种contentType
2.多文件
name名称不相同就是个单文件上传
name名称相同
<script> var btn_add = document.getElementById("btn_add"); btn_add.onclick = function (ev) { var name = document.getElementById("name").value; console.log($(".file")); var files_tag = $(".file"); var form_data = new FormData(); for (var i=0;i<files_tag.length;i++){ var file = files_tag[i].files[0]; form_data.append("file",file); } console.log(files); form_data.append("name",name); $.ajax({ url:"/upload2", type:"POST", data:form_data, contentType:false, processData:false, success:function (data) { console.log(data); }, fail:function (data) { console.log(data); } }) } </script>
context.JSON(http.StatusOK,gin.H{
"code":200,
"tag":"<br>",
"msg":"提交成功",
"html":"<b>Hello, world!</b>",
})
结果:{"code":200,"html":"\u003cb\u003eHello, world!\u003c/b\u003e","msg":"提交成功","tag":"\u003cbr\u003e"}
生成具有转义的非 ASCII 字符的 ASCII-only JSON
context.AsciiJSON(http.StatusOK,gin.H{
"code":200,
"tag":"<br>",
"msg":"提交成功",
"html":"<b>Hello, world!</b>",
})
结果:{"code":200,"html":"\u003cb\u003eHello, world!\u003c/b\u003e","msg":"\u63d0\u4ea4\u6210\u529f","tag":"\u003cbr\u003e"}
使用 JSONP 向不同域的服务器请求数据。如果查询参数存在回调,则将回调添加到响应体中
context.JSONP(http.StatusOK,gin.H{
"code":200,
"tag":"<br>",
"msg":"提交成功",
"html":"<b>Hello, world!</b>",
})
结果:{"code":200,"html":"\u003cb\u003eHello, world!\u003c/b\u003e","msg":"提交成功","tag":"\u003cbr\u003e"}
如果传输的数据在两个不同的域,由于在javascript里无法跨域获取数据,所以一般采取script标签的方式获取数据,传入一些callback来获取最终的数据,这就有可能造成敏感信息被劫持
context.PureJSON(http.StatusOK,gin.H{
"code":200,
"tag":"<br>",
"msg":"提交成功",
})
结果:{"code":200,"html":"<b>Hello, world!</b>","msg":"提交成功","tag":"<br>"}
使用 SecureJSON 防止 json 劫持。如果给定的结构是数组值,则默认预置 “while(1),” 到响应体。
names := []string{"lena", "austin", "foo"}
// 将输出:while(1);["lena","austin","foo"]
context.SecureJSON(http.StatusOK, names)
json劫持:利用网站的cookie未过期,然后访问了攻击者的虚假页面,那么该页面就可以拿到json形式的用户敏感信息
context.XML(http.StatusOK,gin.H{
"code":200,
"tag":"<br>",
"msg":"提交成功",
"html":"<b>Hello, world!</b>",
})
结果:
<map>
<code>200</code>
<tag><br></tag>
<msg>提交成功</msg>
<html><b>Hello, world!</b></html>
</map>
context.YAML(http.StatusOK,gin.H{
"code":200,
"tag":"<br>",
"user":gin.H{"name":"zhiliao","age":18},
"html":"<b>Hello, world!</b>",
})
结果:
code: 200
html: <b>Hello, world!</b>
tag: <br>
user:
age: 18
name: zhiliao
定义proto文件
user.proto
syntax = 'proto3';
package user;
message User {
string name = 1;
int32 age = 2;
}
messages类型:message,server,enum
导出go文件
protoc --go_out=. user.proto
使用
user_data := &user.User{
Name:"zhiliao",
Age:18,
}
context.ProtoBuf(200,user_data)
注意:是指针
ctx.Redirect(http.StatusFound,"/xxx")
状态码是302
func main() {
router := gin.Default()
http.ListenAndServe(":8080", router)
}
或者
func main() {
router := gin.Default()
s := &http.Server{
Addr: ":8080",
Handler: router,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
}
router := gin.Default()
router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)
二、设置启动参数
router := gin.Default()
s := &http.Server{
Addr: fmt.Sprintf(":%s", "8080"),
Handler: router,
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。