All skills
gogf avatar

/goframe-v2

@911705d
by GoFramegogf/skills81 stars
3

GoFrame v2 development skill. Use only when the target Go project uses or is explicitly adopting GoFrame v2: the nearest go.mod requires github.com/gogf/gf/v2, existing Go files import github.com/gogf/gf/v2 or any github.com/gogf/gf/v2/... component package, or the user asks to scaffold, migrate, or build with GoFrame. Trigger for GoFrame-backed Go work such as APIs/controllers/services, middleware, routing/config, ORM/DAO/DO/entity/database operations, gf CLI/codegen, HTTP/gRPC services, and microservice conventions. Do not trigger for generic Go projects without GoFrame evidence, frontend-only work, shell scripts, or unrelated infrastructure tasks.

Use this Skill: https://skilld.dev/gh/gogf/skills/goframe-v2

This session only. Nothing lands on disk.

referencesWEB服务开发请求输入请求输入.md

请求输入依靠 ghttp.Request 对象实现, ghttp.Request 继承了底层的 http.Request 对象。 ghttp.Request 包含一个与当前请求对应的返回输出对象 Response,用于数据的返回处理。

相关方法: https://pkg.go.dev/github.com/gogf/gf/v2/net/ghttp#Request

简要说明

可以看到 Request 对象的 参数获取方法 非常丰富,常用方法如下:

常用方法 描述
Get 常用方法,简化参数获取, GetRequest 的别名。
GetQuery 获取 GET 方式传递过来的参数,包括 Query String 及 Body 参数解析。
GetForm 获取表单方式传递过来的参数,表单方式提交的参数 Content-Type 往往为 application/x-www-form-urlencoded, application/form-data, multipart/form-data, multipart/mixed 等等。
GetRequest 获取客户端提交的所有参数,按照参数优先级进行覆盖,不区分提交方式。
Get*Struct 将指定提交类型的所有请求参数绑定到指定的 struct 对象上,注意给定的参数为对象指针。绝大部分场景中往往使用 Parse 方法将请求数据转换为请求对象,具体详见后续章节。
GetBody/GetBodyString 获取客户端提交的原始数据,该数据是客户端写入到 body 中的原始数据,与 HTTP Method 无关,例如客户端提交 JSON/XML 数据格式时可以通过该方法获取原始的提交数据。
GetJson 自动将原始请求信息解析为 gjson.Json 对象指针返回, gjson.Json 对象具体在 通用编解码-gjson 章节中介绍。
Exit* 用于请求流程退出控制,详见本章后续说明。

提交方式

GoFrame 框架的参数获取不是通过 HTTP Method 来做区分,而是通过参数提交类型来区分。例如,分别通过 HTTP Method: POST、INPUT、DELETE 来提交表单参数,在服务端获取参数不是通过 GetPost/ GetInput/ GetDelete 的方式来获取,而是统一通过 GetForm 方法来获取表单参数,针对其他的 HTTP Method 也是如此。

在 GoFrame 框架下,有以下几种提交类型:

提交类型 描述
Router 路由参数。来源于路由规则匹配。
Query Query 参数。 URL 中的 Query String 参数解析,如: http://127.0.0.1/index?id=1&name=john 中的 id=1&name=john。
Form 表单参数。最常见的提交方式,提交的 Content-Type 往往为: application/x-www-form-urlencoded、 multipart/form-data、 multipart/mixed。
Body 内容参数。从 Body 中获取并解析得到的参数, JSON/ XML 请求往往使用这种方式提交。
Custom 自定义参数,往往在服务端的中间件、服务函数中通过 SetParam/GetParam 方法管理。

参数类型

获取的参数方法可以对指定键名的数据进行 自动类型转换,例如: http://127.0.0.1:8199/?amount=19.66,通过 Get(xxx).String() 将会返回 19.66 的字符串类型, Get(xxx).Float32()/ Get(xxx).Float64() 将会分别返回 float32 和 float64 类型的数值 19.66。但是, Get(xxx).Int()/ Get(xxx).Uint() 将会返回 19(如果参数为 float 类型的字符串,将会按照 向下取整 进行整型转换)。 :::tip 聪明的您一定发现了,获取到的参数都是 泛型变量,根据该泛型变量再根据需要调用对应的方法转换为对应的数据类型。 ::: 使用示例:

package main

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

func main() {
    s := g.Server()
    s.BindHandler("/", func(r *ghttp.Request) {
        r.Response.Writeln(r.Get("amount").String())
        r.Response.Writeln(r.Get("amount").Int())
        r.Response.Writeln(r.Get("amount").Float32())
    })
    s.SetPort(8199)
    s.Run()
}

执行后我们访问地址 http://127.0.0.1:8199/?amount=19.66 页面输出

19.66
19
19.66

参数优先级

我们考虑一种场景,当不同的提交方式中存在同名的参数名称会怎么样?在 GoFrame 框架下,我们根据不同的获取方法,将会按照不同的优先级进行获取,优先级高的方式提交的参数将会优先覆盖其他方式的同名参数。优先级规则如下:

  1. Get 及 GetRequset 方法: Router < Query < Body < Form < Custom,也就是说自定义参数的优先级最高,其次是 Form 表单参数,再次是 Body 提交参数,以此类推。例如, Query 和 Form 中都提交了同样名称的参数 id,参数值分别为 1 和 2,那么 Get("id")/ GetForm("id") 将会返回 2,而 GetQuery("id") 将会返回 1。
  2. GetQuery 方法: Query > Body,也就是说 query string 的参数将会覆盖 Body 中提交的同名参数。例如, Query 和 Body 中都提交了同样名称的参数 id,参数值分别为 1 和 2,那么 Get("id") 将会返回 2,而 GetQuery("id") 将会返回 1。
  3. GetForm 方法:由于该类型的方法仅用于获取 Form 表单参数,因此没什么优先级的差别。

