【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
 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值