httpx

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

Documentation

Overview

Package httpx 提供 HTTP 层的通用件:RFC 9457 错误响应与请求上下文工具。

Index

Constants

View Source
const ClientIPHeader = "X-Lumo-Client-IP"

ClientIPHeader 是内部传递客户端 IP 的请求头名。

View Source
const ContentTypeProblem = "application/problem+json"

ContentTypeProblem 是 RFC 9457 规定的错误响应媒体类型。

Variables

View Source
var LoopbackCIDRs = []string{"127.0.0.0/8", "::1/128"}

LoopbackCIDRs 是仅信任本机回环的默认配置,适用于「反向代理与应用同机」的常见部署。

View Source
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

func ClientIPFrom(req *http.Request) string

ClientIPFrom 读取中间件注入的客户端 IP,缺失时回退到直连对端。

func ClientIPFromContext

func ClientIPFromContext(ctx context.Context) string

ClientIPFromContext 读取中间件注入 context 的客户端 IP;未经过中间件时返回空串。

func Error

func Error(w http.ResponseWriter, r *http.Request, status int, detail string)

Error 是 WriteProblem 的便捷形式。

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

func UserAgentFromContext(ctx context.Context) string

UserAgentFromContext 取出请求的 User-Agent,未设置时返回空串。

func WithUserAgent

func WithUserAgent(ctx context.Context, ua string) context.Context

WithUserAgent 把请求的 User-Agent 放入 context,供评论模块留痕。

func WriteError

func WriteError(w http.ResponseWriter, r *http.Request, err error, logger *slog.Logger)

WriteError 把任意 error 转成 problem 响应。

若 err 链上存在 *Problem 则沿用其状态与说明;否则统一按 500 处理, 并且**不把内部错误细节回传客户端**,只记入日志,避免泄漏实现信息。

func WriteJSON

func WriteJSON(w http.ResponseWriter, r *http.Request, status int, payload any)

WriteJSON 以 application/json 写出成功响应。

func WriteProblem

func WriteProblem(w http.ResponseWriter, r *http.Request, problem *Problem, logger *slog.Logger)

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

func NewProblem(status int, detail string) *Problem

NewProblem 构造一个带状态码与说明的 Problem。

func (*Problem) ContentType

func (p *Problem) ContentType(string) string

ContentType 实现 huma.ContentTypeFilter:错误响应固定为 problem+json。

func (*Problem) Error

func (p *Problem) Error() string

Error 让 Problem 可直接作为 error 使用与包装。

func (*Problem) GetStatus

func (p *Problem) GetStatus() int

GetStatus 实现 huma.StatusError:从处理器返回时决定响应状态码。

func (*Problem) MarshalJSON

func (p *Problem) MarshalJSON() ([]byte, error)

MarshalJSON 把标准成员与扩展成员合并为单层对象。

Jump to

Keyboard shortcuts

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