抬头看天空 2021-12-10 10:48 采纳率: 0%
浏览 42

我的Go+语言初体验——妙不可言的开源web框架gin

1.安装

通过go get命令安装gin框架:

go get -u github.com/ gin-gonic/gin

简单的使用

r := gin.Default() //建立一个gin的服务端
//匹配地址返回匿名函数json数据
r.GET("/hello", func(c *gin.Context) {
    c.JSON(200, gin.H{
        "message": "pong",
    })
})
//启动服务,r.Run(":8090")指定端口
r.Run() // 监听并在 0.0.0.0:8080 上启动服务

2.Gin网络请求与路由处理

在gin框架中,Engine被定义成为一个结构体,Engine代表gin框架的一个结构体定义,其中包含了路由组、中间件、页面渲染接口、框架配置设置等相关内容。默认的Engine可以通过gin.Default进行创建,或者使用gin.New()同样可以创建。两种方式如下所示:
engine1 = gin.Default()
engine2 = gin.New()
gin.Default()和gin.New()的区别在于gin.Default也使用gin.New()创建engine实例,但是会默认使用Logger{打印并输出日志}和Recovery{遇到panic中断了服务,则Recovery会恢复程序执行}中间件。
Handle处理GET请求
engine.Handle("GET", "/hello", func(context *gin.Context) {}
//获取请求接口
fmt.Println(context.FullPath())
可以通过context.Query和context.DefaultQuery获取GET请求携带的参数。
可以通过context.Writer.Write向请求发起端返回数据。
Handle处理POST请求
engine.Handle("POST", "/login", func(context *gin.Context) {}
POST请求是以form表单的方式提交数据的,可以通过context.PostForm获取表单中提交的数据字段。
其他类型的HTTP请求也可以通过Handle方法处理对应类型的请求。
engine.GET()处理GET请求
engine.GET("/hello", func(context *gin.Context) {}
username := context.Query("name")//获取请求参数
name := context.DefaultQuery("name", "")//获取参数
engine.POST()处理POST请求
engine.POST("/login", func(context *gin.Context) {}
 password, exists := context.GetPostForm("pwd")
if exists {
    fmt.Println(password)
}
context.GetPostForm获取表单数据:POST请求以表单的形式提交数据,除了可以使用context.PostForm获取表单数据意外,还可以使用context.GetPostForm来获取表单数据。
engine.DELETE()处理DELETE请求
engine.DELETE("/user/:id", DeleteHandle)
func DeleteHandle(context *gin.Context) {
    userID := context.Param("id")
}
context.Param获取请求参数

3.请求参数绑定与多数据格式处理

表单实体绑定
以一个用户注册功能来进行讲解表单实体绑定操作。用户注册需要提交表单数据,假设注册时表单数据包含三项,分别为:username、phone和password。
type UserRegister struct {
Username string form:"username" binding:"required"
Phone    string form:"phone" binding:"required"
Password string form:"password" binding:"required"
}
创建了UserRegister结构体用于接收表单数据,通过tag标签的方式设置每个字段对应的form表单中的属性名,通过binding属于设置属性是否是必须。
ShouldBindQuery
使用ShouldBindQuery可以实现Get方式的数据请求的绑定。具体实现如下:
 var student Student
 err := context.ShouldBindQuery(&student)
 type Student struct {
 Name    string form:"name"
 Classes string form:"classes"
}
ShouldBind
使用ShouldBind可以实现Post方式的提交数据的绑定工作。具体编程如下所示:
 var _register Register
    if err := context.ShouldBind(&_register); err != nil {
        log.Fatal(err.Error())
        return
    }
type Register struct {
UserName string form:"name"
Phone    string form:"phone"
Password string form:"pwd"

}

ShouldBind
当客户端使用Json格式进行数据提交时,可以采用ShouldBindJson对数据进行绑定并自动解析,如下所示:
var person Person
if err := context.BindJSON(&person); err != nil {
log.Fatal(err.Error())
    return
}
type Person struct {
Name string form:"name"
Sex  string form:"sex"
Age  int    form:"age"

}

4.多数据格式返回请求结果

[]byte
在之前的课程案例中,我们统一使用的请求返回数据格式为[]byte。通过context.Writer.Write方法写入[]byte数据
 context.Writer.Write([]byte(fullPath))
 如上段代码所示,使用context.Writer.Write向客户端写入返回数据。Writer是gin框架中封装的一个ResponseWriter接口类型
string
除了write方法以外,ResponseWriter自身还封装了WriteString方法返回数据。
和[]byte类型调用一样,可以通过Writer进行调用。详细编程示例如下所示:
 context.Writer.WriteString('我是谁')
JSON

map类型

  context.JSON(200, map[string]interface{}{
    "code":    1,
    "message": "OK",
    "data":    fullPath,
})

结构体类型

//通用请求返回结构体定义
type Response struct {
    Code       int64 `json:"code"`
    Message string     `json:"msg"`
    Data    interface{} `json:"data"`
}
resp := Response{Code: 1, Message: "Ok", Data: fullPath}
context.JSON(200, &resp)
HTML模板
除了JSON格式以外,gin框架还支持返回HTML格式的数据。可以直接渲染HTML页面。举例如下:
//设置html的目录
engine.LoadHTMLGlob("./html/*")
context.HTML(http.StatusOK, "index.html", gin.H{
    "title":    "Gin教程",
    "fullpath": fullPath,
})
{{.title}}context.HTML来加载HTMl页面或者模板
加载静态资源文件
在上面的index.html的基础上,添加一张img进行展示。需要将img所在的目录进行静态资源路径设置才可能会生效,如下所示:
engine.Static("/img", "./img")


    //app.Static("/img", "./img")
    //设置html的目录
    //app.LoadHTMLGlob("./static/html/*")
    //{{.title}}context.HTML来加载HTMl页面或者模板

5.使用路由组分类处理请求

Group

gin框架中可以使用路由组来实现对路由的分类。

路由组是router.Group中的一个方法,用于对请求进行分组。如下案例所示:

userGroup := engine.Group("/user")
userGroup.GET("/register", registerHandle)
userGroup.GET("/login", loginHandle)
userGroup.GET("/info", infoHandle)
engine.Run(":9000")

6.middleware的编写与使用

Gin的中间件
在gin中,中间件称之为middleware,中间件的类型定义如下所示:
type HandlerFunc func(*Context)
HandlerFunc是一个函数类型,接收一个Context参数。用于编写程序处理函数并返回HandleFunc类型,作为中间件的定义。
中间件Use用法
在之前学习的课程中,均使用gin.Default创建了gin引擎engins变量,其中,就使用了中间件。如下图所示:

func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
//Log中间件
func Logger() HandlerFunc {
    return LoggerWithConfig(LoggerConfig{})
}
//Recovery中间件
func Recovery() HandlerFunc {
    return RecoveryWithWriter(DefaultErrorWriter)
}

在Default函数中,engine调用Use方法设置了Logger中间件和Recovery中间件。Use函数接收一个可变参数,类型为HandlerFunc,恰为中间件的类型。Use方法定义如下:

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}
自定义中间件
根据上文的介绍,可以自己定义实现一个特殊需求的中间件,中间件的类型是函数,有两条标准:
func函数
返回值类型为HandlerFunc

定义一个名为RequestInfos的中间件,在该中间件中打印请求的path和method。具体代码实现如下所示:
func RequestInfos() gin.HandlerFunc {
return func(context *gin.Context) {
path := context.FullPath()
method := context.Request.Method
fmt.Println("请求Path:", path)
fmt.Println("请求Method:", method)
}
}

func main() {

engine := gin.Default()
engine.Use(RequestInfos())

engine.GET("/query", func(context *gin.Context) {
    context.JSON(200, map[string]interface{}{
        "code": 1,
        "msg":  context.FullPath(),
    })
})
engine.Run(":9000")
}
context.Next函数

context.Next函数可以将中间件代码的执行顺序一分为二,Next函数调用之前的代码在请求处理之前之前,当程序执行到context.Next时,会中断向下执行,转而先去执行具体的业务逻辑,执行完业务逻辑处理函数之后,程序会再次回到context.Next处,继续执行中间件后续的代码。具体用法如下:

    func main() {
    engine := gin.Default()
    engine.Use(RequestInfos())
    engine.GET("/query", func(context *gin.Context) {
        fmt.Println(" 中间件的使用方法  ")
        context.JSON(404, map[string]interface{}{
            "code": 1,
            "msg":  context.FullPath(),
        })
    })
    engine.Run(":9000")
}
func RequestInfos() gin.HandlerFunc {
    return func(context *gin.Context) {
        path := context.FullPath()
        method := context.Request.Method
        fmt.Println("请求Path:", path)
        fmt.Println("请求Method:", method)
        context.Next()
        fmt.Println(context.Writer.Status())
    }
}
结果
请求Path: /query
请求Method: GET
 中间件的使用方法  
404
      

7. Gin框架中使用数据库

1.安装数据库
2.安装MySQL驱动
    go get "github.com/go-sql-driver/mysql"
3.、创建数据库
mysql -uroot -p
create database ginsql;
4.gin_使用

a、引入mysql驱动程序:使用import将mysql驱动默认引入,具体语法如下:

import _ "github.com/go-sql-driver/mysql"

b、拼接链接字符:在程序中链接mysql,需要按照一定的规则进行用户名,密码等信息的组合。

connStr := "root:12345678@tcp(127.0.0.1:3306)/ginsql"

c、使用sql.Open创建数据库连接
    db, err := sql.Open("mysql", connStr)
    if err != nil {
        log.Fatal(err.Error())
        return
    }
5.操作数据库

a、 创建数据库表

  _, err = db.Exec("create table person(" +
"id int auto_increment primary key," +
"name varchar(12) not null," +
"age int default 1" +
");")
if err != nil {
log.Fatal(err.Error())
return
}

b、向数据库中插入数据

  _, err = db.Exec("insert into person(name,age) "+
"values(?,?);", "Lily", 15)
if err != nil {
log.Fatal(err.Error())
return
} else {
fmt.Println("数据插入成功")
}

c、查询数据库记录

rows, err := db.Query("select id,name,age from person")
if err != nil {
log.Fatal(err.Error())
return
}
scan:
if rows.Next() {
//columns, err := rows.Columns()
//fmt.Println(columns)
person := new(Person)
err = rows.Scan(&person.Id, &person.Name, &person.Age)
if err != nil {
log.Fatal(err.Error())
return
}
fmt.Println(person.Id, person.Name, person.Age)
goto scan
}

8.项目开发介绍及初始化

创建项目

在gopath的src目录下,创建OnlineRestaurant目录,作为服务端项目。

mkdir CloudRestaurant

创建配置文件目录

mkdir config

config目录中,配置app.json配置文件:

{
  "app_name": "qianfengmall",
  "app_mode": "debug",
  "app_host": "localhost",
  "app_port": "8090"
}

并创建Config.go文件,用于解析项目配置信息。Config.go如下:

type Config struct {
    AppName  string         json:"app_name"
    AppMode  string         json:"app_mode"
    AppHost  string         json:"app_host"
    AppPort  string         json:"app_port"
}
var _cfg *Config = nil
func GetConfig() *Config {
    return _cfg
}

func ParseConfig(path string) (*Config, error) {
    file, err := os.Open(path)
    if err != nil {
        panic(err)
    }
    defer file.Close()

    reader := bufio.NewReader(file)
    decoder := json.NewDecoder(reader)
    if err := decoder.Decode(&_cfg); err != nil {
        return nil, err
    }
    return _cfg, nil
}

编写main.go

func main() {
    //读取配置文件
    cfg, err := toolbox.ParseConfig("./config/app.json")
    if err != nil {
        toolbox.Error(err.Error())
        return
    }
    app := gin.Default()
    app.Run(cfg.AppHost + ":" + cfg.AppPort)
}

编写Hell world
编写Controller:创建controller,并创建HelloController。

package controller

import "github.com/gin-gonic/gin"

type HelloController struct {
}

func (hello *HelloController) Router(engine *gin.Engine) {
    engine.GET("/hello", hello.Hello)
}

func (hello *HelloController) Hello(context *gin.Context) {
    context.JSON(200, map[string]interface{}{
        "message": "hello world",
    })
}

在main.go程序中添加路由设置

func registerRouter(router *gin.Engine) {
    new(controller.HelloController).Router(router)
}

9.创建数据库和数据表

xorm介绍

在项目开发过程中,我们会使用一些成熟的框架来操作数据库。xorm就是一个比较流行的数据库操作orm框架。

xorm安装及mysql驱动
go get github.com/go-xorm/xorm
//安装mysql驱动:
go get github.com/go-sql-driver/mysql
连接数据库
create database cloudrestaurant;

创建完数据库并安装好xorm库以后,使用xorm进行连接数据库。具体的连接操作如下所示:

import (
"github.com/go-xorm/xorm"
_ "github.com/go-sql-driver/mysql"
)
database := cfg.Database
conn := database.User + ":" + database.Password + "@tcp(" + database.Host + ":" + database.Port + ")/" + database.DbName + "?charset=" + database.Charset
engine, err := xorm.NewEngine(database.Driver, conn)
if err != nil {
    return nil, err
}   
连接数据库有些参数需要自己指定,比如说驱动类型,登录数据库的用户名,密码,数据库名等。将这些变量配置在app.json配置文件中,如下所示:
    {
...
"database": {
    "driver": "mysql",
    "user": "root",
    "password": "12345678",
    "host": "127.0.0.1",
    "port": "3306",
    "db_name": "cloudrestaurant",
    "charset": "utf8mb4",
    "show_sql": true
}
...
}

并在Config结构体中添加对dtabase的解析:

type Config struct {
AppName  string         json:"app_name"
AppMode  string         json:"app_mode"
AppHost  string         json:"app_host"
AppPort  string         json:"app_port"
Database DatabaseConfig json:"database"
Sms      SmsConfig      json:"sms"
}
type DatabaseConfig struct {
    Driver   string json:"driver"
    User     string json:"user"
    Password string json:"password"
    Host     string json:"host"
    Port     string json:"port"
    DbName   string json:"db_name"
    Charset  string json:"charset"
    ShowSql  bool   json:"show_sql"
}

创建SmsCode
要存储验证码,需要在数据库中创建表结构进行存储。我们可以创建SmsCode结构体,并通过tag设置数据库字段约束,具体的SmsCode定义如下:

package model

type SmsCode struct {
    Id         int64  xorm:"pk autoincr" json:"id"
    Phone      string xorm:"varchar(11)" json:"phone"
    BizId      string xorm:"varchar(30)" json:"biz_id"
    Code       string xorm:"varchar(4)" json:"code"
    CreateTime int64  xorm:"bigint" json:"create_time"
}

通过tag的xorm设置字段数据类型以及约束。

  • pk:表示主键
  • autoinc:表示自增
  • bigint:整形变量
  • varchar:字符串类型
Sync2同步生成数据库表

可以调用engine.Sync2方法,将结构体类型同步映射到数据库中,生成数据库表。

err = engine.Sync2(new(model.SmsCode))
    if err != nil {
        return nil, err
}

将验证码数据保存到数据库中

在MemberService的SendCode方法,添加保存验证码到数据库的操作

func (msi *MemberServiceImpl) SendCode(phone string) string {
    code := fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
    ...
    dao := impl.NewMemberDao()
    smsCode := model.SmsCode{Phone: phone, Code: code, BizId: response.BizId, CreateTime: time.Now().Unix()}

    if result := dao.InsertCode(smsCode); result > 0 {
        return code
    }
    return ""
}

10.服务器全局跨域请求处理设置

服务端设置跨域访问

可以在gin服务端,编写程序进行全局设置。通过中间件的方式设置全局跨域访问,用以返回Access-Control-Allow-Origin和浏览器进行匹配。

在服务端编写跨域访问中间件,详细内容如下:
func Cors() gin.HandlerFunc {
return func(context *gin.Context) {
    method := context.Request.Method
    origin := context.Request.Header.Get("Origin")
    var headerKeys []string
    for k, _ := range context.Request.Header {
        headerKeys = append(headerKeys, k)
    }
    headerStr := strings.Join(headerKeys, ",")
    if headerStr != "" {
        headerStr = fmt.Sprintf("access-control-allow-origin, access-control-allow-headers, %s", headerStr)
    } else {
        headerStr = "access-control-allow-origin, access-control-allow-headers"
    }

    if origin != "" {
        context.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        context.Header("Access-Control-Allow-Origin", "*") // 设置允许访问所有域
        context.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
        context.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma")
        context.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar")
        context.Header("Access-Control-Max-Age", "172800")
        context.Header("Access-Control-Allow-Credentials", "false")
        context.Set("content-type", "application/json") //// 设置返回格式是json
    }

    if method == "OPTIONS" {
        context.JSON(http.StatusOK, "Options Request!")
    }
    //处理请求
    context.Next()
}
}

其中的Access-Control-Allow-Origin的设置,表示允许进行跨域访问,*表示可以访问所有域。同时,通过Header方法进行了其他的设置。

最后context.Next()是中间件使用的标准用法,表示继续处理请求。

在main函数中,调用编写好的跨域访问。调用如下:

func main(){
    ...
    app := gin.Default()
    app.Use(Cors())
    ...
}

11.图形化验证码生成和验证

验证码库安装
go get -u github.com/mojocn/base64Captcha

通过自定义配置,可以选择不同的生成验证码的参数,并刷新验证码,同时还可以对验证码进行验证。

通过exmaple目录下的main.go程序可以看到生成验证码和验证验证码的逻辑,此处不再赘述。

项目集成验证码生成和Redis缓存
安装go-redis库
在项目中使用redis,需要安装go-redis库,可以在https://github.com/go-redis/redis中查看如何下载go-redis和配置。
增加Redis配置
在配置文件app.json中新增redis配置:

"redis_config": {
"addr": "127.0.0.1",
"port": "6379",
"password": "",
"db": 0
}
...

同时,新增RedisConfig结构体定义,如下所示:

type RedisConfig struct {
Addr string json:"addr"
Port string json:"port"
Db   int    json:"db"
}   
Redis初始化操作

进行了redis配置以后,需要对redis进行初始化。可以封装redis初始化操作函数如下所示:
type RedisStore struct {
redisClient *redis.Client
}

var Redis *redis.Client

func InitRediStore() *RedisStore {
    config := GetConfig().RedistConfig

Redis = redis.NewClient(&redis.Options{
    Addr:     config.Addr + ":" + config.Port,
    Password: "",
    DB:       config.Db,
})

customeStore := &RedisStore{Redis}
base64Captcha.SetCustomStore(customeStore)

return customeStore
}

同时,为customeStore提供Set和Get两个方法,如下所示:

    func (cs *RedisStore) Set(id string, value string) {
    err := cs.redisClient.Set(id, value, time.Minute*10).Err()
    if err != nil {
        log.Println(err.Error())
    }
}

func (cs *RedisStore) Get(id string, clear bool) string {
    val, err := cs.redisClient.Get(id).Result()
    if err != nil {
        toolbox.Error(err.Error())
        return ""
    }
    if clear {
        err := cs.redisClient.Del(id).Err()
        if err != nil {
            toolbox.Error(err.Error())
            return ""
        }
    }
    return val
}

[https://bbs.csdn.net/topics/603464006?utm_source=1594742339](“我的Go+语言初体验” | 征文活动进行中......)

  • 写回答

1条回答 默认 最新

  • 凯歌响起 2022-10-09 13:09
    关注

    这是问答板块,你在干什么呢

    评论

报告相同问题?

问题事件

  • 创建了问题 12月10日

悬赏问题

  • ¥15 vs2019的js智能提示
  • ¥15 关于#开发语言#的问题:FDTD建模问题图中代码没有报错,但是模型却变透明了
  • ¥15 uniapp的h5项目写一个抽奖动画
  • ¥15 TeleScan不能修改bar
  • ¥100 请问我基于逐飞库写的这个有关于mp u6050传感器的函数,为什么输出的值是固定的?
  • ¥15 hadoop中启动hive报错如下怎么解决
  • ¥15 如何优化QWebEngineView 加载url的速度
  • ¥15 关于#hadoop#的问题,请各位专家解答!
  • ¥15 如何批量抓取网站信息
  • ¥15 Spring Boot离线人脸识别