关于gin:gin框架利用中间件记录用户操作行为

背景阐明在我的项目的具体应用过程中咱们很有必要去记录那个用户操作了什么这样的需要。当然实现的形式有很多,明天咱们介绍一下具体怎么在gin框架中利用协成的个性异步去记录用户的的操作行为。在这里先介绍一下我集体给开源的gsadmin我的项目,GS Admin=gin+scui 它是golang 开发的一个企业级后盾。遵循MIT开源协定。前端框架是scui,SCUI基于 Vue3、elementPlus持续性的提供独家组件和丰盛的业务模板帮忙你疾速搭建企业级中后盾前端工作。后端框架是gin,Gin是一个golang的微框架,封装比拟优雅,具备疾速灵便,容错不便等特点。内置了权限治理、用户治理等根底模块儿,还反对了事件服务,不便业务解耦。后续会依据用户的反馈更新内容! 上面来介绍一下实现思路1、次要性能是在中间件内实现2、利用中间件来获取用户拜访的门路3、用拜访门路来匹配数据库内的路由信息4、收集须要的信息,利用协成的机制异步记录到数据库中或其余须要记录的中央 上面看代码package middlewareimport ( "encoding/json" "github.com/gin-gonic/gin" "github.com/sonhineboy/gsadmin/service/app/models" "github.com/sonhineboy/gsadmin/service/app/repositorys" "github.com/sonhineboy/gsadmin/service/global")func OperationLog() gin.HandlerFunc { return func(c *gin.Context) { cCp := c.Copy() go func() { var ( doData []byte log models.OperationLog ) method := c.Request.Method //参数 if method == "GET" { doData, _ = json.Marshal(cCp.Request.URL.Query()) } if method == "POST" { doData, _ = cCp.GetRawData() } claims, ok := repositorys.GetCustomClaims(c) if ok == true { log.UserId = claims.Id log.UserName = claims.Name } else { log.UserId = 0 } var where = make(map[string]interface{}) var d models.MenuApiList db := global.Db.Model(&models.MenuApiList{}) where["url"] = cCp.Request.URL.Path db.Preload("Menu").Where(where).First(&d) log.Method = cCp.Request.Method log.DoData = string(doData) log.Ip = cCp.ClientIP() title, ok := d.Menu.Meta["title"] if ok { log.PathName = title.(string) } log.UrlPath = cCp.Request.URL.Path global.Db.Create(&log) }() c.Next() }}演示地址喜爱我的分享或者我的项目的多点点star,咱们多多交换 ...

May 24, 2023 · 1 min · jiezi

关于gin:Gin渲染-html代码

Gin渲染html代码,参考文档:https://www.kancloud.cn/shuangdeyu/gin_book/949436 后端代码: package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http")//文档: https://www.kancloud.cn/shuangdeyu/gin_book/949436func main() { router := gin.Default() router.LoadHTMLGlob("templates/*") //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") router.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "title": "Main website", }) return }) type requestDataModel struct { Name string `json:"name"` Pass int `json:"pass"` Age int `json:"age"` } router.POST("/getData", func(c *gin.Context) { fmt.Print(c.PostForm("name")) var requestData requestDataModel if err := c.BindJSON(&requestData); err == nil { } c.JSON(http.StatusOK, gin.H{ "code": 200, "msg": "success", "data": requestData, }) return }) // http://127.0.0.1:8080/index router.Run()}templates/index.html 代码 ...

April 26, 2023 · 1 min · jiezi

关于gin:gin-表单数据处理简单实例

package mainimport ( "encoding/json" "fmt" "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.POST("/json", func(c *gin.Context) { data,_ := c.GetRawData() var m map[string]interface{} _ = json.Unmarshal(data, &m) c.JSON(http.StatusOK,m) }) r.LoadHTMLGlob("./temp/*") r.GET("/user/add", func(c *gin.Context) { c.HTML(http.StatusOK, "useradd.html", gin.H{}) }) r.POST("/user/add", func(c *gin.Context) { username := c.PostFormArray("username") password := c.PostFormArray("password") fmt.Println(username, "goubibibi", password) c.JSON(http.StatusOK,gin.H{ "msg":"ok", "username":username, "password": password, }) }) r.Run()}<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <form action="/user/add" method="post"> <p>username: <input type="text" name="username"></p> <p>password: <input type="text" name="password"></p> <button type="submit"></button> </form></head><body></body></html> ...

February 11, 2023 · 1 min · jiezi

关于gin:gin框架学习

依据发来的申请进行解析 c.Bind()//和表单的数据进行绑定c.ShouldBindUri()//和uri的内容进行绑定go操纵数据库,用xqite框架操纵如果是对数据库的数据进行查问 Db.select("这是要操纵的表","这是sql语句")如果是对数据库的数据进行批改 Db.Exec("这是要操纵的表","这是sql语句")用go操纵redisc, err := redis.Dial("tcp", "localhost:6379")_, err = c.Do("Set", "abc", 100)r, err := redis.Int(c.Do("Get", "abc")_, err = c.Do("MSet", "abc", 100, "efg", 300)r, err := redis.Ints(c.Do("MGet", "abc", "efg"))_, err = c.Do("lpush", "book_list", "abc", "ceg", 300)r, err := redis.String(c.Do("lpop", "book_list"))_, err = c.Do("HSet", "books", "abc", 100)r, err := redis.Int(c.Do("HGet", "books", "abc"))var pool *redis.Pool //创立redis连接池pool = &redis.Pool{ //实例化一个连接池

July 8, 2022 · 1 min · jiezi

关于gin:基于Gin框架的web后端开发八-Gin框架的请求重定向

重定向分为内部重定向和外部重定向,也能够分为永恒重定向和长期重定向。 内部重定向能够应用Redirect办法来实现,举个例子,如果咱们想永恒重定向到内部的百度: package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.GET("/index", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com") }) r.Run(":9090")}运行后果就间接重定向到内部的百度了,咱们发现 外部重定向举个例子,如果想要从/a外部跳到/b,能够这样写: package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.GET("/index", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "https://www.baidu.com") }) r.GET("/a", func(c *gin.Context) { //跳转到/b对应的路由处理函数 c.Request.URL.Path = "/b" r.HandleContext(c) }) r.GET("/b", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "b", }) }) r.Run(":9090")}后果如下: 参考: bilibili

June 9, 2022 · 1 min · jiezi

关于gin:基于Gin框架的web后端开发七-Gin框架的文件上传详解

    上传文件是用户将文件从客户端上传到服务器的过程,上面举个简略的上传文件的例子:    先写一个前端页面: <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>index</title></head><body><form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="f1"> <input type="submit" value="上传"></form></body></html>后果如图: 记得在html的form标签中加上这个属性:enctype="multipart/form-data" 用来二进制传文件。而后再main.go中这样写: package mainimport ( "github.com/gin-gonic/gin" "net/http" "path")func main() { r := gin.Default() r.LoadHTMLFiles("./index.html") r.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", nil) }) r.POST("/upload", func(c *gin.Context) { //从申请中读取文件 f, err := c.FormFile("f1") if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": err.Error(), }) } else { //将文件保留在服务器 //dst := fmt.Sprintf("./%s", f.Filename)//写法1 dst := path.Join("./", f.Filename) c.SaveUploadedFile(f, dst) c.JSON(http.StatusOK, gin.H{ "status": "ok", }) } }) r.Run(":9090")}后果上传文件胜利: ...

June 8, 2022 · 1 min · jiezi

关于gin:基于Gin框架的web后端开发六-参数绑定ShouldBind详解

在前几篇文章中,数据或者参数的绑定须要一个一个的绑定,比方这样: package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http")//建设一个构造体来存储数据type UserInfo struct { username string password string}func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { username := c.Query("username") passsword := c.Query("password") u := UserInfo{ username: username, password: passsword, } fmt.Printf("%v\n", u) //给这个申请返回一个JSON数据 c.JSON(http.StatusOK, gin.H{ "message": "ok", }) }) r.Run(":9090")}后果如下:     然而一单参数略微多一点,这样一个一个的绑定就太麻烦了,这里介绍一下Gin框架中的ShouldBind(),它用于将申请携带的参数和后端的构造体绑定起来,比方下面咱们UserInfo这个构造体有username和password两个字段,如果申请中呈现这两个字段ShouldBind()就会主动帮咱们取出这两个值,而后伴咱们做一个构造体的初始化,咱们就能够失去一个UserInfo类型的变量。    ShouldBind()的应用过程须要留神: ShouldBind接管的是构造体对象的地址(&对象名字),而不是对象构造体的每一个字段首字母要大写(相似Java public申明)构造体该打标签要打,发送json格局的申请要打json标签,地址栏中发送申请要打form标签。ShouldBind模仿queryString举个例子:如果要想把http://127.0.0.1:9090/user?Us...这个链接的两个参数取到,能够这样写: package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http")//建设一个构造体来存储数据type UserInfo struct { Username string Password string}func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { //username := c.Query("username") //passsword := c.Query("password") //u := UserInfo{ // username: username, // password: passsword, //} //申明一个UserInfo类型的变量u var u UserInfo //这里把地址传过来 err := c.ShouldBind(&u) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": err.Error(), }) } else { fmt.Printf("%#v\n", u) c.JSON(http.StatusOK, gin.H{ "status": "ok", }) } }) r.Run(":9090")}后果如下: ...

June 8, 2022 · 2 min · jiezi

关于gin:基于Gin框架的web后端开发四-获取FORM表单参数

咱们罕用POST申请发送FORM表单数据,这种形式绝对于GET形式更加平安。 未完待续。。

June 7, 2022 · 1 min · jiezi

关于gin:基于Gin框架的web后端开发三-获取queryString参数

tips: go mod tidy 能够剖析代码中依赖的第三方包,而后在go.mod中将这些以来记录下来。例如:想要获取query字段中的杨超过:http://127.0.0.1:9090/web?query=杨超过 形式一:Querypackage mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.GET("/web", func(c *gin.Context) { //这里要获取浏览器那边发申请携带的query string参数 name := c.Query("query") c.JSON(http.StatusOK, gin.H{ "name": name, }) }) r.Run(":9090")}测试后果如下: 形式二:DefaultQuery这其实和形式一类似,区别在于,如果没有查问到数据,能够用设置好的default数据: package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.GET("/web", func(c *gin.Context) { //这里要获取浏览器那边发申请携带的query string参数 //name := c.Query("query") name := c.DefaultQuery("query", "someone") c.JSON(http.StatusOK, gin.H{ "name": name, }) }) r.Run(":9090")}测试后果如下: 形式三:GetQuery这个函数有2个返回值,其实就是多了一个bool值,如果去不到参数,第二个返回的就是false: package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.GET("/web", func(c *gin.Context) { //这里要获取浏览器那边发申请携带的query string参数 //name := c.Query("query") //name := c.DefaultQuery("query", "someone") name, ok := c.GetQuery("query") if !ok { name = "someone" } c.JSON(http.StatusOK, gin.H{ "name": name, }) }) r.Run(":9090")}我觉第三种形式,思考的比拟全面,我更喜爱应用,当然,另外两种形式也能够在适合的场景应用。 ...

June 5, 2022 · 1 min · jiezi

关于gin:基于Gin框架的web后端开发二-JSON数据生成

基于Gin框架的web开发,总的来讲有两种: 第一种是后端应用go语言模板引擎实现整个web全栈开发,返回残缺的html文件给浏览器。第二种是前后端拆散,应用JSON交互因为第一种办法消耗太多网络资源,性能差,耦合度高,目前曾经根本被第二种模式取代,这篇博客只介绍如何在Gin框架中返回JSON数据给前端或者挪动端(俗称JSON的渲染)。 生成JSON数据能够用map,也能够用构造体。上面先介绍map: 基于map的JSON数据生成举个例子,代码如下: package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.GET("/json", func(c *gin.Context) { //这里结构服务器要返回前端的数据,一共有两种办法,第一种是:应用map将数据序列化 //,这个map的key是一个string, value是一个空的接口(这样即能够实现接管任意类型的数据) data := map[string]interface{}{ "name": "liber", "message": "hey liber~", "age": 16, } //这里要返回json格局的数据,所以用c.JSON,这样,数据就返回给申请方了 c.JSON(http.StatusOK, data) }) r.Run(":9090")}测试后果如下:因为用map来序列化json数据是很常见的写法,Gin框架的作者将这种写法封装了一下: //H is a shortcut for map[string]interface{}type H map[string]interface{}所以,咱们能够间接应用gin.H{}来代替: package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() r.GET("/json", func(c *gin.Context) { //这里结构服务器要返回前端的数据,一共有两种办法,第一种是:应用map将数据序列化 //,这个map的key是一个string, value是一个空的接口(这样即能够实现接管任意类型的数据) //data := map[string]interface{}{ // "name": "liber", // "message": "hey liber~", // "age": 16, //} data := gin.H{ "name": "libro", "message": "hey libro~", "age": 17, } //这里要返回json格局的数据,所以用c.JSON,这样,数据就返回给申请方了 c.JSON(http.StatusOK, data) }) r.Run(":9090")}测试后果如下: ...

June 5, 2022 · 1 min · jiezi

关于gin:Gin框架介绍

mac一开始装置Gin的时候还会有timeout报错,能够借鉴这篇博客解决: 未完待续。。。。

June 4, 2022 · 1 min · jiezi

关于gin:Go-nethttp-以及ioioutil包的使用

tips:这可能是最繁难的web后端开发demo 在goland中创立一个main.go而后依照http包的应用阐明编写代码如下: package mainimport ( "fmt" "net/http")func sayhello(w http.ResponseWriter, r *http.Request) { _, _ = fmt.Fprint(w, "<h1>hey this is LIBERHOME!</h1>")}func main() { http.HandleFunc("/hello", sayhello) err := http.ListenAndServe(":9090", nil) //服务器启动后,监听9090端口 if err != nil { fmt.Printf("http service failed, err: %v\n", err) return }}接下来在terminal执行go run main.go而后在浏览器输出http://127.0.0.1:9090/hello即可看到 当然,间接把网页显示的内容硬编码能够改成一个txt文件的形式:首先创立一个hello.txt: <h1>hey it is LIBERHOME!</h1><h2>this may be the most simple demo~</h2><img id='i1' src='https://avatar-static.segmentfault.com/274/037/2740371703-61baf9dec42e4_huge256'>而后之前的代码稍加批改,退出ioutil包: package mainimport ( "fmt" "io/ioutil" "net/http")func sayhello(w http.ResponseWriter, r *http.Request) { b, _ := ioutil.ReadFile("./hello.txt") _, _ = fmt.Fprint(w, string(b))}func main() { http.HandleFunc("/hello", sayhello) err := http.ListenAndServe(":9090", nil) //服务器启动后, 监听9090端口 if err != nil { fmt.Printf("http service failed, err: %v\n", err) return }}运行后果如下: ...

June 4, 2022 · 1 min · jiezi

关于gin:Gin初识go语言Web框架小白向

Web框架概述齐全不理解的萌新能够先看这篇文章对一些概念有初步的意识https://cloud.tencent.com/dev... web常见名词万维网:并非某种非凡的计算机网络,万维网是一个大规模的、联机式的信息储藏所,英文简称web。万维网用链接的办法能十分不便地从因特网上的一个站点拜访另一个站点(超链技术),具备提供分布式服务的特点。万维网是一个分布式的超媒体零碎,是超文本零碎的裁减。万维网基于B/S架构工作。URL:万维网应用对立资源定位符(Uniform Resource Locator)来标记万维网上的各种文档,并使每个文档在整个因特网的范畴内具备惟一的标识符URL。HTML:为了解决“怎么使不同作者创作的不同格调的万维网文档,都能在因特网上的各种主机上显示进去,同时使用户分明地晓得在什么中央存在着链接”这一问题,万维网应用超文本标记语言(HyperText Markup Language),使得万维网页面的设计者能够很不便地用链接从页面的某处链接到因特网的任何一个万维网页面,并且可能在本人的主机品目上将这些页面显示进去。HTML与txt一样,仅仅是是一种文档,不同之处在于,这种文档专供于浏览器上为浏览器用户提供对立的界面出现的对立规约。且具备结构化的特色,这是txt所不具备的强制规定。 web开发Web开发在近年来,随着自身技术的冲破以及挪动设施的遍及,基于web畛域的开发,也呈现了明确的岗位职责分工,一个web互联网产品中,基本上会分为web UI设计、Web前端开发以及web后端开发。 对于大型的互联网公司,还会分独立的Web架构开发组,专门负责web框架的保护更新与迭代。 Web前端开发用到的编程语言次要有javascript,以及随同有标记性文本语言html和款式渲染形式CSS。后端开发(Back-End Development,也称服务端开发、服务器端开发等)是创立残缺可运行的Web利用服务端程序(服务端程序和资源合称为后端,即在服务器上运行的、不波及用户界面的局部)的过程,是Web利用程序开发的一部分。后端开发者应用Java、Golang等语言及其衍生的各种框架、库和解决方案来实现Web应用程序的外围业务逻辑,并向外提供特定的API,使得Web利用可能高效、平安、稳固地运行。 web框架随着Web最新发展趋势的一直降级,Web我的项目开发也越来越难,而且须要破费更多的开发工夫,web框架应运而生。Web框架(Web framework)或者叫做Web利用框架(Web application framework),是用于进行Web开发的一套软件架构。大多数的Web框架提供了一套开发和部署网站的形式。为Web的行为提供了一套反对反对的办法。应用Web框架,很多的业务逻辑外的性能不须要本人再去欠缺,而是应用框架已有的性能就能够。Web框次要用于动静网络开发。 web框架的作用 Gin构造组成一些基本概念幂等性 http申请 前缀树 什么是GinGo 语言最风行了两个轻量级 Web 框架别离是 Gin 和 Echo,这两个框架大同小异,都是插件式轻量级框架,背地都有一个开源小生态来提供各式各样的小插件,这两个框架的性能也都十分好,裸测起来跑的飞快。Gin 具备运行速度快,分组的路由器,良好的解体捕捉和错误处理,十分好的反对中间件和 json。总之,在 Go语言开发畛域是一款值得好好钻研的 Web 框架。 开源网址:https://github.com/gin-gonic/gin web框架的组成https://blog.csdn.net/weixin_... GinEngine 对于web服务https://zhuanlan.zhihu.com/p/... 路由什么是路由 路由框架httprouter如同web倒退到肯定阶段呈现了web框架供人们应用疾速开发,路由也有各种各样的框架。httprouter 就是其中极为优良的一种。 handle在计算机程序设计中,句柄是对资源的形象援用。当应用软件援用由另一个零碎(如数据库或操作系统)治理的内存块或对象时,就会应用句柄。资源句柄能够是一个不通明的标识符,在这种状况下,它通常是一个整数(通常是用于治理该类型资源的数组或“表”中的数组索引),也能够是一个容许拜访进一步信息的指针。常见的资源句柄有文件描述符、网络套接字、数据库连贯、过程标识符 httprouter 用法 相干链接https://www.zhihu.com/questio...https://zhuanlan.zhihu.com/p/...https://blog.csdn.net/weixin_...https://zhuanlan.zhihu.com/p/...https://blog.csdn.net/hephaes...https://cloud.tencent.com/dev...https://www.imooc.com/wiki/fl...https://www.runoob.com/http/h...https://developer.mozilla.org...https://zhuanlan.zhihu.com/p/...https://juejin.cn/post/684490...https://qiankunpingtai.cn/art...

January 29, 2022 · 1 min · jiezi

关于gin:全栈开发实战小草看书之开篇

全栈开发实战小草看书之开篇介绍小草看书,一款 gin+vue+flutter 开发的看书利用。内容含古典文学,四大名著,神话武侠,诗词歌赋等。 Android端 技术选型Web端vite2,vue3,vue-router4,vuex4,vuex-persistedstate,axios,elementplus,screenfull,less 服务端gin,gorm,logrus,file-rotatelogs,viper,jwt,authz,cors,colly,cron,riot 挪动端flutter,curved_navigation_bar,device_info_plus,dio,flutter_html,flutter_slidable,fluttertoast,hive_flutter,percent_indicator,pull_to_refresh,uuid

December 15, 2021 · 1 min · jiezi

关于gin:gin框架之日志的使用

在我的项目开发中,日志模块必不可少。Golang作为新兴的语言,第三方日志包也越来越多,其中star数最多的是logrus。云盘我的项目中应用的日志包就是logrus,上面就介绍一下该日志包的个性和应用办法。目前我的项目中应用的服务端框架是gin,日志就以gin中间件的形式来应用。 一、logrus个性:齐全兼容golang规范库日志模块:logrus提供了6中日志级别:debug、info、warn、error、fatal和panic,这是golang规范日志模块的超集,须要留神的是:fatal间接调用的os.Exit(),来不及执行defer。可扩大的hook机制:容许使用者通过hook的形式将日志发送到任意的中央,如:本地文件系统、规范输入、logstash和elasticsearch或者mq等,也能够通过hook自定义日志的内容或格局。可选的日志输入格局:logrus内置两种日志格局:JSONFormatter和TextFormatter,如果这两种格局不满足需要,能够本人手动实现接口,定义日志的输入格局。Field机制:logrus激励通过Field机制进行精细化和结构化的日志记录,而不是通过简短的音讯来记录日志。二、根本应用写入文件:先从配置中获取到日志文件的门路和日志文件名,关上文件,以追加写的模式写入日志 // 写入文件f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND, os.ModeAppend)if err != nil { fmt.Println("err", err)}创立实例、设置输入和日志输入级别 // 实例化logger := logrus.New()// 设置输入logger.Out = f// 设置日志级别logger.SetLevel(logrus.DebugLevel)设置rotatelogs // 设置rotatelogs和writeMaplogWriter, _ := rotatelogs.New( // 宰割后的文件名称 fileName+".%Y%m%d.log", // 生成软链,指向最新的日志文件 rotatelogs.WithLinkName(fileName), // 设置最长保留工夫,这里设置成7天 rotatelogs.WithMaxAge(7*24*time.Hour), // 设置日志切割间隔时间,这里设置成1天 rotatelogs.WithRotationTime(24*time.Hour),)writeMap := lfshook.WriterMap{ logrus.InfoLevel: logWriter, logrus.FatalLevel: logWriter, logrus.DebugLevel: logWriter, logrus.WarnLevel: logWriter, logrus.ErrorLevel: logWriter, logrus.PanicLevel: logWriter,}增加自定义的hook lfhook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{ TimestampFormat: "2006-01-02 15:04:05",})// 新增hooklogger.AddHook(lfhook)设置日志输入的格局 startTime := time.Now()// 解决申请c.Next()endTime := time.Now()// 执行工夫latencyTime := endTime.Sub(startTime)// 申请形式reqMethod := c.Request.Method// 申请路由reqUrl := c.Request.URL// 状态码statuCode := c.Writer.Status()// 申请IPclientIP := c.ClientIP()// 日志格局logger.Infof("| %3d | %13v | %15s | %s | %s", statuCode, latencyTime, clientIP, reqMethod, reqUrl,)中间件的应用:间接在gin的router配置中应用router.use(中间件名称)即可。输入的成果: ...

September 4, 2021 · 2 min · jiezi

关于gin:golang-web框架gin使用教程

InstallationTo install Gin package, you need to install Go and set your Go workspace first. The first need Go installed (version 1.12+ is required), then you can use the below Go command to install Gin.$ go get -u github.com/gin-gonic/gin Import it in your code:import "github.com/gin-gonic/gin" (Optional) Import net/http. This is required for example if using constants such as http.StatusOK.import "net/http" Quick startassume the following codes in example.go file$ cat example.go ...

December 6, 2020 · 2 min · jiezi

gin中间件的使用

在Gin框架中,中间件(Middleware)指的是可以拦截http请求-响应生命周期的特殊函数,在请求-响应生命周期中可以注册多个中间件,每个中间件执行不同的功能,一个中间执行完再轮到下一个中间件执行。 中间件的常见应用场景如下: 请求限速api接口签名处理权限校验...提示:如果你想拦截所有请求做一些事情都可以开发一个中间件函数去实现。1.使用中间件func main() { r := gin.New() // 通过use设置全局中间件 // 设置日志中间件,主要用于打印请求日志 r.Use(gin.Logger()) // 设置Recovery中间件,主要用于拦截paic错误,不至于导致进程崩掉 r.Use(gin.Recovery()) // 忽略后面代码}2.自定义中间件下面我们来看一下gin中的中间件 // Use attaches a global middleware to the router. ie. the middleware attached though Use() will be// included in the handlers chain for every single request. Even 404, 405, static files...// For example, this is the right place for a logger or error management middleware.func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes { engine.RouterGroup.Use(middleware...) engine.rebuild404Handlers() engine.rebuild405Handlers() return engine}通过gin的源码,我们发现,中间件是一个gin.HandlerFunc ...

June 11, 2020 · 1 min · jiezi

gin定义统一处理错误

gin定义统一处理错误在gin中如果有错误需要响应给客户端,如果每一个都判断,并且处理返回,如果项目复杂了,需要写很多重复的代码来响应错误,今天我们来封装一个统一处理错误包装器,使用的是装饰器模式。 1.定义统一处理错误pkg/e/error.go package eimport "github.com/gin-gonic/gin"//自定义api错误结构体type ApiError struct { Status int `json:"-"` Code int `json:"code"` Message string `json:"message"`}func (err ApiError)Error() string { return err.Message}2.编写统一错误处理函数pkg/e/error_wrapper.go package eimport ( "github.com/gin-gonic/gin" "net/http")type WrapperHandle func(c *gin.Context) (interface{}, error)func ErrorWrapper(handle WrapperHandle) gin.HandlerFunc { return func(c *gin.Context) { data, err := handle(c) if err != nil { apiError := err.(ApiError) c.JSON(apiError.Status, apiError) return } c.JSON(http.StatusOK, gin.H{"data": data}) }}这里的统一处理只是一个简单的处理,介绍一种思路,具体的可以根据项目进行改造3.例子编写控制器api/err_handle.go package apiimport ( "cn.sockstack/gin_demo/pkg/e" "github.com/gin-gonic/gin" "net/http")func ErrorHandle(c *gin.Context) (interface{}, error) { query := c.Query("q") if query == "" { return nil, e.ApiError{ Status: http.StatusOK, //状态码 Code: 404, Message: "q的参数不能为空", } } if query == "test" { return nil, e.ApiError{ Status: http.StatusOK, //状态码 Code: 404, Message: "q的参数不能为test", } } return query, nil}注册路由routers/test.go ...

June 9, 2020 · 1 min · jiezi

gin请求参数处理

本章介绍Gin框架获取请求参数的方式 1.获取Get 请求参数Get请求url例子:/path?id=1234&name=Manu&value=111 获取Get请求参数的常用函数: func (c *Context) Query(key string) stringfunc (c *Context) DefaultQuery(key, defaultValue string) stringfunc (c *Context) GetQuery(key string) (string, bool)例子: func Handler(c *gin.Context) { //获取name参数, 通过Query获取的参数值是String类型。 name := c.Query("name") //获取name参数, 跟Query函数的区别是,可以通过第二个参数设置默认值。 name := c.DefaultQuery("name", "sockstack") //获取id参数, 通过GetQuery获取的参数值也是String类型, // 区别是GetQuery返回两个参数,第一个是参数值,第二个参数是参数是否存在的bool值,可以用来判断参数是否存在。 id, ok := c.GetQuery("id") if !ok { // 参数不存在 }}提示:GetQuery函数,判断参数是否存在的逻辑是,参数值为空,参数也算存在,只有没有提交参数,才算参数不存在。2.获取Post请求参数获取Post请求参数的常用函数: func (c *Context) PostForm(key string) stringfunc (c *Context) DefaultPostForm(key, defaultValue string) stringfunc (c *Context) GetPostForm(key string) (string, bool)例子: ...

June 9, 2020 · 1 min · jiezi

参数校验错误信息中文处理

在上一节我们介绍到,gin可以使用ShouldBind方法把参数绑定到结构体,但是没有介绍到参数校验的方式,这节我们来介绍参数校验和校验失败后转换成中文返回前端。 1.数据校验下面我们开始一个简单的例子: 在根目录的requests目录下新建一个test_request.gopackage requests//测试请求结构体 该结构体定义了请求的参数和校验规则type TestRequest struct { Username string `form:"username" binding:"required"`}在根目录的api目录下新建一个test.go的控制器,定义test控制器package apiimport ( "cn.sockstack/gin_demo/requests" "github.com/gin-gonic/gin" "net/http")func Test(c *gin.Context) { //实例化一个TestRequest结构体,用于接收参数 testStruct := requests.TestRequest{} //接收请求参数 err := c.ShouldBind(&testStruct) //判断参数校验是否通过,如果不通过,把错误返回给前端 if err != nil { c.JSON(http.StatusOK, gin.H{"error": err.Error()}) return } //校验通过,返回请求参数 c.JSON(http.StatusOK, gin.H{"params": testStruct})}在根目录的routers下定义/test路由,分别新建init.go和test.go文件初始化路由.test.go package routersimport ( "cn.sockstack/gin_demo/api" "github.com/gin-gonic/gin")func test(r *gin.Engine) { //定义/test路由 r.GET("/test", api.Test)}init.go package routersimport "github.com/gin-gonic/gin"func Init(r *gin.Engine) { //注册test路由 test(r)}在main.go中注册路由package main// 导入gin包import ( "cn.sockstack/gin_demo/pkg/config" "cn.sockstack/gin_demo/routers" "fmt" "github.com/gin-gonic/gin")// 入口函数func main() { // 初始化一个http服务对象 r := gin.Default() //注册路由 routers.Init(r) r.Run(fmt.Sprintf("%s:%d", config.Server.Address, config.Server.Port)) // 监听并在 0.0.0.0:8081 上启动服务}运行并访问localhost:8081/test,不带参数会报错{ "error": "Key: 'TestRequest.Username' Error:Field validation for 'Username' failed on the 'required' tag"}运行并访问localhost:8081/test?username=sockstack,则返回响应的参数。{ "params": { "Username": "sockstack" }}上面的例子已经可以实现参数校验和接收参数了,但是校验不通过的时候返回的提示是英文的,下面我们介绍一下怎么把错误转成中文返回。 ...

June 9, 2020 · 3 min · jiezi

gin的控制器与路由

1.概述路由是一个过程,指的是一个http请求,如何找到对应的处理器函数(也可以叫控制器函数),Gin框架的路由是基于httprouter包实现的。控制器是在路由完成了URL检测和路由检测之后,路由器会分发请求到对应的路由地址,这也是应用请求的生命周期中最重要的一个环节。在这一步骤中,完成应用的业务逻辑及数据返回。 2.路由定义2.1.http请求方法常用的http请求方法有下面4种: GETPOSTPUTDELETE2.2.url路径gin框架,url路径有三种写法: 静态url路径带路径参数的url路径带星号(*)模糊匹配参数的url路径例子: // 例子1, 静态Url路径, 即不带任何参数的url路径/users/center/user/111/food/12// 例子2,带路径参数的url路径,url路径上面带有参数,参数由冒号(:)跟着一个字符串定义。// 路径参数值可以是数值,也可以是字符串//定义参数:id, 可以匹配/user/1, /user/899 /user/xiaoli 这类Url路径/user/:id//定义参数:id, 可以匹配/food/2, /food/100 /food/apple 这类Url路径/food/:id//定义参数:type和:page, 可以匹配/foods/2/1, /food/100/25 /food/apple/30 这类Url路径/foods/:type/:page// 例子3. 带星号(*)模糊匹配参数的url路径// 星号代表匹配任意路径的意思, 必须在*号后面指定一个参数名,后面可以通过这个参数获取*号匹配的内容。//以/foods/ 开头的所有路径都匹配//匹配:/foods/1, /foods/200, /foods/1/20, /foods/apple/1 /foods/*path//可以通过path参数获取*号匹配的内容。2.3.3.分组路由在做api开发的时候,如果要支持多个api版本,我们可以通过分组路由来实现api版本处理。 router := gin.Default()// 创建v1组v1 := router.Group("/v1"){ // 在v1这个分组下,注册路由 v1.POST("/login", loginEndpoint) v1.POST("/submit", submitEndpoint) v1.POST("/read", readEndpoint)}// 创建v2组v2 := router.Group("/v2"){ // 在v2这个分组下,注册路由 v2.POST("/login", loginEndpoint) v2.POST("/submit", submitEndpoint) v2.POST("/read", readEndpoint)}上面的例子将会注册下面的路由信息: /v1/login/v1/submit/v1/read/v2/login/v2/submit/v2/read路由分组,其实就是设置了同一类路由的url前缀。 3.控制器路由需要配合控制器才能完成一次请求,下面我们来看一下控制器的定义。 控制器函数定义: func HandlerFunc(c *gin.Context)控制器函数接受一个上下文参数。可以通过上下文参数,获取http请求参数,响应http请求。 下面我们通过一个例子看一下控制器的定义: //实例化gin实例对象。r := gin.Default() //定义post请求, url路径为:/users, 绑定saveUser控制器函数r.POST("/users", saveUser)//定义get请求,url路径为:/users/:id (:id是参数,例如: /users/10, 会匹配这个url模式),绑定getUser控制器函数r.GET("/users/:id", getUser)//定义put请求r.PUT("/users/:id", updateUser)//定义delete请求r.DELETE("/users/:id", deleteUser)//控制器函数实现func saveUser(c *gin.Context) { ...忽略实现...}func getUser(c *gin.Context) { ...忽略实现...}func updateUser(c *gin.Context) { ...忽略实现...}func deleteUser(c *gin.Context) { ...忽略实现...}提示:实际项目开发中不要把路由定义和控制器函数都写在一个go文件,不方便维护,可以参考项目结构,规划自己的业务模块。

June 8, 2020 · 1 min · jiezi

gin项目配置

1.概述项目配置是整个项目中很重要的一部分,一般项目的配置有数据库配置,应用配置(地址,端口等),缓存配置,第三方扩展的配置,中间件配置等等,可见配置在一个项目中的地位是很重要的,但是,gin中没有提供相关的配置管理的组件,我们可以使用go的第三方包来做配置管理,集成到gin中。 常用的第三方包有: iniyamlviper....本教程主要讲解ini,其他的请执行Google 2.ini的使用目录结构我们使用前面推荐项目结构 安装gopkg.in/ini.v1go get gopkg.in/ini.v1在conf下创建app.ini配置文件[server]address = 0.0.0.0port = 8080在pkg中创建config目录,并创建server.go,配置Server配置package configimport ( "cn.sockstack/gin_demo/pkg/helper" "gopkg.in/ini.v1")var Server *servertype server struct { Address string Port int source *ini.File}func (s *server) Load(path string) *server { var err error //判断配置文件是否存在 exists, err := helper.PathExists(path) if !exists { return s } s.source, err = ini.Load(path) if err != nil { panic(err) } return s}func (s *server)Init() *server { //判断配置是否加载成功 if s.source == nil { return s } s.Address = s.source.Section("server").Key("address").MustString("0.0.0.0") s.Port = s.source.Section("server").Key("port").MustInt(8080) return s}注意:helper.PathExists(path)是pkg/helper/util.go的工具方法,实现如下 ...

June 8, 2020 · 2 min · jiezi

项目结构设置

1.概述实际项目业务功能和模块会很多,我们不可能把所有代码都写在一个go文件里面或者写在一个main入口函数里面;我们需要对项目结构做一些规划,方便维护代码以及扩展。 Gin框没有对项目结构做出限制,我们可以根据自己项目需要自行设计。2.项目结构有视图模板├── conf #项目配置文件目录│ └── config.toml #大家可以选择自己熟悉的配置文件管理工具包例如:toml、xml等等├── controllers #控制器目录,按模块存放控制器(或者叫控制器函数),必要的时候可以继续划分子目录。│ └── user.go├── models #模型目录,负责项目的数据存储部分,例如各个模块的Mysql表的读写模型。│ ├── food.go│ └── user.go├── static #静态资源目录,包括Js,css,jpg等等,可以通过Gin框架配置,直接让用户访问。│ ├── css│ ├── images│ └── js├── logs #日志文件目录,主要保存项目运行过程中产生的日志。└── views #视图模板目录,存放各个模块的视图模板,当然有些项目只有api,是不需要视图部分,可以忽略这个目录│ └── index.html├── main.go #项目入口,这里负责Gin框架的初始化,注册路由信息,关联控制器函数等。api开发├── conf #项目配置文件目录│ └── config.toml #大家可以选择自己熟悉的配置文件管理工具包例如:toml、xml等等├── controllers #控制器目录,按模块存放控制器(或者叫控制器函数),必要的时候可以继续划分子目录。│ └── user.go├── models #模型目录,负责项目的数据存储部分,例如各个模块的Mysql表的读写模型。│ ├── food.go│ ├── user.go│ └── init.go #模型初始化├── logs #日志文件目录,主要保存项目运行过程中产生的日志。├── main.go #项目入口,这里负责Gin框架的初始化,注册路由信息,关联控制器函数等。以上两种目录结构基本上大同小异,一般的项目构建都可以使用这样的项目结构,清晰明了。 3.项目结构扩展对于一般的项目都是使用上面的目录结构,但是有时候项目复杂了,会进一步拆分,把数据校验,模型定义,数据操作,服务,响应进行解耦。 数据校验->控制器->调用服务->数据操作->数据模型->数据响应目录结构如下: ├── conf #项目配置文件目录│ └── config.toml #大家可以选择自己熟悉的配置文件管理工具包例如:toml、xml、ini等等├── requests #定义入参即入参校验规则│ └── user_request.go│ └── food_request.go├── responses #定义响应的数据│ └── user_response.go│ └── food_response.go├── services #服务定义目录| └── v1 #服务v1版本│ | └── user_service.go│ | └── food_service.go| └── v2 #服务v2版本│ | └── user_service.go│ | └── food_service.go├── api #api目录,按模块存放控制器(或者叫控制器函数),必要的时候可以继续划分子目录。│ └── v1 #apiv1版本│ | └── user.go│ | └── food.go│ └── v2 #apiv2版本│ | └── user.go│ | └── food.go├── router #路由目录│ └── v1 #路由v1版本│ | └── user.go│ | └── food.go│ └── v2 #路由v2版本│ | └── user.go│ | └── food.go├── init.go #路由初始化├── pkg #自定义的工具类等│ └── e #项目统一的响应定义,如错误码,通用的错误信息,响应的结构体│ └── util #工具类目录├── models #模型目录,负责项目的数据存储部分,例如各个模块的Mysql表的读写模型。│ ├── food.go│ ├── user.go│ └── init.go #模型初始化├── repositories #数据操作层,定义各种数据操作。│ └── user_repository.go│ └── food_repository.go├── logs #日志文件目录,主要保存项目运行过程中产生的日志。├── main.go #项目入口,这里负责Gin框架的初始化,注册路由信息,关联控制器函数等。通过上面的项目结构扩展,增加了代码量,但是把各层的职责界定了,每一层的只做每一层的事情,数据解耦 ...

June 8, 2020 · 1 min · jiezi