Documentation
¶
Overview ¶
Package ginx 提供与业务无关的通用 gin 请求参数处理能力:解决 gin 请求 body 只能读取一次的痛点 (读取后回填,使后续 ShouldBind 仍可读)、按 Content-Type 解析 body、单值 header 校验、只绑 body 不混入 query,以及用 http.MaxBytesReader 对 body 设硬上限等。
这些函数不含任何业务语义,按请求在 gin.Context 上缓存解析结果,适合作为各服务的通用请求处理底座。
支持的 body 类型为 application/json 与 application/x-www-form-urlencoded;不支持 multipart/form-data(通常携带文件、体积大,与按 MaxBodyBytes 轻量探取的定位冲突)。
并发安全:与 gin.Context 本身一致。本包函数会替换 c.Request.Body、临时修改 URL.RawQuery, 必须在处理该请求的 handler goroutine 内调用,不得跨 goroutine 并发操作同一个 Context。
Index ¶
- Constants
- Variables
- func BindBody(c *gin.Context, obj any) error
- func BindBodyCached(c *gin.Context, obj any) error
- func Body[T Scalar](c *gin.Context, field string, def T) T
- func BodyString(c *gin.Context, field string) string
- func ContextValue[T any](c *gin.Context, key string) (val T, ok bool)
- func Header[T Scalar](c *gin.Context, key string, def T) T
- func IsRequestBodyTooLarge(err error) bool
- func LimitRequestBody(c *gin.Context, maxBytes int64)
- func Param[T Scalar](c *gin.Context, key string, def T) T
- func Query[T Scalar](c *gin.Context, key string, def T) T
- func QuerySlice[T Scalar](c *gin.Context, key string) []T
- func QuerySliceStrict[T Scalar](c *gin.Context, key string) ([]T, error)
- func QueryStrict[T Scalar](c *gin.Context, key string) (T, error)
- func RawBody(c *gin.Context) ([]byte, error)
- func RequireContentType(c *gin.Context, types ...string) error
- func SingleValueHeader(c *gin.Context, headerKey string) (string, error)
- func SingleValueQuery(c *gin.Context, key string) (string, error)
- type BodySources
- type Scalar
Examples ¶
Constants ¶
const ( // ContentTypeJSON 即 application/json。 ContentTypeJSON = "application/json" // ContentTypeForm 即 application/x-www-form-urlencoded。 ContentTypeForm = "application/x-www-form-urlencoded" )
本包支持解析的两种 body Content-Type,供调用方传给 RequireContentType 等,避免手写字符串。
const MaxBodyBytes = 8 * 1024
MaxBodyBytes 是 ParseBody 允许读取并解析的请求 body 软上限,超过则视为不可解析、返回空结果。 它只约束本包的解析行为;如需对整个请求生命周期施加硬上限,请用 LimitRequestBody。
const Version = "v1.2.0"
Version 是 ginx 的当前版本号。
Variables ¶
var ( // ErrDuplicateHeader 表示同一 header 出现了多个非空值。 ErrDuplicateHeader = errors.New("header provided multiple times") // ErrInvalidHeaderValue 表示 header 的值非法(如含逗号的多值形式)。 ErrInvalidHeaderValue = errors.New("header contains invalid value") // ErrInvalidBindContext 表示 BindBody 收到的 context / Request / URL 为 nil,无法绑定。 ErrInvalidBindContext = errors.New("invalid bind body context") // ErrNoBody 表示请求没有可解析的 body:context 或 body 为 nil,或方法为 GET/HEAD。 ErrNoBody = errors.New("request has no parsable body") // ErrBodyTooLarge 表示请求 body 长度超过 MaxBodyBytes 软上限,ParseBody 拒绝解析。 ErrBodyTooLarge = errors.New("request body exceeds MaxBodyBytes") // ErrUnsupportedContentType 表示 Content-Type 不在 ParseBody 支持的类型范围内。 ErrUnsupportedContentType = errors.New("unsupported content type") // ErrMalformedBody 表示 body 语法非法:JSON 语法错误、JSON 尾部存在多余数据或 form 编码非法, // 底层解析错误可经 errors.As / Unwrap 获取。 ErrMalformedBody = errors.New("malformed request body") )
var ( // ErrDuplicateQuery 表示同名 query 参数出现了多个非空值(HTTP 参数污染)。 ErrDuplicateQuery = errors.New("query parameter provided multiple times") // ErrQueryMissing 表示严格取值时 query 参数缺失或去空白后为空。 ErrQueryMissing = errors.New("query parameter missing") // ErrInvalidQueryValue 表示严格取值时 query 值无法解析为目标类型,错误中附带该值。 ErrInvalidQueryValue = errors.New("query parameter has invalid value") )
Functions ¶
func BindBody ¶
BindBody 仅将请求 body 绑定到 obj。绑定前临时清空 URL.RawQuery,避免 gin 在 form 模式下把 query 参数一并并入绑定结果——确保只信任 body、不信任 query;绑定结束后恢复 RawQuery。 context、Request 或 URL 为 nil 时返回 ErrInvalidBindContext。
绑定会消费 body 流(一次性语义):同一请求内第二次调用将读到空 body。如需先探取字段再绑定, 先调用 ParseBody(它会回填 body)、再调用本函数。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", `{"token":"body-token"}`)
c.Request.URL.RawQuery = "token=query-token" // query 不会被并入绑定结果
var req struct {
Token string `json:"token" form:"token"`
}
if err := ginx.BindBody(c, &req); err != nil {
fmt.Println("bind:", err)
return
}
fmt.Println(req.Token)
}
Output: body-token
func BindBodyCached ¶
BindBodyCached 基于 RawBody 缓存的原始字节将 body 绑定到 obj:同一请求内可重复调用,每次绑定 都读到完整 body,并继承 BindBody 的只信 body、不信 query 语义。首次调用会完整读取并缓存 body, 长度防御同样依赖 LimitRequestBody。context、Request 或 URL 为 nil 时返回 ErrInvalidBindContext。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", `{"token":"t1","scene":"s1"}`)
var a struct {
Token string `json:"token"`
}
var b struct {
Scene string `json:"scene"`
}
_ = ginx.BindBodyCached(c, &a) // 同一请求可重复绑定
_ = ginx.BindBodyCached(c, &b)
fmt.Println(a.Token, b.Scene)
}
Output: t1 s1
func Body ¶ added in v1.1.2
Body 从请求 body 中读取指定 field 并解析为 T。按 JSON → Form 的顺序查找 field。 值缺失、类型不匹配、body 不可解析(含 GET/HEAD、Content-Type 不匹配等)时返回 def, 不 panic、不返回错误。需要严格校验时请先 ParseBody 再自行处理。
本函数通过 ParseBody 缓存,同一请求内多次调用或与 BodyString 混用只解析一次 body。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", `{"count":3}`)
fmt.Println(ginx.Body(c, "count", 0))
}
Output: 3
func BodyString ¶
BodyString 从请求 body 中读取指定字段并转为字符串:JSON body 支持 string/bool/number/Stringer 等标量类型,form body 取对应键值;body 不可解析时返回空串。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/x-www-form-urlencoded", "token=abc123")
fmt.Println(ginx.BodyString(c, "token"))
}
Output: abc123
func ContextValue ¶ added in v1.1.2
ContextValue 从 gin.Context 的键值存储中读取 key 关联的值并以类型 T 返回。 常用于中间件注入的值(如用户 ID、角色等);key 不存在或类型不匹配时返回 (zero, false)。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", "{}")
c.Set("user_id", int64(42))
uid, ok := ginx.ContextValue[int64](c, "user_id")
fmt.Println(uid, ok)
}
Output: 42 true
func Header ¶ added in v1.1.2
Header 读取单值 header 并解析为 T。去空白后忽略空值,值含逗号视为非法、多个非空值视为重复, 均返回 def。需要严格校验时请使用 SingleValueHeader。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", "{}")
c.Request.Header.Set("X-Page", "42")
fmt.Println(ginx.Header(c, "X-Page", int64(0)))
}
Output: 42
func IsRequestBodyTooLarge ¶
IsRequestBodyTooLarge 判断 err 链中是否含 *http.MaxBytesError,即请求 body 是否超过了 LimitRequestBody 设定的硬上限,便于上游据此返回 413 Request Entity Too Large。
func LimitRequestBody ¶
LimitRequestBody 用 http.MaxBytesReader 为请求 body 设置硬上限 maxBytes:调用后,任何对 body 的读取(包括下游 c.ShouldBind / BindBody)一旦累计超过 maxBytes,都会立即返回 *http.MaxBytesError 并停止继续读入内存,从根上防御超大 body 撑爆内存,必要时还会关闭连接。 这与软上限 MaxBodyBytes 不同:MaxBodyBytes 仅用于本包解析时按长度跳过、不约束下游读取,而本函数的 限制对整个请求生命周期生效。应在读取或绑定 body 之前调用(如路由进入处或 handler 开头)。 maxBytes <= 0 或无 body 时直接返回、不设限。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", `{"data":"`+strings.Repeat("a", 64)+`"}`)
ginx.LimitRequestBody(c, 16) // 16 字节硬上限
var req struct {
Data string `json:"data"`
}
err := ginx.BindBody(c, &req)
fmt.Println(ginx.IsRequestBodyTooLarge(err))
}
Output: true
func Param ¶
Param 从路由参数(如 /user/:id 的 id)读取 key 并解析为 T,缺失与解析失败的回退语义同 Query。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", "{}")
c.Params = gin.Params{{Key: "id", Value: "42"}} // 路由 /user/:id
fmt.Println(ginx.Param(c, "id", int64(0)))
}
Output: 42
func Query ¶
Query 从 query 参数读取 key 并解析为 T:值缺失、去空白后为空、或解析失败时返回 def, 不 panic、不返回错误。需要严格校验时请使用 gin 的 ShouldBindQuery 配合 binding tag。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", "{}")
c.Request.URL.RawQuery = "page=3&size=abc"
fmt.Println(ginx.Query(c, "page", 1), ginx.Query(c, "size", 20), ginx.Query(c, "dry_run", false))
}
Output: 3 20 false
func QuerySlice ¶ added in v1.1.2
QuerySlice 从 query 参数读取 key 的多个值并返回类型 T 的切片。 例如 ?id=1&id=2&id=3 可经 QuerySlice[int64](c, "id") 取得 []int64{1,2,3}。 非空字符串总是有效;非 string 类型的值中解析失败的条目被静默跳过。 key 不存在、context 为 nil 或无有效值时返回 nil。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", "{}")
c.Request.URL.RawQuery = "id=1&id=2&id=3"
ids := ginx.QuerySlice[int64](c, "id")
fmt.Println(ids[0], ids[1], ids[2])
}
Output: 1 2 3
func QuerySliceStrict ¶ added in v1.2.0
QuerySliceStrict 严格版 QuerySlice:?id=1&id=2&id=3 取得 []T,遇到任一无法解析为 T 的值 (含去空白后为空的值)立即返回附带该值的 ErrInvalidQueryValue,不静默跳过。适合接口入参校验。 key 不存在、context 为 nil 时返回 (nil, nil),由调用方按 len 判断是否缺失。
func QueryStrict ¶ added in v1.2.0
QueryStrict 严格读取并类型化单值 query 参数:复用 SingleValueQuery 的查重语义,同名参数出现 多个非空值时返回 ErrDuplicateQuery(防 HTTP 参数污染);缺失或去空白后为空返回 ErrQueryMissing; 解析为 T 失败返回附带原值的 ErrInvalidQueryValue。三种情形均可用 errors.Is 判定,便于上层返回 400。 需要宽松取值(缺失/非法静默回退默认值)时用 Query。
func RawBody ¶
RawBody 读取并返回完整的原始请求 body:读取后回填 body(后续 BindBody / ShouldBind / ParseBody 仍可完整读取),并按请求缓存,同一请求内重复调用不再读流。适合 webhook 验签等先取原始字节、 再绑定结构体的场景。
本函数不设长度上限:请配合 LimitRequestBody 使用,超过硬上限时返回的错误可用 IsRequestBodyTooLarge 判定。context、Request 或 body 为 nil 时返回 ErrNoBody; 读取失败时如实返回错误且不缓存。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
// webhook 验签场景:先取原始字节算签名,再绑定结构体,两者不互斥
c := newPostContext("application/json", `{"event":"pay.success"}`)
raw, err := ginx.RawBody(c)
if err != nil {
fmt.Println("read:", err)
return
}
// verifySignature(raw, c.GetHeader("X-Signature")) ...
var notify struct {
Event string `json:"event"`
}
if err := ginx.BindBody(c, ¬ify); err != nil {
fmt.Println("bind:", err)
return
}
fmt.Println(len(raw) > 0, notify.Event)
}
Output: true pay.success
func RequireContentType ¶
RequireContentType 校验请求的 Content-Type(忽略参数与大小写)是否在 types 白名单内:命中返回 nil;不命中返回可用 errors.Is 判定 ErrUnsupportedContentType 的错误并附实际类型,便于上游返回 415 Unsupported Media Type。types 为空时不做约束、返回 nil;context 或 Request 为 nil 视为 无 Content-Type。
Example ¶
package main
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("text/plain", "hello")
err := ginx.RequireContentType(c, "application/json", "application/x-www-form-urlencoded")
fmt.Println(errors.Is(err, ginx.ErrUnsupportedContentType))
}
Output: true
func SingleValueHeader ¶
SingleValueHeader 读取并校验单值 header:去空白后忽略空值,值含逗号视为非法 (ErrInvalidHeaderValue),出现多个非空值视为重复(ErrDuplicateHeader)。 恰好一个非空值时返回该值,全部为空时返回空串且无错误。
Example ¶
package main
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", "{}")
c.Request.Header.Add("X-Token", "a")
c.Request.Header.Add("X-Token", "b")
_, err := ginx.SingleValueHeader(c, "X-Token")
fmt.Println(errors.Is(err, ginx.ErrDuplicateHeader))
}
Output: true
func SingleValueQuery ¶
SingleValueQuery 读取并校验单值 query 参数:去空白后忽略空值,出现多个非空值视为参数污染、 返回 ErrDuplicateQuery。恰好一个非空值时返回该值,全部为空时返回空串且无错误。query 值中的 逗号视为合法(逗号合并是 header 的语义,query 没有)。context、Request 或 URL 为 nil 时 返回空串且无错误。
Example ¶
package main
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", "{}")
c.Request.URL.RawQuery = "id=1&id=2" // HTTP 参数污染
_, err := ginx.SingleValueQuery(c, "id")
fmt.Println(errors.Is(err, ginx.ErrDuplicateQuery))
}
Output: true
Types ¶
type BodySources ¶
BodySources 承载一次请求 body 的解析结果:Available 表示是否解析成功,JSON 与 Form 分别对应 application/json 与 application/x-www-form-urlencoded 两种 body 的解析产物(另一种为 nil)。 JSON 中的数字以 json.Number 承载(UseNumber 解码),原样保留 body 中的字面量。
Err 在 Available 为 false 时说明失败原因,可用 errors.Is 判定 ErrNoBody、ErrBodyTooLarge、 ErrUnsupportedContentType、ErrMalformedBody(body 读取失败时为相应读取错误的包装); Available 为 true 时恒为 nil。结果按请求缓存,同一请求内重复调用返回相同的 Err。
func ParseBody ¶
func ParseBody(c *gin.Context) (sources BodySources)
ParseBody 解析请求 body 并按请求缓存结果(同一请求内多次调用只读取/解析一次 body)。命中缓存直接 返回;否则经跳过判断(nil body、GET/HEAD、ContentLength 超 MaxBodyBytes)、限长读取并回填 body 后 按 Content-Type 解析。任一环节失败或不支持的类型均返回 Available 为 false 的结果, 失败原因经 Err 字段透出(见 BodySources)。
Example ¶
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("application/json", `{"name":"alice"}`)
src := ginx.ParseBody(c)
fmt.Println(src.Available, src.JSON["name"])
}
Output: true alice
Example (FailureReason) ¶
package main
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"github.com/gin-gonic/gin"
"github.com/gtkit/ginx"
)
// newPostContext 构造一个带 body 的 POST 请求测试上下文,仅用于 Example。
func newPostContext(contentType, body string) *gin.Context {
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(httptest.NewRecorder())
req := httptest.NewRequest(http.MethodPost, "/demo", strings.NewReader(body))
req.Header.Set("Content-Type", contentType)
c.Request = req
return c
}
func main() {
c := newPostContext("text/plain", "hello")
src := ginx.ParseBody(c)
fmt.Println(src.Available, errors.Is(src.Err, ginx.ErrUnsupportedContentType))
}
Output: false true