• 【GO语言编程】(四)


    http编程

     package main
    
     import (
     "fmt"
     "net/http"
     )
    
     func main() {
     //http://127.0.0.1:8000/go
     // 单独写回调函数
     http.HandleFunc("/go", myHandler)
     //http.HandleFunc("/ungo",myHandler2 )
     // addr:监听的地址
    // handler:回调函数
     http.ListenAndServe("127.0.0.1:8000", nil)
     }
    
     // handler函数
     func myHandler(w http.ResponseWriter, r *http.Request) {
     fmt.Println(r.RemoteAddr, "连接成功")
    
    // 请求方式:GET POST DELETE PUT UPDATE
     fmt.Println("method:", r.Method)
     // /go
     fmt.Println("url:", r.URL.Path)
     fmt.Println("header:", r.Header)
     fmt.Println("body:", r.Body)
     // 回复
     w.Write([]byte("www.5lmh.com"))
    }
    

    请添加图片描述

    package main
    
     import (
     "fmt"
     "io"
     "net/http"
     )
    
     func main() {
     //resp, _ := http.Get("http://www.baidu.com")
     //fmt.Println(resp)
     resp, _ := http.Get("http://127.0.0.1:8000/go")
     defer resp.Body.Close()
     // 200 OK
     fmt.Println(resp.Status)
     fmt.Println(resp.Header)
    
     buf := make([]byte, 1024)
     for {
     // 接收服务端信息
     n, err := resp.Body.Read(buf)
     if err != nil && err != io.EOF {
     fmt.Println(err)
     return
     } else {
     fmt.Println("读取完毕")
     res := string(buf[:n])
     fmt.Println(res)
     break
     }
    
     }
     }
    

    请添加图片描述
    请添加图片描述

    go操作MYSQL

    Mysql-install

    请添加图片描述

    https://developer.aliyun.com/article/758177#:~:text=%E5%A6%82%E4%BD%95%E5%9C%A8%20Ubuntu%2020.04%20%E4%B8%8A%E5%AE%89%E8%A3%85%20MySQL%201%20%E4%B8%80%E3%80%81%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6%20%E7%A1%AE%E4%BF%9D%E4%BD%A0%E4%BB%A5,5%20%E4%BA%94%E3%80%81%E6%80%BB%E7%BB%93%20%E6%88%91%E4%BB%AC%E5%B7%B2%E7%BB%8F%E5%90%91%E4%BD%A0%E5%B1%95%E7%A4%BA%E5%A6%82%E4%BD%95%E5%9C%A8%20Ubuntu%2020.04%20%E4%B8%8A%E5%AE%89%E8%A3%85%20MySQL%E3%80%82%20

    请添加图片描述
    请添加图片描述

    请添加图片描述
    请添加图片描述
    一旦安装完成,MySQL 服务将会自动启动。想要验证 MySQL 服务器正在运行,输入:

    sudo systemctl status mysql
    

    请添加图片描述

    ● mysql.service - MySQL Community Server
         Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
         Active: active (running) since Sun 2022-09-25 17:18:35 CST; 2min 10s ago
       Main PID: 20386 (mysqld)
         Status: "Server is operational"
          Tasks: 38 (limit: 9294)
         Memory: 360.0M
         CGroup: /system.slice/mysql.service
                 └─20386 /usr/sbin/mysqld
    
    925 17:18:27 algernon-Lenovo-Legion-Y7000 systemd[1]: Starting MySQL Community Server...
    925 17:18:35 algernon-Lenovo-Legion-Y7000 systemd[1]: Started MySQL Community Server.
    
    

    请添加图片描述
    请添加图片描述

    Mysql使用

    新建test数据库,person、place 表

     CREATE TABLE `person` (
     `user_id` int(11) NOT NULL AUTO_INCREMENT,
     `username` varchar(260) DEFAULT NULL,
     `sex` varchar(260) DEFAULT NULL,
     `email` varchar(260) DEFAULT NULL,
     PRIMARY KEY (`user_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    
     CREATE TABLE place (
     country varchar(200),
     city varchar(200),
     telcode int
     )ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    

    请添加图片描述

     mysql> desc person;
     +----------+--------------+------+-----+---------+----------------+
     | Field | Type | Null | Key | Default | Extra |
     +----------+--------------+------+-----+---------+----------------+
     | user_id | int(11) | NO | PRI | NULL | auto_increment |
     | username | varchar(260) | YES | | NULL | |
     | sex | varchar(260) | YES | | NULL | |
     | email | varchar(260) | YES | | NULL | |
     +----------+--------------+------+-----+---------+----------------+
     4 rows in set (0.00 sec)
    
     mysql> desc place;
     +---------+--------------+------+-----+---------+-------+
     | Field | Type | Null | Key | Default | Extra |
     +---------+--------------+------+-----+---------+-------+
     | country | varchar(200) | YES | | NULL | |
     | city | varchar(200) | YES | | NULL | |
     | telcode | int(11) | YES | | NULL | |
     +---------+--------------+------+-----+---------+-------+
     3 rows in set (0.01 sec)
    

    https://blog.csdn.net/kuangshp128/article/details/115816597
    请添加图片描述

    +----------+--------------+------+-----+---------+----------------+
    | Field    | Type         | Null | Key | Default | Extra          |
    +----------+--------------+------+-----+---------+----------------+
    | user_id  | int          | NO   | PRI | NULL    | auto_increment |
    | username | varchar(260) | YES  |     | NULL    |                |
    | sex      | varchar(260) | YES  |     | NULL    |                |
    | email    | varchar(260) | YES  |     | NULL    |                |
    +----------+--------------+------+-----+---------+----------------+
    4 rows in set (0.01 sec)
    
    mysql> desc place;
    +---------+--------------+------+-----+---------+-------+
    | Field   | Type         | Null | Key | Default | Extra |
    +---------+--------------+------+-----+---------+-------+
    | country | varchar(200) | YES  |     | NULL    |       |
    | city    | varchar(200) | YES  |     | NULL    |       |
    | telcode | int          | YES  |     | NULL    |       |
    +---------+--------------+------+-----+---------+-------+
    3 rows in set (0.01 sec)
    
    
    
    mysql使用

    使用第三方开源的mysql库: github.com/go-sql-driver/mysql (mysql驱动)
    github.com/jmoiron/sqlx (基于mysql驱动的封装)

     go get github.com/go-sql-driver/mysql
     go get github.com/jmoiron/sqlx
    

    链接mysql

     database, err := sqlx.Open("mysql", "root:XXXX@tcp(127.0.0.1:3306)/test")
     //database, err := sqlx.Open("数据库类型", "用户名:密码@tcp(地址:端口)/数据库名")
     database, err := sqlx.Open("mysql", "debian-sys-maint:ACrhxSvWeDh0z8Km@tcp(127.0.0.1:3306)/test")
    
    Insert操作
     package main
    
     import (
     "fmt"
     _ "github.com/go-sql-driver/mysql"
     "github.com/jmoiron/sqlx"
     )
    
     type Person struct {
     UserId int `db:"user_id"`
     Username string `db:"username"`
     Sex string `db:"sex"`
     Email string `db:"email"`
     }
    
     type Place struct {
     Country string `db:"country"`
     City string `db:"city"`
     TelCode int `db:"telcode"`
     }
    
     var Db *sqlx.DB
    
     func init() {
     database, err := sqlx.Open("mysql", "debian-sys-maint:ACrhxSvWeDh0z8Km@tcp(127.0.0.1:3306)/test")
     if err != nil {
     fmt.Println("open mysql failed,", err)
     return
     }
     Db = database
     }
    
     func main() {
    
    r, err := Db.Exec("insert into person(username, sex, email)values(?, ?,
    ?)", "stu001", "man", "stu01@qq.com")
     if err != nil {
     fmt.Println("exec failed, ", err)
     return
     }
     id, err := r.LastInsertId()
     if err != nil {
     fmt.Println("exec failed, ", err)
     return
     }
    
     fmt.Println("insert succ:", id)
     }
    

    请添加图片描述
    请添加图片描述

    Select操作
     package main
    
     import (
     "fmt"
    
     _ "github.com/go-sql-driver/mysql"
     "github.com/jmoiron/sqlx"
     )
    
     type Person struct {
     UserId int `db:"user_id"`
     Username string `db:"username"`
     Sex string `db:"sex"`
     Email string `db:"email"`
     }
     type Place struct {
     Country string `db:"country"`
     City string `db:"city"`
     TelCode int `db:"telcode"`
     }
    
     var Db *sqlx.DB
    
     func init() {
    
     database, err := sqlx.Open("mysql", "debian-sys-maint:ACrhxSvWeDh0z8Km@tcp(127.0.0.1:3306)/test")
     if err != nil {
     fmt.Println("open mysql failed,", err)
     return
     }
    
     Db = database
     }
    
     func main() {
    
     var person []Person
    
    err := Db.Select(&person, "select user_id, username, sex, email from person
    where user_id=?", 1)
     if err != nil {
     fmt.Println("exec failed, ", err)
     return
     }
    
     fmt.Println("select succ:", person)
     }
    
    update操作
     package main
    
     import (
     "fmt"
    
     _ "github.com/go-sql-driver/mysql"
     "github.com/jmoiron/sqlx"
     )
    
     type Person struct {
     UserId int `db:"user_id"`
     Username string `db:"username"`
     Sex string `db:"sex"`
     Email string `db:"email"`
     }
    
     type Place struct {
     Country string `db:"country"`
     City string `db:"city"`
     TelCode int `db:"telcode"`
     }
    
     var Db *sqlx.DB
    
     func init() {
    
     database, err := sqlx.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
     if err != nil {
     fmt.Println("open mysql failed,", err)
     return
     }
    
     Db = database
     }
    
     func main() {
    
    
    res, err := Db.Exec("update person set username=? where user_id=?",
    "stu0003", 1)
     if err != nil {
     fmt.Println("exec failed, ", err)
     return
     }
     row, err := res.RowsAffected()
     if err != nil {
     fmt.Println("rows failed, ",err)
     }
     fmt.Println("update succ:",row)
    
     }
    
    delete操作
     package main
    
     import (
     "fmt"
    
     _ "github.com/go-sql-driver/mysql"
     "github.com/jmoiron/sqlx"
     )
    
     type Person struct {
     UserId int `db:"user_id"`
     Username string `db:"username"`
     Sex string `db:"sex"`
     Email string `db:"email"`
     }
    
     type Place struct {
     Country string `db:"country"`
     City string `db:"city"`
     TelCode int `db:"telcode"`
     }
    
     var Db *sqlx.DB
    
     func init() {
    
     database, err := sqlx.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
     if err != nil {
     fmt.Println("open mysql failed,", err)
     return
     }
    
     Db = database
     }
    
     func main() {
    
     /*
     _, err := Db.Exec("delete from person where user_id=?", 1)
     if err != nil {
     fmt.Println("exec failed, ", err)
     return
     }
     */
    
     res, err := Db.Exec("delete from person where user_id=?", 1)
     if err != nil {
     fmt.Println("exec failed, ", err)
     return
     }
    
     row,err := res.RowsAffected()
     if err != nil {
     fmt.Println("rows failed, ",err)
     }
    
     fmt.Println("delete succ: ",row)
     }
    
    Mysql事务
     package main
    
     import (
     "fmt"
    
     _ "github.com/go-sql-driver/mysql"
     "github.com/jmoiron/sqlx"
     )
    
     type Person struct {
     UserId int `db:"user_id"`
     Username string `db:"username"`
     Sex string `db:"sex"`
     Email string `db:"email"`
     }
    
     type Place struct {
     Country string `db:"country"`
     City string `db:"city"`
     TelCode int `db:"telcode"`
     }
    
     var Db *sqlx.DB
     func init() {
    
    database, err := sqlx.Open("mysql",
    "root:root@tcp(127.0.0.1:3306)/test")
     if err != nil {
     fmt.Println("open mysql failed,", err)
     return
     }
     Db = database
     }
    
     func main() {
     conn, err := Db.Begin()
     if err != nil {
     fmt.Println("begin failed :", err)
     return
     }
    
    
    r, err := conn.Exec("insert into person(username, sex, email)values(?,
    ?, ?)", "stu001", "man", "stu01@qq.com")
     if err != nil {
     fmt.Println("exec failed, ", err)
     conn.Rollback()
     return
     }
     id, err := r.LastInsertId()
     if err != nil {
     fmt.Println("exec failed, ", err)
     conn.Rollback()
     return
     }
     fmt.Println("insert succ:", id)
    
    
    r, err = conn.Exec("insert into person(username, sex, email)values(?,
    ?, ?)", "stu001", "man", "stu01@qq.com")
     if err != nil {
     fmt.Println("exec failed, ", err)
     conn.Rollback()
     return
     }
     id, err = r.LastInsertId()
     if err != nil {
     fmt.Println("exec failed, ", err)
     conn.Rollback()
     return
     }
     fmt.Println("insert succ:", id)
    
     conn.Commit()
     }
    

    请添加图片描述

    查看MySQL:

     mysql> select * from person;
     +---------+----------+------+--------------+
     | user_id | username | sex | email |
     +---------+----------+------+--------------+
     | 2 | stu001 | man | stu01@qq.com |
     | 3 | stu001 | man | stu01@qq.com |
     +---------+----------+------+--------------+
     2 rows in set (0.00 sec)
    

    Gin框架

    安装配置

    go get -u github.com/gin-gonic/gin
    import "github.com/gin-gonic/gin"
    
    
     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     // 1.创建路由
     r := gin.Default()
     // 2.绑定路由规则,执行的函数
     // gin.Context,封装了request和response
     r.GET("/", func(c *gin.Context) {
     c.String(http.StatusOK, "hello World!")
     })
     // 3.监听端口,默认在8080
    // Run("里面不指定端口号默认为8080")
     r.Run(":8000")
     }
    

    https://zhuanlan.zhihu.com/p/453462046

    https://blog.csdn.net/weixin_63943623/article/details/124245012

    请添加图片描述
    请添加图片描述
    请添加图片描述
    请添加图片描述

    gin路由

     package main
    
     import (
     "net/http"
     "strings"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     r.GET("/user/:name/*action", func(c *gin.Context) {
     name := c.Param("name")
     action := c.Param("action")
     //截取/
     action = strings.Trim(action, "/")
     c.String(http.StatusOK, name+" is "+action)
     })
     //默认为监听8080端口
     r.Run(":8000")
     }
    
     package main
    
     import (
     "fmt"
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     r.GET("/user", func(c *gin.Context) {
     //指定默认值
     //http://localhost:8080/user 才会打印出来默认的值
     name := c.DefaultQuery("name", "枯藤")
     c.String(http.StatusOK, fmt.Sprintf("hello %s", name))
     })
     r.Run()
     }
    

    请添加图片描述

     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>Document</title>
     </head>
     <body>
    
    <form action="http://localhost:8080/form" method="post"
    action="application/x-www-form-urlencoded">
    
    用户名:<input type="text" name="username" placeholder="请输入你的用户名">
    <br>&nbsp;&nbsp;&nbsp;码:<input type="password" name="userpassword"
    placeholder="请输入你的密码"> <br>
     <input type="submit" value="提交">
     </form>
     </body>
     </html>
    
    package main
    
     //
     import (
     "fmt"
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
    
     func main() {
     r := gin.Default()
     r.POST("/form", func(c *gin.Context) {
     types := c.DefaultPostForm("type", "post")
     username := c.PostForm("username")
     password := c.PostForm("userpassword")
    
    // c.String(http.StatusOK,
    fmt.Sprintf("username:%s,password:%s,type:%s", username, password, types))
    
    c.String(http.StatusOK, fmt.Sprintf("username:%s,password:%s,type:%s",
    username, password, types))
     })
     r.Run()
     }
    

    请添加图片描述
    请添加图片描述
    请添加图片描述

    上传单个文件
     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>Document</title>
     </head>
     <body>
    
    <form action="http://localhost:8080/upload" method="post"
    enctype="multipart/form-data">
     上传文件:<input type="file" name="file" >
     <input type="submit" value="提交">
     </form>
     </body>
     </html>
    
     package main
    
     import (
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     //限制上传最大尺寸
     r.MaxMultipartMemory = 8 << 20
     r.POST("/upload", func(c *gin.Context) {
     file, err := c.FormFile("file")
     if err != nil {
     c.String(500, "上传图片出错")
     }
     // c.JSON(200, gin.H{"message": file.Header.Context})
     c.SaveUploadedFile(file, file.Filename)
    
     c.String(http.StatusOK, file.Filename)
     })
     r.Run()
     }
    

    请添加图片描述
    请添加图片描述

    请添加图片描述

    上传特定文件
     package main
    
     import (
     "fmt"
     "log"
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     r.POST("/upload", func(c *gin.Context) {
     _, headers, err := c.Request.FormFile("file")
     if err != nil {
    log.Printf("Error when try to get file: %v", err)
     }
     //headers.Size 获取文件大小
     if headers.Size > 1024*1024*2 {
     fmt.Println("文件太大了")
     return
     }
     //headers.Header.Get("Content-Type")获取上传文件的类型
     if headers.Header.Get("Content-Type") != "image/png" {
     fmt.Println("只允许上传png图片")
     return
     }
     c.SaveUploadedFile(headers, "./video/"+headers.Filename)
     c.String(http.StatusOK, headers.Filename)
     })
     r.Run()
     }
    
    上传多个文件
     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>Document</title>
     </head>
     <body>
    
    <form action="http://localhost:8000/upload" method="post"
    enctype="multipart/form-data">
     上传文件:<input type="file" name="files" multiple>
     <input type="submit" value="提交">
     </form>
     </body>
     </html>
    
     package main
    
     import (
     "github.com/gin-gonic/gin"
     "net/http"
     "fmt"
     )
    
     // gin的helloWorld
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 限制表单上传大小 8MB,默认为32MB
     r.MaxMultipartMemory = 8 << 20
     r.POST("/upload", func(c *gin.Context) {
     form, err := c.MultipartForm()
     if err != nil {
    
    c.String(http.StatusBadRequest, fmt.Sprintf("get err %s",
    err.Error()))
     }
     // 获取所有图片
     files := form.File["files"]
     // 遍历所有图片
     for _, file := range files {
     // 逐个存
     if err := c.SaveUploadedFile(file, file.Filename); err != nil {
    
    c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s",
    err.Error()))
     return
     }
     }
     c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
     })
     //默认端口号是8080
     r.Run(":8000")
     }
    

    请添加图片描述
    请添加图片描述

    routes group
     package main
    
     import (
     "github.com/gin-gonic/gin"
     "fmt"
     )
    
    // gin的helloWorld
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 路由组1 ,处理GET请求
     v1 := r.Group("/v1")
     // {} 是书写规范
     {
     v1.GET("/login", login)
     v1.GET("submit", submit)
     }
     v2 := r.Group("/v2")
     {
     v2.POST("/login", login)
     v2.POST("/submit", submit)
     }
     r.Run(":8000")
     }
    
     func login(c *gin.Context) {
     name := c.DefaultQuery("name", "jack")
     c.String(200, fmt.Sprintf("hello %s\n", name))
     }
    
     func submit(c *gin.Context) {
     name := c.DefaultQuery("name", "lily")
     c.String(200, fmt.Sprintf("hello %s\n", name))
     }
    

    请添加图片描述

    路由拆分与注册

    基本的路由注册
     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func helloHandler(c *gin.Context) {
     c.JSON(http.StatusOK, gin.H{
     "message": "Hello www.topgoer.com!",
     })
     }
    
     func main() {
     r := gin.Default()
     r.GET("/topgoer", helloHandler)
     if err := r.Run(); err != nil {
     fmt.Println("startup service failed, err:%v\n", err)
     }
     }
    
    路由拆分成单独文件或包

    当项目的规模增大后就不太适合继续在项目的main.go文件中去实现路由注册相关逻辑了,我们会倾向
    于把路由部分的代码都拆分出来,形成一个单独的文件或包:
    我们在routers.go文件中定义并注册路由信息:

     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func helloHandler(c *gin.Context) {
     c.JSON(http.StatusOK, gin.H{
     "message": "Hello www.topgoer.com!",
     })
     }
    
     func setupRouter() *gin.Engine {
     r := gin.Default()
     r.GET("/topgoer", helloHandler)
     return r
     }
    

    此时main.go中调用上面定义好的setupRouter函数:

     func main() {
     r := setupRouter()
     if err := r.Run(); err != nil {
     fmt.Println("startup service failed, err:%v\n", err)
     }
     }
    

    routers/routers.go需要注意此时setupRouter需要改成首字母大写:

     package routers
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func helloHandler(c *gin.Context) {
     c.JSON(http.StatusOK, gin.H{
     "message": "Hello www.topgoer.com",
     })
     }
    
     // SetupRouter 配置路由信息
     func SetupRouter() *gin.Engine {
     r := gin.Default()
     r.GET("/topgoer", helloHandler)
     return r
     }
    

    main.go文件内容如下:

     package main
    
     import (
     "fmt"
     "gin_demo/routers"
     )
    
     func main() {
     r := routers.SetupRouter()
     if err := r.Run(); err != nil {
     fmt.Println("startup service failed, err:%v\n", err)
     }
     }
    
    路由拆分成多个文件

    我们可以分开定义多个路由文件,例如:

    1. gin_demo
    2. ├── go.mod
    3. ├── go.sum
    4. ├── main.go
    5. └── routers
    6. ├── blog.go
    7. └── shop.go
    

    routers/shop.go中添加一个LoadShop的函数,将shop相关的路由注册到指定的路由器:

     func LoadShop(e *gin.Engine) {
     e.GET("/hello", helloHandler)
     e.GET("/goods", goodsHandler)
     e.GET("/checkout", checkoutHandler)
     ...
     }
    

    routers/blog.go中添加一个LoadBlog的函数,将blog相关的路由注册到指定的路由器:

     func LoadBlog(e *gin.Engine) {
     e.GET("/post", postHandler)
     e.GET("/comment", commentHandler)
     ...
     }
    

    在main函数中实现最终的注册逻辑如下:

     func main() {
     r := gin.Default()
     routers.LoadBlog(r)
     routers.LoadShop(r)
     if err := r.Run(); err != nil {
     fmt.Println("startup service failed, err:%v\n", err)
      }
     }
    
    路由拆分到不同APP

    有时候项目规模实在太大,那么我们就更倾向于把业务拆分的更详细一些,例如把不同的业务代码拆分
    成不同的APP。
    因此我们在项目目录下单独定义一个app目录,用来存放我们不同业务线的代码文件,这样就很容易进
    行横向扩展。大致目录结构如下:

    1. gin_demo
    2. ├── app
    3. │ ├── blog
    4. │ │ ├── handler.go
    5. │ │ └── router.go
    6. │ └── shop
    7. │ ├── handler.go
    8. │ └── router.go
    9. ├── go.mod
    10. ├── go.sum
    11. ├── main.go
    12. └── routers
    13. └── routers.go
    

    其中app/blog/router.go用来定义post相关路由信息,具体内容如下:

     func Routers(e *gin.Engine) {
     e.GET("/post", postHandler)
     e.GET("/comment", commentHandler)
     }
    

    app/shop/router.go用来定义shop相关路由信息,具体内容如下:

     func Routers(e *gin.Engine) {
     e.GET("/goods", goodsHandler)
     e.GET("/checkout", checkoutHandler)
     }
    

    routers/routers.go中根据需要定义Include函数用来注册子app中定义的路由,Init函数用来进
    行路由的初始化操作:

     type Option func(*gin.Engine)
    
     var options = []Option{}
    
     // 注册app的路由配置
     func Include(opts ...Option) {
     options = append(options, opts...)
     }
    
     // 初始化
     func Init() *gin.Engine {
     r := gin.New()
     for _, opt := range options {
     opt(r)
     }
     return r
     }
    

    main.go中按如下方式先注册子app中的路由,然后再进行路由的初始化:

     func main() {
     // 加载多个APP的路由配置
     routers.Include(shop.Routers, blog.Routers)
     // 初始化路由
     r := routers.Init()
     if err := r.Run(); err != nil {
     fmt.Println("startup service failed, err:%v\n", err)
     }
     }
    

    gin数据解析与绑定

    Json 数据解析和绑定

    客户端传参,后端接收并解析到结构体:

     package main
    
     import (
     "github.com/gin-gonic/gin"
     "net/http"
     )
    
    // 定义接收数据的结构体
     type Login struct {
     // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
    
    User string `form:"username" json:"user" uri:"user" xml:"user"
    binding:"required"`
    
    Pssword string `form:"password" json:"password" uri:"password"
    xml:"password" binding:"required"`
     }
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // JSON绑定
     r.POST("loginJSON", func(c *gin.Context) {
     // 声明接收的变量
     var json Login
     // 将request的body中的数据,自动按照json格式解析到结构体
     if err := c.ShouldBindJSON(&json); err != nil {
     // 返回错误信息
     // gin.H封装了生成json数据的工具
     c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
     return
     }
     // 判断用户名密码是否正确
     if json.User != "root" || json.Pssword != "admin" {
     c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
     return
     }
     c.JSON(http.StatusOK, gin.H{"status": "200"})
     })
     r.Run(":8000")
     }
    

    请添加图片描述
    请添加图片描述

    表单数据解析和绑定
     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>Document</title>
     </head>
     <body>
    
    <form action="http://localhost:8000/loginForm" method="post"
    enctype="application/x-www-form-urlencoded">
     用户名<input type="text" name="username"><br>
     密码<input type="password" name="password">
     <input type="submit" value="提交">
     </form>
     </body>
     </html>
    
     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     // 定义接收数据的结构体
     type Login struct {
     // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
    
    User string `form:"username" json:"user" uri:"user" xml:"user"
    binding:"required"`
    
    Pssword string `form:"password" json:"password" uri:"password"
    xml:"password" binding:"required"`
     }
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // JSON绑定
     r.POST("/loginForm", func(c *gin.Context) {
     // 声明接收的变量
     var form Login
     // Bind()默认解析并绑定form格式
     // 根据请求头中content-type自动推断
     if err := c.Bind(&form); err != nil {
     c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
     return
     }
     // 判断用户名密码是否正确
     if form.User != "root" || form.Pssword != "admin" {
     c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
     return
     }
     c.JSON(http.StatusOK, gin.H{"status": "200"})
     })
     r.Run(":8000")
     }
    

    请添加图片描述

    URI数据解析和绑定
     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     // 定义接收数据的结构体
     type Login struct {
     // binding:"required"修饰的字段,若接收为空值,则报错,是必须字段
    
    User string `form:"username" json:"user" uri:"user" xml:"user"
    binding:"required"`
    
    Pssword string `form:"password" json:"password" uri:"password"
    xml:"password" binding:"required"`
     }
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // JSON绑定
     r.GET("/:user/:password", func(c *gin.Context) {
     // 声明接收的变量
     var login Login
     // Bind()默认解析并绑定form格式
     // 根据请求头中content-type自动推断
     if err := c.ShouldBindUri(&login); err != nil {
     c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
     return
      }
     // 判断用户名密码是否正确
     if login.User != "root" || login.Pssword != "admin" {
     c.JSON(http.StatusBadRequest, gin.H{"status": "304"})
     return
     }
     c.JSON(http.StatusOK, gin.H{"status": "200"})
     })
     r.Run(":8000")
     }
    

    请添加图片描述

    gin渲染

    各种数据格式的响应
     package main
    
     import (
     "github.com/gin-gonic/gin"
     "github.com/gin-gonic/gin/testdata/protoexample"
     )
    
     // 多种响应方式
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 1.json
     r.GET("/someJSON", func(c *gin.Context) {
     c.JSON(200, gin.H{"message": "someJSON", "status": 200})
     })
     // 2. 结构体响应
     r.GET("/someStruct", func(c *gin.Context) {
     var msg struct {
     Name string
     Message string
     Number int
     }
     msg.Name = "root"
     msg.Message = "message"
     msg.Number = 123
     c.JSON(200, msg)
     })
     // 3.XML
     r.GET("/someXML", func(c *gin.Context) {
     c.XML(200, gin.H{"message": "abc"})
     })
     // 4.YAML响应
     r.GET("/someYAML", func(c *gin.Context) {
     c.YAML(200, gin.H{"name": "zhangsan"})
     })
     // 5.protobuf格式,谷歌开发的高效存储读取的工具
     // 数组?切片?如果自己构建一个传输格式,应该是什么格式?
     r.GET("/someProtoBuf", func(c *gin.Context) {
     reps := []int64{int64(1), int64(2)}
     // 定义数据
     label := "label"
     // 传protobuf格式数据
     data := &protoexample.Test{
     Label: &label,
     Reps: reps,
     }
     c.ProtoBuf(200, data)
     })
    
     r.Run(":8000")
     }
    
    HTML模板渲染
     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     r.LoadHTMLGlob("tem/*")
     r.GET("/index", func(c *gin.Context) {
    
    c.HTML(http.StatusOK, "index.html", gin.H{"title": "我是测试", "ce":
    "123456"})
     })
     r.Run()
     }
    
     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>{{.title}}</title>
     </head>
     <body>
     fgkjdskjdsh{{.ce}}
     </body>
     </html>
    

    在这里插入图片描述
    代码如下:

     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     r.LoadHTMLGlob("tem/**/*")
     r.GET("/index", func(c *gin.Context) {
    
    c.HTML(http.StatusOK, "user/index.html", gin.H{"title": "我是测试",
    "address": "www.5lmh.com"})
     })
     r.Run()
     }
    
     {{ define "user/index.html" }}
     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>{{.title}}</title>
     </head>
     <body>
     fgkjdskjdsh{{.address}}
     </body>
     </html>
     {{ end }}
    

    在这里插入图片描述

     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     r.LoadHTMLGlob("tem/**/*")
     r.GET("/index", func(c *gin.Context) {
    
    c.HTML(http.StatusOK, "user/index.html", gin.H{"title": "我是测试",
    "address": "www.5lmh.com"})
     })
     r.Run()
     }
    

    user/index.html 文件代码:

     {{ define "user/index.html" }}
     {{template "public/header" .}}
     fgkjdskjdsh{{.address}}
     {{template "public/footer" .}}
     {{ end }}
    

    public/header.html 文件代码:

     {{define "public/header"}}
     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     <title>{{.title}}</title>
     </head>
     <body>
    
     {{end}}
    

    public/footer.html 文件代码:

     {{define "public/footer"}}
     </body>
     </html>
     {{ end }}
    
    • 如果你需要引入静态文件需要定义一个静态文件目录
     r.Static("/assets", "./assets")
    
    重定向
     package main
    
     import (
     "net/http"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     r := gin.Default()
     r.GET("/index", func(c *gin.Context) {
     c.Redirect(http.StatusMovedPermanently, "http://www.5lmh.com")
     })
     r.Run()
     }
    
    同步异步
     package main
    
     import (
     "log"
     "time"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 1.异步
     r.GET("/long_async", func(c *gin.Context) {
     // 需要搞一个副本
     copyContext := c.Copy()
     // 异步处理
     go func() {
     time.Sleep(3 * time.Second)
     log.Println("异步执行:" + copyContext.Request.URL.Path)
     }()
     })
     // 2.同步
     r.GET("/long_sync", func(c *gin.Context) {
     time.Sleep(3 * time.Second)
     log.Println("同步执行:" + c.Request.URL.Path)
     })
    
     r.Run(":8000")
     }
    

    gin中间件

    全局中间件
    • 所有请求都经过此中间件
     package main
    
     import (
     "fmt"
     "time"
    
     "github.com/gin-gonic/gin"
     )
    
     // 定义中间
     func MiddleWare() gin.HandlerFunc {
     return func(c *gin.Context) {
     t := time.Now()
     fmt.Println("中间件开始执行了")
     // 设置变量到Context的key中,可以通过Get()取
    c.Set("request", "中间件")
     status := c.Writer.Status()
     fmt.Println("中间件执行完毕", status)
     t2 := time.Since(t)
     fmt.Println("time:", t2)
     }
     }
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 注册中间件
     r.Use(MiddleWare())
     // {}为了代码规范
     {
     r.GET("/ce", func(c *gin.Context) {
     // 取值
     req, _ := c.Get("request")
     fmt.Println("request:", req)
     // 页面接收
     c.JSON(200, gin.H{"request": req})
     })
    
     }
     r.Run()
     }
    

    请添加图片描述
    请添加图片描述
    请添加图片描述

    next()方法
     package main
    
     import (
     "fmt"
     "time"
    
     "github.com/gin-gonic/gin"
     )
    
     // 定义中间
     func MiddleWare() gin.HandlerFunc {
     return func(c *gin.Context) {
     t := time.Now()
     fmt.Println("中间件开始执行了")
     // 设置变量到Context的key中,可以通过Get()取
     c.Set("request", "中间件")
     // 执行函数
     c.Next()
     // 中间件执行完后续的一些事情
     status := c.Writer.Status()
     fmt.Println("中间件执行完毕", status)
     t2 := time.Since(t)
     fmt.Println("time:", t2)
     }
     }
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 注册中间件
     r.Use(MiddleWare())
     // {}为了代码规范
     {
     r.GET("/ce", func(c *gin.Context) {
     // 取值
     req, _ := c.Get("request")
     fmt.Println("request:", req)
     // 页面接收
     c.JSON(200, gin.H{"request": req})
     })
     }
     r.Run()
     }
    

    请添加图片描述

    局部中间件
     package main
    
     import (
     "fmt"
     "time"
    
     "github.com/gin-gonic/gin"
     )
    
     // 定义中间
     func MiddleWare() gin.HandlerFunc {
     return func(c *gin.Context) {
     t := time.Now()
     fmt.Println("中间件开始执行了")
     // 设置变量到Context的key中,可以通过Get()取
     c.Set("request", "中间件")
     // 执行函数
     c.Next()
     // 中间件执行完后续的一些事情
     status := c.Writer.Status()
     fmt.Println("中间件执行完毕", status)
     t2 := time.Since(t)
     fmt.Println("time:", t2)
     }
     }
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     //局部中间键使用
     r.GET("/ce", MiddleWare(), func(c *gin.Context) {
     // 取值
     req, _ := c.Get("request")
     fmt.Println("request:", req)
     // 页面接收
     c.JSON(200, gin.H{"request": req})
     })
     r.Run()
     }
    

    请添加图片描述

    中间件练习
    • 定义程序计时中间件,然后定义2个路由,执行函数后应该打印统计的执行时间,如下:
     package main
    
     import (
     "fmt"
     "time"
    
     "github.com/gin-gonic/gin"
     )
    
     // 定义中间
     func myTime(c *gin.Context) {
     start := time.Now()
     c.Next()
     // 统计时间
     since := time.Since(start)
     fmt.Println("程序用时:", since)
     }
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 注册中间件
     r.Use(myTime)
     // {}为了代码规范
     shoppingGroup := r.Group("/shopping")
     {
     shoppingGroup.GET("/index", shopIndexHandler)
     shoppingGroup.GET("/home", shopHomeHandler)
     }
     r.Run(":8000")
     }
    
     func shopIndexHandler(c *gin.Context) {
     time.Sleep(5 * time.Second)
     }
     func shopHomeHandler(c *gin.Context) {
     time.Sleep(3 * time.Second)
     }
    

    请添加图片描述

    会话控制

    Cookie介绍
    • HTTP是无状态协议,服务器不能记录浏览器的访问状态,也就是说服务器不能区分两次请求是否 由同一个客户端发出

    • Cookie就是解决HTTP协议无状态的方案之一,中文是小甜饼的意思

    • Cookie实际上就是服务器保存在浏览器上的一段信息。浏览器有了Cookie之后,每次向服务器

    • 发送请求时都会同时将该信息发送给服务器,服务器收到请求后,就可以根据该信息处理请求

    • Cookie由服务器创建,并发送给浏览器,最终由浏览器保存

    cookie的用途

    测试服务端发送cookie给客户端,客户端请求时携带cookie

    Cookie的使用

    测试服务端发送cookie给客户端,客户端请求时携带cookie

     package main
    
     import (
     "github.com/gin-gonic/gin"
     "fmt"
     )
    
     func main() {
     // 1.创建路由
     // 默认使用了2个中间件Logger(), Recovery()
     r := gin.Default()
     // 服务端要给客户端cookie
     r.GET("cookie", func(c *gin.Context) {
     // 获取客户端是否携带cookie
     cookie, err := c.Cookie("key_cookie")
     if err != nil {
     cookie = "NotSet"
     // 给客户端设置cookie
     // maxAge int, 单位为秒
     // path,cookie所在目录
     // domain string,域名
     // secure 是否智能通过https访问
     // httpOnly bool 是否允许别人通过js获取自己的cookie
     c.SetCookie("key_cookie", "value_cookie", 60, "/",
     "localhost", false, true)
     }
     fmt.Printf("cookie的值是: %s\n", cookie)
     })
     r.Run(":8000")
     }
    
    cookie练习
     package main
    
     import (
     "github.com/gin-gonic/gin"
     "net/http"
     )
     func AuthMiddleWare() gin.HandlerFunc {
     return func(c *gin.Context) {
     // 获取客户端cookie并校验
     if cookie, err := c.Cookie("abc"); err == nil {
     if cookie == "123" {
     c.Next()
     return
     }
     }
      // 返回错误
     c.JSON(http.StatusUnauthorized, gin.H{"error": "err"})
     // 若验证不通过,不再调用后续的函数处理
     c.Abort()
     return
     }
     }
    
     func main() {
     // 1.创建路由
     r := gin.Default()
     r.GET("/login", func(c *gin.Context) {
     // 设置cookie
     c.SetCookie("abc", "123", 60, "/",
     "localhost", false, true)
     // 返回信息
     c.String(200, "Login success!")
     })
     r.GET("/home", AuthMiddleWare(), func(c *gin.Context) {
     c.JSON(200, gin.H{"data": "home"})
     })
     r.Run(":8000")
     }
    

    请添加图片描述
    请添加图片描述

    Sessions
     package main
    
     import (
     "fmt"
     "net/http"
    
     "github.com/gorilla/sessions"
     )
    
     // 初始化一个cookie存储对象
      // something-very-secret应该是一个你自己的密匙,只要不被别人知道就行
     var store = sessions.NewCookieStore([]byte("something-very-secret"))
    
     func main() {
     http.HandleFunc("/save", SaveSession)
     http.HandleFunc("/get", GetSession)
     err := http.ListenAndServe(":8080", nil)
     if err != nil {
     fmt.Println("HTTP server failed,err:", err)
     return
     }
     }
    
    func SaveSession(w http.ResponseWriter, r *http.Request) {
     // Get a session. We're ignoring the error resulted from decoding an
     // existing session: Get() always returns a session, even if empty.
    
     // 获取一个session对象,session-name是session的名字
     session, err := store.Get(r, "session-name")
     if err != nil {
     http.Error(w, err.Error(), http.StatusInternalServerError)
     return
     }
    
     // 在session中存储值
     session.Values["foo"] = "bar"
     session.Values[42] = 43
    // 保存更改
     session.Save(r, w)
     }
     func GetSession(w http.ResponseWriter, r *http.Request) {
     session, err := store.Get(r, "session-name")
      if err != nil {
     http.Error(w, err.Error(), http.StatusInternalServerError)
     return
     } 
     foo := session.Values["foo"]
     fmt.Println(foo)
     }
    

    删除session的值:

     // 删除
     // 将session的最大存储时间设置为小于零的数即为删除
     session.Options.MaxAge = -1
     session.Save(r, w)
    

    参数校正

    结构体验证

    用gin框架的数据验证,可以不用解析数据,减少if else,会简洁许多。

     package main
    
     import (
     "fmt"
     "time"
    
     "github.com/gin-gonic/gin"
     )
    
     //Person ..
     type Person struct {
     //不能为空并且大于10
     Age int `form:"age" binding:"required,gt=10"`
     Name string `form:"name" binding:"required"`
     Birthday time.Time `form:"birthday" time_format:"2006-01-02" time_utc:"1"`
     }
    
     func main() {
     r := gin.Default()
     r.GET("/5lmh", func(c *gin.Context) {
     var person Person
     if err := c.ShouldBind(&person); err != nil {
     c.String(500, fmt.Sprint(err))
     return
     }
     c.String(200, fmt.Sprintf("%#v", person))
     })
     r.Run()
     }
    

    请添加图片描述

    自定义验证
     package main
    
     import (
     "net/http"
     "reflect"
     "github.com/gin-gonic/gin"
     "github.com/gin-gonic/gin/binding"
     "gopkg.in/go-playground/validator.v8"
     )
    
     /*
     对绑定解析到结构体上的参数,自定义验证功能
    
    比如我们要对 name 字段做校验,要不能为空,并且不等于 admin ,类似这种需求,就无法
    binding 现成的方法
    
    需要我们自己验证方法才能实现 官网示例(https://godoc.org/gopkg.in/goplayground/validator.v8#hdr-Custom_Functions)
     这里需要下载引入下 gopkg.in/go-playground/validator.v8
     */
     type Person struct {
     Age int `form:"age" binding:"required,gt=10"`
     // 2、在参数 binding 上使用自定义的校验方法函数注册时候的名称
     Name string `form:"name" binding:"NotNullAndAdmin"`
     Address string `form:"address" binding:"required"`
     }
     // 1、自定义的校验方法
    
    func nameNotNullAndAdmin(v *validator.Validate, topStruct reflect.Value,
    currentStructOrField reflect.Value, field reflect.Value, fieldType
    reflect.Type, fieldKind reflect.Kind, param string) bool {
    
     if value, ok := field.Interface().(string); ok {
     // 字段不能为空,并且不等于 admin
     return value != "" && !("5lmh" == value)
     }
    
     return true
     }
    
     func main() {
     r := gin.Default()
    
     // 3、将我们自定义的校验方法注册到 validator中
     if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
     // 这里的 key 和 fn 可以不一样最终在 struct 使用的是 key
     v.RegisterValidation("NotNullAndAdmin", nameNotNullAndAdmin)
     }
    
     /*
    
    curl -X GET "http://127.0.0.1:8080/testing?
    name=&age=12&address=beijing"
    
    curl -X GET "http://127.0.0.1:8080/testing?
    name=lmh&age=12&address=beijing"
    
    curl -X GET "http://127.0.0.1:8080/testing?
    name=adz&age=12&address=beijing"
     */
     r.GET("/5lmh", func(c *gin.Context) {
     var person Person
     if e := c.ShouldBind(&person); e == nil {
     c.String(http.StatusOK, "%v", person)
     } else {
     c.String(http.StatusOK, "person bind err:%v", e.Error())
     }
     })
     r.Run()
     }
    
    多语言翻译验证
     package main
    
     import (
     "fmt"
    
     "github.com/gin-gonic/gin"
     "github.com/go-playground/locales/en"
     "github.com/go-playground/locales/zh"
     "github.com/go-playground/locales/zh_Hant_TW"
     ut "github.com/go-playground/universal-translator"
     "gopkg.in/go-playground/validator.v9"
     en_translations "gopkg.in/go-playground/validator.v9/translations/en"
     zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
     zh_tw_translations "gopkg.in/go-playground/validator.v9/translations/zh_tw"
     )
    
     var (
     Uni *ut.UniversalTranslator
     Validate *validator.Validate
     )
    
     type User struct {
     Username string `form:"user_name" validate:"required"`
     Tagline string `form:"tag_line" validate:"required,lt=10"`
     Tagline2 string `form:"tag_line2" validate:"required,gt=1"`
     }
    
     func main() {
     en := en.New()
     zh := zh.New()
     zh_tw := zh_Hant_TW.New()
     Uni = ut.New(en, zh, zh_tw)
     Validate = validator.New()
    
     route := gin.Default()
     route.GET("/5lmh", startPage)
     route.POST("/5lmh", startPage)
     route.Run(":8080")
     }
    
     func startPage(c *gin.Context) {
     //这部分应放到中间件中
     locale := c.DefaultQuery("locale", "zh")
     trans, _ := Uni.GetTranslator(locale)
     switch locale {
     case "zh":
     zh_translations.RegisterDefaultTranslations(Validate, trans)
     break
     case "en":
     en_translations.RegisterDefaultTranslations(Validate, trans)
     break
     case "zh_tw":
     zh_tw_translations.RegisterDefaultTranslations(Validate, trans)
     break
     default:
     zh_translations.RegisterDefaultTranslations(Validate, trans)
     break
     }
    
     //自定义错误内容
    
    Validate.RegisterTranslation("required", trans, func(ut ut.Translator)
    error {
    
    return ut.Add("required", "{0} must have a value!", true) // see
    universal-translator for details
     }, func(ut ut.Translator, fe validator.FieldError) string {
     t, _ := ut.T("required", fe.Field())
     return t
     })
    
     //这块应该放到公共验证方法中
     user := User{}
     c.ShouldBind(&user)
     fmt.Println(user)
     err := Validate.Struct(user)
     if err != nil {
     errs := err.(validator.ValidationErrors)
     sliceErrs := []string{}
     for _, e := range errs {
     sliceErrs = append(sliceErrs, e.Translate(trans))
     }
     c.String(200, fmt.Sprintf("%#v", sliceErrs))
     }
     c.String(200, fmt.Sprintf("%#v", "user"))
     }
    

    请添加图片描述

    其他

    日志文件
     package main
    
     import (
     "io"
     "os"
    
     "github.com/gin-gonic/gin"
     )
    
     func main() {
     gin.DisableConsoleColor()
    
     // Logging to a file.
     f, _ := os.Create("gin.log")
     gin.DefaultWriter = io.MultiWriter(f)
    
     // 如果需要同时将日志写入文件和控制台,请使用以下代码。
     // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
     r := gin.Default()
     r.GET("/ping", func(c *gin.Context) {
     c.String(200, "pong")
     })
     r.Run()
     }
    

    请添加图片描述
    请添加图片描述

    gin验证码

    后端代码

     package main
    
     import (
     "bytes"
     "github.com/dchest/captcha"
     "github.com/gin-contrib/sessions"
     "github.com/gin-contrib/sessions/cookie"
     "github.com/gin-gonic/gin"
     "net/http"
     "time"
     )
    
     // 中间件,处理session
     func Session(keyPairs string) gin.HandlerFunc {
     store := SessionConfig()
     return sessions.Sessions(keyPairs, store)
     }
     func SessionConfig() sessions.Store {
     sessionMaxAge := 3600
     sessionSecret := "topgoer"
     var store sessions.Store
     store = cookie.NewStore([]byte(sessionSecret))
      store.Options(sessions.Options{
     MaxAge: sessionMaxAge, //seconds
     Path: "/",
     })
     return store
     }
    
     func Captcha(c *gin.Context, length ...int) {
     l := captcha.DefaultLen
     w, h := 107, 36
     if len(length) == 1 {
     l = length[0]
     }
     if len(length) == 2 {
     w = length[1]
     }
     if len(length) == 3 {
     h = length[2]
     }
     captchaId := captcha.NewLen(l)
     session := sessions.Default(c)
     session.Set("captcha", captchaId)
     _ = session.Save()
     _ = Serve(c.Writer, c.Request, captchaId, ".png", "zh", false, w, h)
     }
     func CaptchaVerify(c *gin.Context, code string) bool {
     session := sessions.Default(c)
     if captchaId := session.Get("captcha"); captchaId != nil {
     session.Delete("captcha")
     _ = session.Save()
     if captcha.VerifyString(captchaId.(string), code) {
     return true
     } else {
     return false
     }
     } else {
     return false
     }
     }
    
    func Serve(w http.ResponseWriter, r *http.Request, id, ext, lang string,
    download bool, width, height int) error {
     w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
     w.Header().Set("Pragma", "no-cache")
     w.Header().Set("Expires", "0")
    
     var content bytes.Buffer
     switch ext {
     case ".png":
     w.Header().Set("Content-Type", "image/png")
     _ = captcha.WriteImage(&content, id, width, height)
     case ".wav":
     w.Header().Set("Content-Type", "audio/x-wav")
     _ = captcha.WriteAudio(&content, id, lang)
     default:
     return captcha.ErrNotFound
     }
    
     if download {
     w.Header().Set("Content-Type", "application/octet-stream")
     }
    
    http.ServeContent(w, r, id+ext, time.Time{},
    bytes.NewReader(content.Bytes()))
     return nil
     }
    
     func main() {
     router := gin.Default()
     router.LoadHTMLGlob("./*.html")
     router.Use(Session("topgoer"))
     router.GET("/captcha", func(c *gin.Context) {
     Captcha(c, 4)
     })
     router.GET("/", func(c *gin.Context) {
     c.HTML(http.StatusOK, "index.html", nil)
     })
     router.GET("/captcha/verify/:value", func(c *gin.Context) {
     value := c.Param("value")
     if CaptchaVerify(c, value) {
     c.JSON(http.StatusOK, gin.H{"status": 0, "msg": "success"})
     } else {
     c.JSON(http.StatusOK, gin.H{"status": 1, "msg": "failed"})
     }
     })
     router.Run(":8080")
     }
    

    前端代码:

     <!DOCTYPE html>
     <html lang="en">
     <head>
     <meta charset="UTF-8">
     <title>www.topgoer.com验证码</title>
     </head>
     <body>
     <img src="/captcha" onclick="this.src='/captcha?v='+Math.random()">
     </body>
     </html>
    

    请添加图片描述

    浏览器访问http://127.0.0.1:8080
    访问http://127.0.0.1:8080/captcha/verify/5721 进行验证

    {
     "msg": "failed",
     "status": 1 }
     
    
    生成解析token

    下面的代码是gin框架对jwt的封装

     package main
    
     import (
     "fmt"
     "net/http"
     "time"
    
     "github.com/dgrijalva/jwt-go"
     "github.com/gin-gonic/gin"
     )
    
     //自定义一个字符串
     var jwtkey = []byte("www.topgoer.com")
     var str string
    
     type Claims struct {
     UserId uint
     jwt.StandardClaims
     }
    
     func main() {
     r := gin.Default()
     r.GET("/set", setting)
     r.GET("/get", getting)
     //监听端口默认为8080
     r.Run(":8080")
     }
    
     //颁发token
     func setting(ctx *gin.Context) {
     expireTime := time.Now().Add(7 * 24 * time.Hour)
     claims := &Claims{
     UserId: 2,
     StandardClaims: jwt.StandardClaims{
     ExpiresAt: expireTime.Unix(), //过期时间
     IssuedAt: time.Now().Unix(),
     Issuer: "127.0.0.1", // 签名颁发者
     Subject: "user token", //签名主题
     },
     }
     token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
     // fmt.Println(token)
     tokenString, err := token.SignedString(jwtkey)
     if err != nil {
     fmt.Println(err)
     }
     str = tokenString
     ctx.JSON(200, gin.H{"token": tokenString})
     }
    
     //解析token
     func getting(ctx *gin.Context) {
     tokenString := ctx.GetHeader("Authorization")
     //vcalidate token formate
     if tokenString == "" {
     ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
     ctx.Abort()
     return
     }
    
     token, claims, err := ParseToken(tokenString)
     if err != nil || !token.Valid {
     ctx.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "权限不足"})
     ctx.Abort()
     return
     }
     fmt.Println(111)
     fmt.Println(claims.UserId)
     }
    
     func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
     Claims := &Claims{}
    
    token, err := jwt.ParseWithClaims(tokenString, Claims, func(token
    *jwt.Token) (i interface{}, err error) {
     return jwtkey, nil
     })
     return token, Claims, err
     }
    
  • 相关阅读:
    直击永悦科技半年报:双轮驱动下的“增长曲线”
    Express 基础操作和详解
    GlusterFs分布式文件系统
    高精度地图定位在高速公路自动驾驶系统中的应用
    【C++中预处理语句 include、define、if】
    机器学习强基计划2-3:图文详解决策树预剪枝、后剪枝原理+Python实现
    SpringBoot:使用Spring Batch实现批处理任务
    2021年数维杯数学建模A题外卖骑手的送餐危机求解全过程文档及程序
    Gartner:2022年全球IT支出将超4万亿美元,软件增速最高
    rv1126-rv1109-环境搭建-全部编译的方法
  • 原文地址:https://blog.csdn.net/Algernon98/article/details/127039432