Documentation
¶
Overview ¶
Package httpx 提供 HTTP 层的通用件:RFC 9457 错误响应与请求上下文工具。
Index ¶
- Constants
- Variables
- func BadRequest(w http.ResponseWriter, r *http.Request, detail string)
- func ClientIPFrom(req *http.Request) string
- func ClientIPFromContext(ctx context.Context) string
- func Error(w http.ResponseWriter, r *http.Request, status int, detail string)
- func Forbidden(w http.ResponseWriter, r *http.Request, detail string)
- func NotFound(w http.ResponseWriter, r *http.Request, detail string)
- func Unauthorized(w http.ResponseWriter, r *http.Request, detail string)
- func UserAgentFromContext(ctx context.Context) string
- func WithUserAgent(ctx context.Context, ua string) context.Context
- func WriteError(w http.ResponseWriter, r *http.Request, err error, logger *slog.Logger)
- func WriteJSON(w http.ResponseWriter, r *http.Request, status int, payload any)
- func WriteProblem(w http.ResponseWriter, r *http.Request, problem *Problem, logger *slog.Logger)
- type ClientIPResolver
- type ErrorDetail
- type Problem
Constants ¶
const ClientIPHeader = "X-Lumo-Client-IP"
ClientIPHeader 是内部传递客户端 IP 的请求头名。
const ContentTypeProblem = "application/problem+json"
ContentTypeProblem 是 RFC 9457 规定的错误响应媒体类型。
Variables ¶
var LoopbackCIDRs = []string{"127.0.0.0/8", "::1/128"}
LoopbackCIDRs 是仅信任本机回环的默认配置,适用于「反向代理与应用同机」的常见部署。
var PrivateCIDRs = []string{
"127.0.0.0/8", "::1/128",
"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16",
"169.254.0.0/16", "fc00::/7", "fe80::/10",
}
PrivateCIDRs 是 RFC 1918 与链路本地范围,适用于代理位于内网的部署。
Functions ¶
func BadRequest ¶
func BadRequest(w http.ResponseWriter, r *http.Request, detail string)
BadRequest 输出 400,detail 为面向调用方的可读原因。
func ClientIPFrom ¶
ClientIPFrom 读取中间件注入的客户端 IP,缺失时回退到直连对端。
func ClientIPFromContext ¶
ClientIPFromContext 读取中间件注入 context 的客户端 IP;未经过中间件时返回空串。
func Forbidden ¶
func Forbidden(w http.ResponseWriter, r *http.Request, detail string)
Forbidden 输出 403。
func NotFound ¶
func NotFound(w http.ResponseWriter, r *http.Request, detail string)
NotFound 输出 404。
func Unauthorized ¶
func Unauthorized(w http.ResponseWriter, r *http.Request, detail string)
Unauthorized 输出 401。
func UserAgentFromContext ¶
UserAgentFromContext 取出请求的 User-Agent,未设置时返回空串。
func WithUserAgent ¶
WithUserAgent 把请求的 User-Agent 放入 context,供评论模块留痕。
func WriteError ¶
WriteError 把任意 error 转成 problem 响应。
若 err 链上存在 *Problem 则沿用其状态与说明;否则统一按 500 处理, 并且**不把内部错误细节回传客户端**,只记入日志,避免泄漏实现信息。
func WriteProblem ¶
WriteProblem 以 application/problem+json 写出错误响应。
logger 可为 nil;序列化失败时降级为纯文本,保证客户端总能收到可解析的响应。
Types ¶
type ClientIPResolver ¶
type ClientIPResolver struct {
// contains filtered or unexported fields
}
ClientIPResolver 在已知可信代理范围的前提下解析真实客户端 IP。
为什么不能直接用 chi 的 middleware.RealIP:它无条件信任 X-Forwarded-For 与 X-Real-IP,任何客户端都能伪造这些头(GHSA-3fxj-6jh8-hvhx)。伪造的 IP 会污染日志、绕过限流、并让基于 IP 的审计失效。
正确做法是:只有当直连对端本身属于可信代理时,才采信转发头; 并从 X-Forwarded-For 右端向左跳过连续的可信代理,取第一个非可信地址。
func NewClientIPResolver ¶
func NewClientIPResolver(cidrs []string) (*ClientIPResolver, error)
NewClientIPResolver 按 CIDR 列表构造解析器。
传入空列表表示不信任任何代理,此时一律使用直连对端地址—— 这是最安全的默认值。
func (*ClientIPResolver) ClientIP ¶
func (r *ClientIPResolver) ClientIP(req *http.Request) string
ClientIP 返回请求的客户端 IP。
直连对端不可信时直接返回它,忽略所有转发头。
func (*ClientIPResolver) Middleware ¶
func (r *ClientIPResolver) Middleware(next http.Handler) http.Handler
Middleware 把解析出的客户端 IP 注入请求头 X-Lumo-Client-IP,供下游读取。
不修改 r.RemoteAddr:那正是 middleware.RealIP 的问题所在—— 覆盖原始值会让后续代码再也无法判断链路是否可信。
type ErrorDetail ¶
type ErrorDetail struct {
// Message 是可读的错误说明。
Message string `json:"message,omitempty" doc:"错误说明"`
// Location 是出错位置,如 body.title、query.page、path.id。
Location string `json:"location,omitempty" doc:"出错位置,如 body.title 或 query.page"`
// Value 是出错位置上的原始值,便于调用方排查。
Value any `json:"value,omitempty" doc:"出错位置上的原始值"`
}
ErrorDetail 是一条定位到具体位置的错误明细,主要用于请求校验失败。
type Problem ¶
type Problem struct {
// Type 是标识问题类型的 URI 引用,缺省 "about:blank"。
Type string `json:"type" doc:"标识问题类型的 URI 引用,缺省 about:blank"`
// Title 是问题类型的简短人类可读摘要。
Title string `json:"title" doc:"问题类型的简短摘要"`
// Status 是 HTTP 状态码,与响应状态行一致。
Status int `json:"status" doc:"HTTP 状态码,与响应状态行一致"`
// Detail 是本次具体发生的可读说明。
Detail string `json:"detail,omitempty" doc:"本次问题的具体说明"`
// Instance 标识本次问题实例,本项目填请求路径。
Instance string `json:"instance,omitempty" doc:"本次问题实例,填请求路径"`
// Errors 是逐条错误明细,请求校验失败时列出每个出错位置。
Errors []ErrorDetail `json:"errors,omitempty" doc:"错误明细,请求校验失败时逐条列出"`
// Extensions 是其余扩展成员,序列化时平铺到 JSON 顶层。
Extensions map[string]any `json:"-"`
}
Problem 是 RFC 9457 application/problem+json 的响应体,也是全站唯一的错误模型。
字段命名遵循规范:type / title / status / detail / instance 为标准成员, errors 是校验失败时的逐条明细,Extensions 承载其余扩展成员(如 requiredPermissions), 序列化时平铺到顶层。它同时实现 huma.StatusError 与 huma.ContentTypeFilter, 因此可以直接从 huma 操作处理器返回。
func NewProblem ¶
NewProblem 构造一个带状态码与说明的 Problem。
func (*Problem) ContentType ¶
ContentType 实现 huma.ContentTypeFilter:错误响应固定为 problem+json。
func (*Problem) MarshalJSON ¶
MarshalJSON 把标准成员与扩展成员合并为单层对象。