请求输入依靠 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 类型的字符串,将会按照 向下取整 进行整型转换)。
聪明的您一定发现了,获取到的参数都是 泛型变量,根据该泛型变量再根据需要调用对应的方法转换为对应的数据类型。
使用示例:
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 框架下,我们根据不同的获取方法,将会按照不同的优先级进行获取,优先级高的方式提交的参数将会优先覆盖其他方式的同名参数。优先级规则如下:
- Get及- GetRequset方法:- Router < Query < Body < Form < Custom,也就是说自定义参数的优先级最高,其次是- Form表单参数,再次是- Body提交参数,以此类推。例如,- Query和- Form中都提交了同样名称的参数- id,参数值分别为- 1和- 2,那么- Get("id")/- GetForm("id")将会返回- 2,而- GetQuery("id")将会返回- 1。
- GetQuery方法:- Query > Body,也就是说- query string的参数将会覆盖- Body中提交的同名参数。例如,- Query和- Body中都提交了同样名称的参数- id,参数值分别为- 1和- 2,那么- Get("id")将会返回- 2,而- GetQuery("id")将会返回- 1。
- 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 的值。