contract

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package contract 框架核心契约:统一 Handler 模板、路由注册描述与基础类型。 业务层(app/handlers)通过别名引用本包类型,运行时(internal/server)与 文档生成(internal/openapi)通过本包消费路由注册表。

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound 资源不存在(运行时适配器映射为 HTTP 404)。 需要携带对外信息时优先使用 contract.NotFound(msg)(StatusError)。

Functions

func BadRequest

func BadRequest(msg string) error

BadRequest 400 请求错误

func BindFail

func BindFail(status int) (int, int)

BindFail 绑定/校验失败响应的 (status, code):默认 200 + CodeError(存量行为); 自定义非 200 状态码时 code 跟随状态码(与 StatusError 约定一致)。

func CheckHandler

func CheckHandler(fn any) error

CheckHandler 校验 Handler 是否符合统一模板 func(context.Context, Q, B) (R, error)。 适配器与文档生成器在挂载/生成期调用:签名错误尽早暴露并给出可读信息, 避免拖到反射调用期才 panic 且信息晦涩。

func CheckTarget

func CheckTarget(t reflect.Type, v reflect.Value) any

CheckTarget 校验入参目标:接口类型占位(NoReq/any 等)返回 nil(validator.isNil 跳过); 具体结构体返回其指针(与绑定阶段一致)

func Conflict

func Conflict(msg string) error

Conflict 409 冲突

func DefaultErrorMapper

func DefaultErrorMapper(err error) (httpStatus, bizCode int)

DefaultErrorMapper 默认兑底映射:ErrNotFound → 404;其余业务错误 → HTTP 200 + code=7。

func Forbidden

func Forbidden(msg string) error

Forbidden 403 无权限

func Framework

func Framework(ctx context.Context) any

Framework 取出框架上下文对象;未注入时返回 nil

func FuncName

func FuncName(fn any) string

FuncName 反射提取函数名(稳定标识,供文档钩子匹配)。 返回「包.函数」形态(如 middleware.Auth),比裸函数名更不易跨包撞名。

func HasParamBinder

func HasParamBinder(t reflect.Type) bool

HasParamBinder 判断类型是否注册过自定义参数绑定器。 openapi 生成器据此把参数 schema 标注为 string——绑定器类型的 HTTP 形态是原始字符串 (逗号串、ID 等),而非其 Go 类型的 JSON 形态。

func Internal

func Internal(msg string) error

Internal 500 内部错误(对外只暴露 msg,内部细节用 WithCause 附加)

func IsBodyMethod

func IsBodyMethod(method string) bool

func NewStatusError

func NewStatusError(status int, msg string) error

NewStatusError 构造携带自定义状态码的错误

func NewValue

func NewValue(t reflect.Type) reflect.Value

func NotFound

func NotFound(msg string) error

NotFound 404 资源不存在(替代直接使用 ErrNotFound 哨兵值,可携带信息)

func RegisterParamBinder

func RegisterParamBinder[T any](fn func(src []string) (T, error))

RegisterParamBinder 注册某字段类型的自定义绑定器。 T 为字段声明类型(如 IDs、*User);同名类型重复注册覆盖。

func ResolveError

func ResolveError(mapError func(err error) (httpStatus, bizCode int), err error) (int, int, string)

ResolveError 业务错误解析:错误自带状态码优先,否则调用 mapError 兑底 (mapError 由适配器注入,Server.SetErrorMapper;传 nil 则用 DefaultErrorMapper)。

func ResolveErrorStatus

func ResolveErrorStatus(err error) (status, code int, msg string, ok bool)

ResolveErrorStatus 提取错误自带的状态信息(StatusError → StatusCoder)。 ok=false 表示普通错误,由调用方决定兑底策略(业务错误走 errorMapper,绑定错误走 bindStatus)。

func SetRaw

func SetRaw(f reflect.Value, raw, name string) error

把原始字符串解析写入字段(query/path/header 共用)。 支持基本类型、指针(自动分配)、time.Time(RFC3339)与切片(逗号分隔或重复参数); 声明了绑定标签但类型不支持时返回错误(避免静默丢值难以排查)。

func SetRawBasic

func SetRawBasic(f reflect.Value, raw, name string) error

标量绑定(不含指针/切片解包)

func SetSliceValue

func SetSliceValue(f reflect.Value, vals []string, name string) error

绑定切片字段:vals 为收集到的原始值(重复参数或逗号分隔展开后)。 []string 保持原样;其他元素类型逐个解析,失败即报错。

func TagValue

func TagValue(ft reflect.StructField, key string) (string, bool)

func TransformIn

func TransformIn(ctx context.Context, v any) error

TransformIn 入参转换(适配器内部使用):v 为绑定后的入参(*Q / *B 指针)。

func TransformOut