使用示例:

package main

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

func main() {
    s := g.Server()
    s.BindHandler("/input", func(r *ghttp.Request) {
        r.Response.Writeln(r.Get("amount"))
    })
    s.BindHandler("/query", func(r *ghttp.Request) {
        r.Response.Writeln(r.GetQuery("amount"))
    })
    s.SetPort(8199)
    s.Run()
}

执行后,我们通过 curl 工具进行测试:

$ curl -d "amount=1" -X POST "http://127.0.0.1:8199/input?amount=100"
1
$ curl -d "amount=1" -X POST "http://127.0.0.1:8199/query?amount=100"
100

可以看到,当我们访问 /input 路由时,该路由方法中采用了 Get 方法获取 amount 参数,按照同名优先级的规则返回了 1,即 body 中传递的参数。而当我们通过 /query 路由访问时,该路由方法内部使用了 GetQuery 方法获取 amount 参数,因此获取到的是 query string 参数中的 amount 值,返回了 100。

大小写敏感

需要注意哦, 参数名称是大小写敏感 的哦,例如客户端提交的参数 name 和 Name 是两个参数。 由于服务端默认是通过字符串名称获取参数,因此大小写敏感并不会产生什么问题,但是如果服务端接收的是一个API对象,那么可能需要注意一下。我们来看一个例子。

服务端:

package main

import (
    "context"

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

type Controller struct{}

type PathReq struct {
    g.Meta `path:"/api/v1/*path" method:"post"`
    Path   string
}

type PathRes struct {
    Path string
}

func (c *Controller) Path(ctx context.Context, req *PathReq) (res *PathRes, err error) {
    return &PathRes{Path: req.Path}, nil
}

func main() {
    s := g.Server()
    s.SetPort(8199)
    s.Use(ghttp.MiddlewareHandlerResponse)
    s.Group("/", func(group *ghttp.RouterGroup) {
        group.Bind(&Controller{})
    })
    s.Run()
}

服务端的接口设计原意是定义一个路由参数 path,并且通过 API 对象的 Path 属性来接收。

客户端:

package main

import (
    "fmt"

    "github.com/gogf/gf/v2/frame/g"
    "github.com/gogf/gf/v2/os/gctx"
)

func main() {
    var (
        ctx    = gctx.New()
        client = g.Client()
    )
    client.SetPrefix("http://127.0.0.1:8199")
    for i := 0; i < 10; i++ {
        fmt.Println(client.PostContent(ctx, "/api/v1/user/info", `{"Path":"user/profile"}`))
    }
}

根据我们的理解,客户端提交后,服务端应该会收到路由参数 path 的值为 user/info。但是很奇怪,由于程序 BUG,客户端提交的 Body 中同样提交了一个 JSON,参数名称为 Path,参数值为 user/profile。那么这个时候服务端 API 对象中的属性 Path 将会是哪个值呢?

我们将客户端反复执行多次,发现输出的结果都是:

{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}
{"code":0,"message":"","data":{"Path":"user/profile"}}

即参数都是 Body 中提交的值,而不是路由参数中的值。其实, 由于参数大小写敏感的缘故,服务端这个时候接受到了两个参数,一个是路由参数 path,一个是 Body JSON 提交的 Path。服务端在转换 API 参数对象的时候,由于属性 Path 和 Body JSON 中提交的 Path 更匹配,因此,使用的是 Body JSON 中的 Path 的值。

相关文档

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk MEDIUM
  • Gen Agent Trust Hub16d

    The skill provides a comprehensive development guide and code examples for the GoFrame v2 framework. While highly informative for Go developers, it contains several examples with hardcoded placeholder secrets, tokens, and specific local file paths to sensitive configuration files (like Kubernetes credentials). These elements are provided for demonstration purposes but represent insecure practices if used in production without modification.

  • Socket16d

    5 alerts: gptSecurity, gptAnomaly

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    227/949 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 911705d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 4 months ago.

Steadyupdated 4 months ago
  • Go
  • Database
  • goframe
  • orm
  • http
  • grpc
  • microservices
  • crud
  • scaffolding
  • middleware

README badge

README badge for gogf/skills

Provides instruction and conventions for developing with GoFrame v2, a modular Go framework for building HTTP and gRPC services, microservices, and database operations using its ORM, CLI tooling, and project scaffolding. Use this skill when the target project has GoFrame v2 as a dependency or when the user requests scaffolding, migrations, or service development with GoFrame conventions.

Generated from the current SKILL.md.

When should I use this skill?
Use this skill only when your Go project uses GoFrame v2, indicated by github.com/gogf/gf/v2 in go.mod or imports. Do not use for generic Go projects, frontend work, or infrastructure tasks unrelated to GoFrame.
Should I manually set created_at and updated_at fields?
No. GoFrame automatically writes created_at on insert and updated_at on insert/update/save. Manually setting these fields is redundant and violates project conventions.
What should I use for database operations instead of g.Map?
Always use DO objects from internal/model/do/. DO struct fields are interface{}, unset fields remain nil and are automatically ignored by the ORM.
How does soft delete work in GoFrame?
Call Delete() on the DAO; GoFrame automatically converts it to UPDATE SET deleted_at = NOW(). Queries automatically filter out soft-deleted rows without requiring manual WhereNull conditions.
Where should I implement business logic?
Implement business logic directly in the service/ directory. Do not use the logic/ directory unless explicitly requested.

Generated from the current SKILL.md. These answers refresh after source changes.