朱纯树博客
VPS测评推荐网站
cloudacead cloudacead cloudacead

golang框架GoFrame重要知识点整理

golang 框架 goFrame 重要知识点整理

本文介绍 golang 框架 GoFrame 的重要知识点。本文不会前面介绍 goframe,仅罗列重要的点。

1. 路由注册

GoFrame 支持各种各样的路由注册方式,非常灵活。
但实际上只需要掌握官方推荐的一种注册方式即可: 以嵌套的方式定义分组路由。
这种方式的推荐理由:

  • 以分组方式,方便管理路由
  • 以嵌套方式,方便从代码角度一眼看出路由的上下级层次。
    示例如下:
package main

import (
    "net/http"

    "github.com/gogf/gf/frame/g"
    "github.com/gogf/gf/net/ghttp"
)

func MiddlewareAuth(r *ghttp.Request) {
    token := r.Get("token")
    if token == "123456" {
        r.Middleware.Next()
    } else {
        r.Response.WriteStatus(http.StatusForbidden)
    }
}

func MiddlewareCORS(r *ghttp.Request) {
    r.Response.CORSDefault()
    r.Middleware.Next()
}

func MiddlewareLog(r *ghttp.Request) {
    r.Middleware.Next()
    g.Log().Println(r.Response.Status, r.URL.Path)
}

func main() {
    s := g.Server()
    s.Use(MiddlewareLog)
    s.Group("/api.v2", func(group *ghttp.RouterGroup) {
        group.Middleware(MiddlewareAuth, MiddlewareCORS)
        group.GET("/test", func(r *ghttp.Request) {
            r.Response.Write("test")
        })
        group.Group("/order", func(group *ghttp.RouterGroup) {
            group.GET("/list", func(r *ghttp.Request) {
                r.Response.Write("list")
            })
            group.PUT("/update", func(r *ghttp.Request) {
                r.Response.Write("update")
            })
        })
        group.Group("/user", func(group *ghttp.RouterGroup) {
            group.GET("/info", func(r *ghttp.Request) {
                r.Response.Write("info")
            })
            group.POST("/edit", func(r *ghttp.Request) {
                r.Response.Write("edit")
            })
            group.DELETE("/drop", func(r *ghttp.Request) {
                r.Response.Write("drop")
            })
        })
        group.Group("/hook", func(group *ghttp.RouterGroup) {
            group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
                r.Response.Write("hook any")
            })
            group.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
                r.Response.Write("hook name")
            })
        })
    })
    s.SetPort(8199)
    s.Run()
}

2. 请求输入

2.1 按参数类型获取参数

Get*Struct 和 GetBody/GetBodyString 的区别
接口参数中有一种特殊的参数: 自定义参数,往往在服务端的中间件、服务函数中通过 SetParam/GetParam 方法管理

Get*(或 GetRequset*)方法存在优先级:Router Body

2.2 参数绑定到对象(推荐)

若想将接口参数(支持所有类型的参数: query、form、json 等)绑定到 struct 上,框架默认支持,无需指定 tag。
若想自定义绑定规则,则可使用 tag 标签自定义。

package main

import (
    "github.com/gogf/gf/frame/g"
    "github.com/gogf/gf/net/ghttp"
)

type RegisterReq struct {
    Name  string
    Pass  string `p:"password1"`
    Pass2 string `p:"password2"`
}

type RegisterRes struct {
    Code  int         `json:"code"`
    Error string      `json:"error"`
    Data  interface{} `json:"data"`
}

func main() {
    s := g.Server()
    s.BindHandler("/register", func(r *ghttp.Request) {
        var req *RegisterReq
        if err := r.Parse(&req); err != nil {
            r.Response.WriteJsonExit(RegisterRes{
                Code:  1,
                Error: err.Error(),
            })
        }
        // ...
        r.Response.WriteJsonExit(RegisterRes{
            Data: req,
        })
    })
    s.SetPort(8199)
    s.Run()
}

2.3 请求校验

推荐的错误校验方法: 当产生错误时,我们可以将校验错误转换为*gvalid.Error对象,随后可以通过灵活的方法控制错误的返回。

package main

import (
    "github.com/gogf/gf/frame/g"
    "github.com/gogf/gf/net/ghttp"
    "github.com/gogf/gf/util/gvalid"
)

type RegisterReq struct {
    Name  string `p:"username"  v:"required|length:6,30#请输入账号|账号长度为:min到:max位"`
    Pass  string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
    Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|密码长度不够|两次密码不一致"`
}

type RegisterRes struct {
    Code  int         `json:"code"`
    Error string      `json:"error"`
    Data  interface{} `json:"data"`
}

func main() {
    s := g.Server()
    s.BindHandler("/register", func(r *ghttp.Request) {
        var req *RegisterReq
        if err := r.Parse(&req); err != nil {
            // Validation error.
            if v, ok := err.(*gvalid.Error); ok {
                r.Response.WriteJsonExit(RegisterRes{
                    Code:  1,
                    Error: v.FirstString(),
                })
            }
            // Other error.
            r.Response.WriteJsonExit(RegisterRes{
                Code:  1,
                Error: err.Error(),
            })
        }
        // ...
        r.Response.WriteJsonExit(RegisterRes{
            Data: req,
        })
    })
    s.SetPort(8199)
    s.Run()
}

测试结果:

$ curl "http://127.0.0.1:8199/register"
{"code":1,"error":"请输入账号","data":null}

$ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=12345"
{"code":1,"error":"两次密码不一致","data":null}

2.4 自定义变量

开发者可以在请求中自定义一些变量设置,自定义变量的获取优先级是最高的,可以覆盖原有的客户端提交参数。

自定义变量可以通过 SetParam 方法进行设置。自定义变量的获取可以通过请求参数的获取方法获得到,例如:Get/GetVar/GetMap 等等,也可以通过特定的自定义变量方法获取到 GetParam/GetParamVar

package main

import (
    "github.com/gogf/gf/frame/g"
    "github.com/gogf/gf/net/ghttp"
)

// 前置中间件1
func MiddlewareBefore1(r *ghttp.Request) {
    r.SetParam("name", "GoFrame")
    r.Response.Writeln("set name")
    r.Middleware.Next()
}

// 前置中间件2
func MiddlewareBefore2(r *ghttp.Request) {
    r.SetParam("site", "https://goframe.org")
    r.Response.Writeln("set site")
    r.Middleware.Next()
}

func main() {
    s := g.Server()
    s.Group("/", func(group *ghttp.RouterGroup) {
        group.Middleware(MiddlewareBefore1, MiddlewareBefore2)
        group.ALL("/", func(r *ghttp.Request) {
            r.Response.Writefln(
                "%s: %s",
                r.GetParamVar("name").String(),
                r.GetParamVar("site").String(),
            )
        })
    })
    s.SetPort(8199)
    s.Run()
}

结果:

set name
set site
GoFrame: https://goframe.org

2.5 上下文变量

上下文变量自定义变量的区别:自定义变量会覆盖接口参数,而上下文变量不会覆盖。

3. 请求输出

3.1 缓冲区

Response 输出采用了缓冲控制,输出的内容预先写入到一块缓冲区,等待服务方法执行完毕后才真正地输出到客户端。该特性在提高执行效率同时为输出内容的控制提供了更高的灵活性。

可应用于全局异常处理场景: 接口出现异常, 重新定义接口的输出内容。

package main

import (
    "github.com/gogf/gf/frame/g"
    "github.com/gogf/gf/net/ghttp"
    "net/http"
)

func MiddlewareErrorHandler(r *ghttp.Request) {
    r.Middleware.Next()
    if r.Response.Status >= http.StatusInternalServerError {
        r.Response.ClearBuffer()
        r.Response.Writeln("服务器居然开小差了,请稍后再试吧!")
    }
}

func main() {
    s := g.Server()
    s.Group("/api.v2", func(group *ghttp.RouterGroup) {
        group.Middleware(MiddlewareErrorHandler)
        group.ALL("/user/list", func(r *ghttp.Request) {
            panic("db error: sql is xxxxxxx")
        })
    })
    s.SetPort(8199)
    s.Run()
}

4. session

GF 框架的 Session 默认过期时间是 24 小时

SessionId 默认通过 Cookie 来传递,并且也支持客户端通过 Header 传递 SessionId,SessionId 的识别名称可以通过 ghttp.Server 的 SetSessionIdName 进行修改。
ghttp.Server 中的 SessionId 使用的是客户端的 RemoteAddr + Header 请求信息通过 guid 模块来生成的,保证随机及唯一性

自定义 session 的过期时间:

    s := g.Server()
    s.SetConfigWithMap(g.Map{
        "SessionMaxAge": time.Minute,
    })

gsession 实现并为开发者提供了常见的三种 Session 存储实现方式:

  • 基于文件存储(默认)
    单节点部署方式下比较高效的持久化存储方式;
    重启程序后,session 数据不丢失,自动加载到内存。
  • 基于纯内存存储
    性能最高效,但是无法持久化保存,重启即丢失;
    s := g.Server()
    s.SetConfigWithMap(g.Map{
        "SessionMaxAge":  time.Minute,
        "SessionStorage": gsession.NewStorageMemory(),
    })
  • 基于 Redis 存储
    适用于多节点部署的场景;

5. 配置管理

2 种配置方式: 代码或配置文件
服务启动后不允许修改配置
用户提交的数据大小限制的配置项:

[server]
    MaxHeaderBytes    = "20KB"
    ClientMaxBodySize = "200MB"

6. 全局异常处理

默认情况下,全局异常会记录到日志中。
开发者也可以自定义异常处理逻辑。异常信息的获取: Request 对象中的 GetError 方法

疑问:即使开发者有自己捕获记录异常错误的日志,但是Server依旧会打印到Server自己的错误日志文件中中的 server 自己的错误日志文件指的是?

7. HTTP 客户端

推荐使用单例对象g.Client()
基于连接池。
支持链式操作
若默认的单例客户端不满足需求,可以自定义客户端。

*Bytes*Content 方法:
以 Bytes 及 Content 后缀结尾的请求方法为直接获取返回内容的快捷方法,这些方法将会自动读取服务端返回内容并自动关闭请求连接。需要注意的是,如果请求执行失败,返回内容将会为空。

*Bytes*Content 方法:
必须手动调用 Close 方法关闭

*Var 方法:
若服务端返回 json 或 xml 数据时, 可使用该方法转换为对象

文章来源于互联网:
golang框架GoFrame重要知识点整理

赞(0) 打赏
未经允许不得转载:VPS测评推荐网站 - 朱纯树博客 » golang框架GoFrame重要知识点整理

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #0

    http://rs0l11s.cn/
    东东博客
    在线资讯

    123cc2个月前 (09-18)回复