func TransformOut(ctx context.Context, v reflect.Value) (reflect.Value, error)

TransformOut 出参转换(适配器内部使用):v 为 Handler 返回的响应值 (reflect.Call 的返回值,值类型可能不可寻址)。 返回转换后的值;指针接收者实现且为值类型时,写回拷贝。

func Unauthorized

func Unauthorized(msg string) error

Unauthorized 401 未认证

func WithCause

func WithCause(statusErr error, cause error) error

WithCause 给状态错误附加内部原因(err 只进日志/错误链,不对外)

func WithFramework

func WithFramework(ctx context.Context, fw any) context.Context

WithFramework 注入框架上下文对象:适配器在 decorate 阶段把 gin.Context / echo.Context 存入 context,业务层按需断言使用 代价是业务层与框架耦合,仅限无法模板化的少数场景。

Types

type AdaptHandler

type AdaptHandler[Q, B, R any] = func(context.Context, Q, B) (R, error)

AdaptHandler 统一 Handler 模板:

func(ctx context.Context, query Q, body B) (resp R, err error)

type Empty

type Empty any

Empty 无响应数据的操作使用该类型占位(序列化为 data: null)

type FieldMeta

type FieldMeta struct {
	Index    int
	Kind     reflect.Kind
	Path     string
	Query    string
	Form     string
	Header   string
	Def      string      // default 标签:绑定缺失时的运行时默认值(与文档 default 同步生效)
	Children []FieldMeta // 内嵌结构体递归展平
}

FieldMeta 绑定字段元数据(挂载期解析一次,请求期零反射)。 与框架无关:path/query/form/header/default 标签由各框架适配器按同一语义消费, 框架侧只保留"从请求取原始值"的取值函数(如 gin 的 c.Query / echo 的 c.QueryParam)。

func ParseFields

func ParseFields(t reflect.Type) []FieldMeta

ParseFields 反射解析结构体字段元数据(内嵌结构体递归展平)。 挂载期调用一次即缓存;各框架适配器共享同一份解析结果。

type FileStream

type FileStream struct {
	Name        string // 下载文件名(Content-Disposition)
	Size        int64  // 内容长度
	ContentType string // 如 application/octet-stream、text/plain
	Reader      io.Reader
}

FileStream 二进制下载响应。运行时适配器识别该类型后直接输出流, 数据源可以是文件、go:embed 内存数据或任何 io.Reader(见 app/handlers/file.go 示例)。

type Group

type Group struct {
	Prefix      string
	Description string
	Tags        []string
	Middlewares []any // 框架中间件函数;名字由反射派生(见 FuncName),文档侧按名匹配钩子
	Routes      []Route
	Children    []*Group
}

Group 路由分组(树形):对应 gin.RouterGroup / echo.Group 的嵌套模型。

  • Prefix 为空表示根组
  • 组内 Route.Path 为相对组前缀的路径(列表路由用 "" 表示组根)
  • Middlewares 沿树向子组继承(运行时与文档生成行为一致)
  • 文档侧 Tags 合并:组 Tags + 路由 Tags

type InTransformer

type InTransformer interface {
	InTransform(context.Context) error
}

InTransformer 入参转换接口:Q/B 绑定完成后、校验之前被适配器自动调用。 用于规范化输入(trim、大小写归一、默认值填充);返回错误将短路请求。

实现建议使用指针接收者(与 B 的绑定值一致,修改直接生效):

func (r *CreateUserReq) InTransform(ctx context.Context) error {
    r.Name = strings.TrimSpace(r.Name)
    return nil
}

type NoReq

type NoReq any

NoReq 无入参的 Handler 使用该类型占位

type OutTransformer

type OutTransformer interface {
	OutTransform(context.Context) error
}

OutTransformer 出参转换接口:Handler 返回后、序列化之前被适配器自动调用。 用于输出加工(脱敏、裁剪字段、补充计算字段);返回错误将短路请求。

建议使用指针接收者;值接收者也可用(只读场景),但修改不会写回:

func (u *User) OutTransform(ctx context.Context) error {
    u.Password = "******"
    return nil
}

type ParamBinder

type ParamBinder func(src []string) (any, error)

ParamBinder 把 query/path/header 的原始字符串解析为字段值。 src:原始值列表(单值参数长度 1;重复参数 ?ids=1&ids=2 与逗号串由实现自行处理)。 返回值必须可赋给字段声明类型;错误建议使用 StatusError(如 NotFound/BadRequest), 会汇入统一错误链(HTTP 状态码 + 响应壳)。

注册方式(通常在 init 或装配阶段):

contract.RegisterParamBinder(func(src []string) (IDs, error) { ... })

func BinderFor

func BinderFor(t reflect.Type) (ParamBinder, bool)

BinderFor 返回类型的自定义绑定器;未注册返回 false(适配器内部使用)。

