公司网站建设需求书,seo要点,跨境网站有哪些平台,做网站用什么电脑配置Gin框架Gin简介第一个Gin示例HelloworldRESTful APIGin返回数据的几种格式Gin 获取参数HTTP重定向Gin路由路由组Gin框架当中的中间件Gin简介
Gin 是一个用 Go (Golang) 编写的 web 框架。它是一个类似于 martini 但拥有更好性能的 API 框架#xff0c;由于 httprouter路由组Gin框架当中的中间件Gin简介
Gin 是一个用 Go (Golang) 编写的 web 框架。它是一个类似于 martini 但拥有更好性能的 API 框架由于 httprouter速度提高了近 40 倍。Gin在GitHub上已经有47k的star它和Golang的语法一样简洁明了使得初学者得以迅速入门。只需要在终端上输入以下命令就可以将使用gin框架了
go get -u github.com/gin-gonic/gin第一个Gin示例Helloworld
import (github.com/gin-gonic/ginnet/http
)func main() {router : gin.Default()//默认引擎router.GET(/, func(c *gin.Context) {c.String(http.StatusOK, Hello World)})router.Run(:8000)//启动服务
}下面解释一下这个上面这份代码的意思
router:gin.Default()这是默认的服务器。使用gin的Default方法创建一个路由Handler然后通过Http方法绑定路由规则和路由函数。不同于net/http库的路由函数gin进行了封装把request和response都封装到了gin.Context的上下文环境中。最后启动路由的Run方法监听端口。还可以用http.ListenAndServe(“:8080”, router)或者自定义Http服务器配置。
启动方式有如下两种
// 启动方式一
router.Run(:8000)
// 启动方式二
http.ListenAndServe(:8000, router)注意这个 8080的意思其实是这个127.0.0.18080这一点大家需要注意了。当然我们也可以将这个**“:8080改为这个0.0.0.0:8080”**.
package mainimport (github.com/gin-gonic/ginnet/http
)func Index(context *gin.Context) {context.String(http.StatusOK, Hello ksy!)
}
func main() {// 创建一个默认的路由router : gin.Default()// 绑定路由规则和路由函数访问/index的路由将由对应的函数去处理router.GET(/index, Index)// 启动监听gin会把web服务运行在本机的0.0.0.0:8080端口上router.Run(0.0.0.0:8080)// 用原生http服务的方式 router.Run本质就是http.ListenAndServe的进一步封装http.ListenAndServe(:8080, router)
}在这里博主在说一下这个Index这个函数的参数是固定写死的就是确定的他的类型必须是这个gin.Context。上面的这个http.StatusOk代表的是这个状态码在这里代表的是这个200* 下面我们将上面这个程序跑起来然后打开浏览器访问/index这个路径我看一下这个效果
RESTful API
REST与技术无关代表的是一种软件架构风格REST是Representational State Transfer的简称中文翻译为“表征状态转移”或“表现层状态转化”。 简单来说REST的含义就是客户端与Web服务器之间进行交互的时候使用HTTP协议中的4个请求方法代表不同的动作。
GET用来获取资源POST用来新建资源PUT用来更新资源DELETE用来删除资源。 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,})})
}其中这个c.JSON代表的是返回的是一个json格式的数据而这个gin.H其实是一个这个Map。我们可以查看其定义。
// H is a shortcut for map[string]interface{}
type H map[string]any当然我们也可以不使用这个gin框架给我提供的使用我们自己定义的。
package mainimport (github.com/gin-gonic/ginnet/http
)func main() {r : gin.Default()r.GET(/json, func(c *gin.Context) {//方法1使用map,方法二使用结构体data : map[string]interface{}{name: 小王子,message: ksy,age: 18,}var msg struct { //注意这个结构体里面的字段必须大写因为golang当中包的访问性Name string json:nameAge int json:ageMessage string json:message}msg.Message ni hao ksymsg.Age 20msg.Name ksy//获取使用gin.Hc.JSON(http.StatusOK, data)c.JSON(http.StatusOK, msg)})r.Run(:9090)
}在这里我们需要注意的是这个结构体在进行序列化的时候这个首字母需要大写赋值会序列化失败这是因为这个golang包的可见性决定的。 那会不会有这样一个场景了就是某个路径Get,Post等方法都可以访问这个路径时我们又该如何来写了或者是这个当用户返回这个服务器上的路径不存在时我们不希望返回这个404NotFound,我们希望返回我们这个我们自定义的东西时又该如何写了。gin框架已经提供给了我们这个处理的函数了下面我们一起来看看吧。
package mainimport (github.com/gin-gonic/ginnet/http
)func main() {r : gin.Default()//可以接受任何请求r.Any(usr, func(c *gin.Context) {switch c.Request.Method {//判断到底是那种请求case http.MethodGet:c.JSON(http.StatusOK, gin.H{method: get})case http.MethodPost:c.JSON(http.StatusOK, gin.H{method: post})}})//定义没有时执行的函数也就是用户访问的路径不存在时会调用这个函数r.NoRoute(func(c *gin.Context) {c.JSON(http.StatusNotFound, gin.H{msg: ksy.com,})})r.Run(:8081)
}我们可以使用这个Any方法进而在其回调方法当中获取其请求的方试通过一个简单的switchcase进行不同的处理将数据返回。还有就是这个NoRoute方法就是当用户这个输入的路径不存在时会执行这个回调。下面我们来演示一下这个效果。 当这个请求路径不存在时。
Gin返回数据的几种格式
在gin框架当中我们可以指定这个返回数据的格式。在前面的例子当中我们这个String,json类型的数据我们都返回了下面我们一起看看这个剩下的格式吧
1.返回字符串类型的数据
router.GET(/index, func(c *gin.Context) {c.String(http.StatusOK, hello world)
})2.返回json类型数据
c.JSON(http.StatusOK, gin.H{method: get})3.返回xml数据格式
router.GET(/xml, func(c *gin.Context) {c.XML(http.StatusOK, gin.H{user: hanru, message: hey, status: http.StatusOK})
})4.返回yaml数据格式
router.GET(/yaml, func(c *gin.Context) {c.YAML(http.StatusOK, gin.H{user: hanru, message: hey, status: http.StatusOK})
})5.返回html数据格式 先要使用 **LoadHTMLGlob()或者LoadHTMLFiles()**方法来加载模板文件。
router.LoadHTMLGlob(gin框架/templates/*)
//router.LoadHTMLFiles(templates/index.html, templates/index2.html)
//定义路由
router.GET(/html, func(c *gin.Context) {//根据完整文件名渲染模板并传递参数c.HTML(http.StatusOK, index.html, gin.H{title: hello world,})
})在模板中使用这个title需要使用{{ .title }} 不同文件夹下模板名字可以相同此时需要 LoadHTMLGlob() 加载两层模板路径
router.LoadHTMLGlob(templates/**/*)
router.GET(/posts/index, func(c *gin.Context) {c.HTML(http.StatusOK, posts/index.html, gin.H{title: Posts,})c.HTML(http.StatusOK, users/index.html, gin.H{title: Users,})})6.返回文件响应 // 在golang总没有相对文件的路径它只有相对项目的路径
// 网页请求这个静态目录的前缀 第二个参数是一个目录注意前缀不要重复
router.StaticFS(/static, http.Dir(static/static))
// 配置单个文件 网页请求的路由文件的路径
router.StaticFile(/titian.png, static/titian.png)Gin 获取参数
1.查询参数 Query也就是这个获取querystring参数querystring指的是URL中?后面携带的参数例如/user/search?username匡思源address沙河。 获取请求的querystring参数的方法如下
package mainimport (github.com/gin-gonic/ginnet/http
)//queryString 通常用在Get方法func main() {r : gin.Default()r.GET(/get, func(c *gin.Context) {//Get请求URL?后面是querystring参数 keyval形势多个用连接//获取json那边发请求携带的queryString//Name : c.Query(name)//Name:c.DefaultQuery(name,ddd)//如果能查到用查到的否则用设置的默认值Name, _ : c.GetQuery(name) //返回值bool取不到返回falsec.JSON(http.StatusOK, gin.H{name: Name,})})r.Run(:8089)
}在这里获取这个这个请求参数querystring这个gin提供了多种函数
GetQuery如果参数不存在第二个返回值为falseDefaultQuery:如果参数不存在返回默认设置的值存在用传进来的值Query:获取参数不存在为 下面我们将这个程序运行起来看一看这个效果如何 剩下的各位铁子可以自行下去演示这个效果在这里博主就不一一演示这个效果了。
2.获取form参数 当前端请求的数据通过form表单提交时例如向/user/search发送一个POST请求获取请求数据的方式如下
package mainimport (github.com/gin-gonic/ginnet/http
)/*获取form表单的提交参数
*/func main() {r : gin.Default()r.LoadHTMLFiles(./login.html) //加载某版文件r.GET(/login, func(c *gin.Context) {c.HTML(http.StatusOK, login.html, nil)})r.POST(/login, func(c *gin.Context) {//获取form表单提交的数据// username:c.PostForm(username)// password:c.PostForm(password)password : c.DefaultPostForm(password, ****)username : c.DefaultPostForm(username, somebody)//c.GetPostForm(username)两个返回值有一个为这个是否存在c.JSON(http.StatusOK, gin.H{username: username,password: password,})})r.Run(127.0.0.1:9091)
}和这个QueryString是一样的这个gin提供了多种获取函数
PostForm:获取用户传过来的表单数据DefaultPostForm如果用户没传使用设置的默认值GetPostForm第二个返回值可以判断用户是否传递了这个参数
下面我们可以使用这个PostMan来进行这个测试 3.获取json数据 当前端请求的数据通过JSON提交时例如向/json发送一个POST请求则获取请求参数的方式如下
package mainimport (encoding/jsongithub.com/gin-gonic/ginnet/http
)func main() {r : gin.Default()r.POST(/json, func(c *gin.Context) {// 注意下面为了举例子方便暂时忽略了错误处理b, _ : c.GetRawData() // 从c.Request.Body读取请求数据// 定义map或结构体var m map[string]interface{}// 反序列化_ json.Unmarshal(b, m)c.JSON(http.StatusOK, m)})r.Run(:8081)
}在这里我们同样的使用这个Postman来进行测试 4.获取获取path参数 请求的参数通过URL路径传递例如/user/search/小王子/沙河。 获取请求URL路径中的参数的方式如下。
package mainimport (github.com/gin-gonic/ginnet/http
)//获取请求path(URI)参数返回的都是字符串类型
func main() {r : gin.Default()r.GET(/:name/:age, func(c *gin.Context) {//获取路径参数name : c.Param(name)age : c.Param(age)c.JSON(http.StatusOK, gin.H{name: name,age: age,})})r.GET(/blog/:year/:month, func(c *gin.Context) {year : c.Param(year)month : c.Param(month)c.JSON(http.StatusOK, gin.H{year: year,month: month,})})r.Run(:9092)
}这个在这里就不演示了非常的简单各位铁子可以自行演示即可。
5.参数绑定非常重要 为了能够更方便的获取请求相关参数提高开发效率我们可以基于请求的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: q1mi, password: 123456})router.POST(/loginJSON, func(c *gin.Context) {var login Loginif 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表单示例 (userq1mipassword123456)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(/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()})}})// Listen and serve on 0.0.0.0:8080router.Run(:8080)
}ShouldBind会按照下面的顺序解析请求中的数据完成绑定 如果是 GET 请求只使用 Form 绑定引擎query。 如果是 POST 请求首先检查 content-type 是否为 JSON 或 XML然后再使用 Formform-data功能非常的强大各位铁子需要这个重点看一下这个.
HTTP重定向
HTTP 重定向很容易。 内部、外部重定向均支持。下面我们一起来看看这个如何进行重定向
package mainimport (github.com/gin-gonic/ginnet/http
)//重定向未登录进行重定向
func main() {r : gin.Default()r.GET(/a, func(c *gin.Context) {//重定向//c.Redirect(http.StatusMovedPermanently, https://www.baidu.com) //永久重定向c.Request.URL.Path /br.HandleContext(c) //继续处理})//路由重定向r.GET(/b, func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{message: b,})})r.Run(:9093)
}注意上面注释那个是永久重定向当用户第二次请求时不会再这个返回它了而是直接访问重定向的网址。而下面那个时使用这个路由重定向使用HandleContext。
Gin路由路由组
我们之前的例子使用的是这个普通路由就是类似于这个下面这样
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)})下面我们看看这个路由组吧有时候这个路由都有这个公共的前缀但是又有很多个函数此时我们就可以将其抽离出来。下面我们看看这个路由组的写法吧
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(:8900)
}当然这个路由组也支持这个嵌套
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) {...})// 嵌套路由组xx : shopGroup.Group(xx)xx.GET(/oo, func(c *gin.Context) {...})}通常我们将路由分组用在划分业务逻辑或划分API版本时。
Gin框架当中的中间件
Gin框架允许开发者在处理请求的过程中加入用户自己的钩子Hook函数。这个钩子函数就叫中间件中间件适合处理一些公共的业务逻辑比如登录认证、权限校验、数据分页、记录日志、耗时统计等。 Gin中的中间件必须是一个gin.HandlerFunc类型。 这点非常的重要这个是固定不变的 下面我们就写一个小小的Demo来看看这个中间件的写法
package mainimport (fmtgithub.com/gin-gonic/ginnet/httptime
)//定义一个中间件
func httpfunc(c *gin.Context) {start : time.Now()c.Next() //调用后续的处理函数//c.Abort()//阻止调用后面的函数cost : time.Since(start)fmt.Printf(一共花费了%dms, int(cost))
}
func main() {r : gin.Default()r.Use(httpfunc) //全局注册中间件函数httpfunc,所有的函数都会只会这个全局的中间件函数r.GET(/index, func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{msg: index,})})r.Run(:9098)
}
首先这个客户段的请求进来会执行这个httpFunc函数这个是安装这个顺序决定的c.Next代表的是这个继续执行而如果我们使用这个c.Abort()那么下面这个函数将不会执行也就是说他的意思就是停止的意思。
Demo2 我们有时候可能会想要记录下某些情况下返回给客户端的响应数据这个时候就可以编写一个中间件来搞定。 type bodyLogWriter struct {gin.ResponseWriter // 嵌入gin框架ResponseWriterbody *bytes.Buffer // 我们记录用的response
}// Write 写入响应体数据
func (w bodyLogWriter) Write(b []byte) (int, error) {w.body.Write(b) // 我们记录一份return w.ResponseWriter.Write(b) // 真正写入响应
}// ginBodyLogMiddleware 一个记录返回给客户端响应体的中间件
// https://stackoverflow.com/questions/38501325/how-to-log-response-body-in-gin
func ginBodyLogMiddleware(c *gin.Context) {blw : bodyLogWriter{body: bytes.NewBuffer([]byte{}), ResponseWriter: c.Writer}c.Writer blw // 使用我们自定义的类型替换默认的c.Next() // 执行业务逻辑fmt.Println(Response body: blw.body.String()) // 事后按需记录返回的响应
}当然这个我们还可以为这个路由组定义这个中间件
shopGroup : r.Group(/shop, StatCost())
{shopGroup.GET(/index, func(c *gin.Context) {...})...
}当然还有另外一种写法
shopGroup : r.Group(/shop)
shopGroup.Use(StatCost())
{shopGroup.GET(/index, func(c *gin.Context) {...})...
}Demo3 中间件传递数据 使用Set设置一个key-value,在后续中间件中使用Get接收数据。对应代码如下
package mainimport (fmtgithub.com/gin-gonic/gin
)type User struct {Name stringAge int
}func m10(c *gin.Context) {fmt.Println(m1 ...in)c.Set(name, User{ranran, 21})c.Next()fmt.Println(m1 ...out)
}func main() {router : gin.Default()router.Use(m10)router.GET(/, func(c *gin.Context) {fmt.Println(index ...in)name, _ : c.Get(name)user : name.(User)fmt.Println(user.Name, user.Age)c.JSON(200, gin.H{msg: index})})router.Run(:8080)}value的类型是any类型所有我们可以用它传任意类型在接收的时候做好断言即可
中间件的使用还是这个非常好的当前端发送请求过来之后我们可以定义中间件先检查这个参数的合法性如果不合法我们可以直接Abort停止如果合法我们可以执行这个配置文件的初始化最后再这个到具体的业务逻辑。
最后说一下这个中间件再使用过程当中的几个注意事项 1.gin.Default()默认使用了Logger和Recovery中间件其中Logger中间件将日志写入gin.DefaultWriter即使配置了GIN_MODErelease。Recovery中间件会recover任何panic。如果有panic的话会写入500响应码。 如果不想使用上面两个默认的中间件可以使用gin.New()新建一个没有任何默认中间件的路由。
2.gin中间件中使用goroutine 当在中间件或handler中启动新的goroutine时不能使用原始的上下文c *gin.Context必须使用其只读副本c.Copy()。因为传递指针会修改上一层的内容导致意想不到的事情发生