首页
友情链接
统计
关于我
更多
留言板
Search
1
多目标优化问题及两种常用解法
608 阅读
2
SNN & NoC仿真器收集
171 阅读
3
Windows常见问题记录
121 阅读
4
Leetcode刷题 - KMP算法
111 阅读
5
SNN映射论文阅读-SentryOS
96 阅读
未分类
论文笔记
linux
go学习
verilog
systemc
算法学习
latex
C++
Redis
登录
Search
标签搜索
redis
Lisheng Xie
累计撰写
31
篇文章
累计收到
6
条评论
首页
栏目
未分类
论文笔记
linux
go学习
verilog
systemc
算法学习
latex
C++
Redis
页面
友情链接
统计
关于我
留言板
搜索到
2
篇与
的结果
2023-01-24
go-gin-chat项目学习
项目地址:https://github.com/hezhizheng/go-gin-chatGin框架学习参考笔记Websocket学习Websocket连接是升级的HTTP连接,在连接被客户端或服务器终止之前一直存在,基于Websocket可以实现客户端和服务端之间的双工通信,而不需要对HTTP端口进行持续轮询,大大节省了网络资源开销。go-gin-chat中基于gorilla/websocket实现websocket的使用,下面是该软件包使用的示例,可以参考博客。// We'll need to define an Upgrader // this will require a Read and Write buffer size var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, } func wsEndpoint(w http.ResponseWriter, r *http.Request) { upgrader.CheckOrigin = func(r *http.Request) bool { return true } // upgrade this connection to a WebSocket connection ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) } // 向连接的客户端写入信息 log.Println("Client Connected") err = ws.WriteMessage(1, []byte("Hi Client!")) if err != nil { log.Println(err) } // listen indefinitely for new messages coming // through on our WebSocket connection go reader(ws) } // define a reader which will listen for // new messages being sent to our WebSocket // endpoint func reader(conn *websocket.Conn) { for { // read in a message messageType, p, err := conn.ReadMessage() if err != nil { log.Println(err) return } // print out that message for clarity fmt.Println(string(p)) if err := conn.WriteMessage(messageType, p); err != nil { log.Println(err) return } } } Websocket心跳检测参考博客 https://www.cnblogs.com/tugenhua0707/p/8648044.html在使用websocket的过程中,如果网络断开而服务器没有触发onclose事件,那么服务器会继续向客户端发送数据,这些数据将被丢失。需要心跳机制来检测客户端和服务端之间是否处于正常的链接状态。心跳机制的大致实现方式为:每隔一段时间客户端向服务器发送一个数据包,通知服务器自己还活着,如果链接还存在服务器会发送一个数据包给客户端,客户端确认服务端也活着,否则的话可能是链接已经断开,需要重新链接。发送顺序也可以反过来由服务器向客户端发送ping信息,由客户端反馈pong信息。go-gin-chat中使用了https://github.com/zimv/websocket-heartbeat-js中提供的心跳检测方法,由服务段发送ping信息("heartbeat"),客户端接收到这个ping信息后返回一个pong信息,服务端重置心跳倒计时。postman使用进入postman网站注册用户并下载客户端,这里是使用postman进行POST请求提交json格式文件的示例。
2023年01月24日
19 阅读
0 评论
0 点赞
2023-01-22
Gin框架学习
概述Gin框架地址:https://github.com/gin-gonic/gin 参考文档: https://www.kancloud.cn/shuangdeyu/gin_book https://github.com/piexlmax/1010classGin服务入门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 } Gin获取Get/Post参数获取Get参数func main() { router := gin.Default() // 匹配的url格式: /welcome?firstname=Jane&lastname=Doe router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") // DefaultQuery可以在参数值不存在时使用传入的默认值 lastname := c.Query("lastname") // 是 c.Request.URL.Query().Get("lastname") 的简写 c.String(http.StatusOK, "Hello %s %s", firstname, lastname) }) router.Run(":8080") } 获取Post参数func main() { router := gin.Default() router.POST("/form_post", func(c *gin.Context) { message := c.PostForm("message") nick := c.DefaultPostForm("nick", "anonymous") // 此方法可以设置默认值 c.JSON(200, gin.H{ "status": "posted", "message": message, "nick": nick, }) }) router.Run(":8080") } Get和Post参数混合使用 router := gin.Default() router.POST("/post", func(c *gin.Context) { id := c.Query("id") page := c.DefaultQuery("page", "0") name := c.PostForm("name") message := c.PostForm("message") fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, message) }) router.Run(":8080") } 请求示例:POST /post?id=1234&page=1 HTTP/1.1 Content-Type: application/x-www-form-urlencoded name=manu&message=this_is_great Bind绑定参数&参数验证为了将请求主体绑定到结构体中,需要使用模型绑定,Gin当前支持JSON、XML、YAML和标准表单值(foo=bar&boo=baz)的绑定。可以使用Must Bind和Should Bind两种绑定方式,推荐使用Should Bind方式 Methods: ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAML, 后几种基于ShouldBind实现相应的行为,可以指定绑定的数据格式,发生错误时会将错误返回供后续处理。下面给出了绑定json格式的示例 // 绑定为json type Login struct { User string `form:"user" json:"user" xml:"user" binding:"required"` Password string `form:"password" json:"password" xml:"password" binding:"required"` } func main() { router := gin.Default() // Example for binding JSON ({"user": "manu", "password": "123"}) router.POST("/loginJSON", func(c *gin.Context) { var json Login if err := c.ShouldBindJSON(&json); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if json.User != "manu" || json.Password != "123" { c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) return } c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) }) router.Run(":8080") } 绑定form,uri,xml格式时可以在结构体后的的修饰符中使用相应的字段。 参数验证一个字段用binding:"required"修饰,在绑定时如果该字段的值为空,那么将返回一个错误。可以在binding后使用自定义的验证函数,可以参考文档中提供的自定义验证的示例,需要注意文档中使用的验证器版本是v8,实际使用时版本不一致需要做出更改,可以参考视频教程。 Gin文件上传和返回文件读取上传文件的文件名可以由用户自定义,所以可能包含非法字符串,为了安全起见,应该由服务端统一文件名规则 上传文件的示例代码如下所示:单文件上传func main() { router := gin.Default() // 给表单限制上传大小 (默认 32 MiB) // router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { // 单文件 file, _ := c.FormFile("file") log.Println(file.Filename) // 上传文件到指定的路径 c.SaveUploadedFile(file, "./saved.txt") c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) }) router.Run(":8080") } 使用FormFile接收单文件,使用Postman测试文件上传时,需要先设置Header中Content-Type为multipart/form-data,随后在Body中选择上传form-data类型的键值对,在具体的值中选择文件,同时需要注意Posterman上传文件时工作路径的设置。多文件上传主要区别在于先获取文件列表,再依次对每个文件进行处理。func main() { router := gin.Default() // 给表单限制上传大小 (默认 32 MiB) // router.MaxMultipartMemory = 8 << 20 // 8 MiB router.POST("/upload", func(c *gin.Context) { // 多文件 form, _ := c.MultipartForm() files := form.File["file"] for _, file := range files { log.Println(file.Filename) // 上传文件到指定的路径 // c.SaveUploadedFile(file, dst) } c.String(http.StatusOK, fmt.Sprintf("%d files uploaded!", len(files))) }) router.Run(":8080") } 文件返回// Header添加文件相关信息,不加也可以跑? c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", file.Filename)) // 在服务器文件路径下启动相应的服务 c.File(dst) 路由分组和中间件路由分组对router创建Group就是分组,同一分组中会拥有同一前缀和同一中间件,分组可以使得路由结构更加清晰,更加方便管理路由。路由分组实例如下所示:func main() { router := gin.Default() // Simple group: v1 v1 := router.Group("/v1") { v1.POST("/login", loginEndpoint) v1.POST("/submit", submitEndpoint) v1.POST("/read", readEndpoint) } router.Run(":8080") } 中间件在请求到达路由的方法的前和后进行的一系列操作,在路由(路由组)上进行Use操作,后面传入中间件函数即可。// HandlerFunc defines the handler used by gin middleware as return value. type HandlerFunc func(*Context) HandlerFunc是一个函数类型,接收一个Context参数。用于编写程序处理函数并返回HandleFunc类型,作为中间件的定义。gin.Default()中默认已经使用了Recovery和Logger两个中间件,无中间件启动服务可以使用gin.New()方法。下面给出了Gin自定义中间件使用的实例func Logger() gin.HandlerFunc { return func(c *gin.Context) { t := time.Now() // Set example variable c.Set("example", "12345") // before request c.Next() // after request latency := time.Since(t) log.Print(latency) // access the status we are sending status := c.Writer.Status() log.Println(status) } } func main() { r := gin.New() r.Use(Logger()) r.GET("/test", func(c *gin.Context) { example := c.MustGet("example").(string) // it would print: "12345" log.Println(example) }) // Listen and serve on 0.0.0.0:8080 r.Run(":8080") } 中间件中context.Next函数可以将中间件代码的执行顺序一分为二,Next函数调用之前的代码在请求处理之前之前,当程序执行到context.Next时,会中断向下执行,转而先去执行具体的业务逻辑,执行完业务逻辑处理函数之后,程序会再次回到context.Next处,继续执行中间件中后续的代码。使用Use方法调用自定义的中间件,可以在路由或路由组后调用,需要注意的是,调用多个中间件时可以在一个Use方法中依次写入,也可以连续使用多个Use方法。多个中间件的代码执行顺序可以参考洋葱中间件模型。Gin日志可以参考文档相关章节。日志可以用来记录参数信息,猜测用户行为,尝试复现bug并修复,推荐使用第三方的日志工具go-logging,logrus等,对于大量的日志,可以使用go-file-rotatelogs, file-rotatelogs对日志自行按照时间进行切割等。记录用户的输入输出信息常使用中间件实现。Gin数据库使用链接和使用MySQL数据库import _ "github.com/go-sql-driver/mysql" connStr := "root:password@tcp(127.0.0.1:3306)/ginsql" db, err := sql.Open("mysql", connStr) if err != nil { log.Fatal(err.Error()) return } ORM进行数据库操作ORM是一种数据库辅助工具,可以在go结构体和数据库之间产生映射,基于结构体即可完成增删改查操作。可用的ORM框架有GORM,gorose和xorm等。GORM链接数据库GORM参考文档如下: https://learnku.com/docs/gorm/v2/index/9728 http://gorm.book.jasperxu.com/ (好像无法访问了) https://gorm.io/zh_CN/docs/connecting_to_the_database.html https://books.studygolang.com/gorm/ 1. 导入gorm 2. 导入mysql驱动器 3. 使用Open方法链接得到数据库对象,以MySQL为例dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) // 最后关闭数据库链接 defer db.Close() 加载静态资源&HTML渲染设置静态文件路径func main() { router := gin.Default() router.Static("/assets", "./assets") router.StaticFS("/more_static", http.Dir("my_file_system")) router.StaticFile("/favicon.ico", "./resources/favicon.ico") // Listen and serve on 0.0.0.0:8080 router.Run(":8080") } HTML渲染func 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.tmpl", gin.H{ "title": "Main website", }) }) router.Run(":8080") } <html> <h1> {{ .title }} </h1> </html>
2023年01月22日
35 阅读
2 评论
0 点赞