type Response

type Response[R any] struct {
	Status  int
	Headers map[string]string
	Cookies []*http.Cookie
	Data    R
}

Response 响应定制壳:业务层返回 contract.Response[R] 时, 适配器应用 Status/Headers/Cookies 后,Data 仍走统一 envelope {code, data, msg}。 例:return contract.Response[handlers.User]{Status: 201, Headers: ..., Data: u}, nil

func (Response[R]) ResponseCookies

func (r Response[R]) ResponseCookies() []*http.Cookie

func (Response[R]) ResponseData

func (r Response[R]) ResponseData() any

func (Response[R]) ResponseHeaders

func (r Response[R]) ResponseHeaders() map[string]string

func (Response[R]) ResponseStatus

func (r Response[R]) ResponseStatus() int

type ResponseWrapper

type ResponseWrapper interface {
	ResponseStatus() int
	ResponseHeaders() map[string]string
	ResponseCookies() []*http.Cookie
	ResponseData() any
}

ResponseWrapper 适配器识别接口:泛型实例通过该接口被统一处理

type Route

type Route struct {
	Method            string
	Path              string
	Summary           string
	Description       string
	Tags              []string
	DefaultStatusCode int
	Deprecated        bool
	Envelope          response.Envelope
	Handler           any // func(context.Context, Q, B) (R, error)
}

Route 路由表条目(非泛型;Handler 由适配器通过反射消费,Q/B/R 泛型信息由 New 在构造期保证)

func New

func New[Q, B, R any](m RouteMeta[Q, B, R]) Route

New 以强类型方式构造路由表条目

type RouteMeta

type RouteMeta[Q, B, R any] struct {
	Method      string
	Path        string
	Summary     string
	Description string
	Tags        []string
	// DefaultStatusCode 成功响应默认 HTTP 状态码;0 → 200。
	// 动态覆盖优先级:contract.Response[R].Status > DefaultStatusCode > 200。
	// OpenAPI 文档生成器读取该值作为成功响应码(替代硬编码 200)。
	DefaultStatusCode int
	// Deprecated 弃用标记(运行时+文档共用层):
	// 文档生成 op.Deprecated=true;运行时后续可在 dev 模式对弃用接口打日志。
	Deprecated bool
	// Envelope 路由级响应壳;nil → 服务级默认壳(server.SetEnvelope)。
	// 文档侧壳 schema 用 openapi.OptionWithEnvelopeSchema 配对配置。
	Envelope response.Envelope
	Handler  AdaptHandler[Q, B, R]
}

RouteMeta 路由注册描述(强类型)。 鉴权等中间件效果不在本结构声明:中间件挂载在所属 Group(见 Group.Middlewares), 文档标注由 internal/openapi 按中间件函数名匹配文档钩子(RegisterMiddlewareDoc)。

type Server

type Server interface {
	Mount(g any, groups []*Group)
}

Server 运行时适配器接口规范:各框架适配器(servergin / serverecho) 提供具体实现,业务装配层面向该接口编程时可互换适配器。 作为 server 实现的示范存在:适配器可参考其形态提供自己的 Mount 签名。

type StatusCoder

type StatusCoder interface {
	error
	StatusCode() int
}

StatusCoder 最小契约:任何错误实现该接口即可携带 HTTP 状态码。 适配器错误路径按优先级识别:StatusError → StatusCoder → SetErrorMapper 全局映射

type StatusError

type StatusError struct {
	Status int
	Code   int
	Msg    string
	Err    error
}

StatusError 携带状态码/业务码/对外信息的错误。 业务层直接返回:return nil, contract.NotFound("用户不存在")

字段规则:

  • Status:HTTP 状态码;0 视为未设置,StatusCode() 兜底 500
  • Code:业务码;0 视为未设置,适配器沿用默认约定 (HTTP 200 → 默认业务错误码;非 200 → 跟随状态码,与现有 ErrNotFound 映射一致)
  • Msg:对外错误信息;空则回退 err.Error()
  • Err:内部错误(不对外暴露),支持 errors.Unwrap 链路穿透

func (*StatusError) Error

func (e *StatusError) Error() string

func (*StatusError) StatusCode

func (e *StatusError) StatusCode() int

StatusCode 实现 StatusCoder;0 → 500

func (*StatusError) Unwrap

func (e *StatusError) Unwrap() error

Unwrap 支持错误链穿透(fmt.Errorf("...: %w", err) 后仍可被 errors.As 识别)

Directories

Path Synopsis
Package response 统一响应数据壳、业务错误码约定与响应壳接口(零框架依赖)。
Package response 统一响应数据壳、业务错误码约定与响应壳接口(零框架依赖)。
Package validator 参数校验扩展点。
Package validator 参数校验扩展点。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL