httpx

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 20, 2025 License: MIT Imports: 12 Imported by: 3

README

httpx: Minimalist HTTP Extensions for Go

Go Report Card

中文 | English

httpx is a set of HTTP extension tools based on Go 1.18+ generics. It is not a heavy web framework, but a powerful enhancement for the standard library net/http.

It aims to eliminate repetitive JSON decoding, parameter validation, and response encapsulation code in http.Handler through Generics, while maintaining full compatibility with the standard library.

Core Concepts

  1. Type Safety: Leveraging generics to transform func(w, r) into strongly typed func(ctx, *Req) (*Res, error).
  2. Modern Conventions: Uses String Codes (e.g., "OK", "INVALID_ARGUMENT") for business logic, distinct from HTTP Status Codes.
  3. Performance at Scale: Provides a SelfValidatable interface to bypass reflection validation; integrates httpsnoop for zero-overhead metric capture.

Installation

go get github.com/oy3o/httpx

Quick Start

1. Define Request & Response

Use json tags to define the structure and validate tags to define validation rules.

type LoginReq struct {
    Username string `json:"username" validate:"required,min=3"`
    Password string `json:"password" validate:"required"`
}

type LoginRes struct {
    Token string `json:"token"`
    User  string `json:"user_id"`
}
2. Write Business Logic

No need to manipulate http.ResponseWriter and *http.Request. Just focus on inputs and outputs.

import "github.com/oy3o/httpx"

func LoginHandler(ctx context.Context, req *LoginReq) (*LoginRes, error) {
    if req.Username == "admin" && req.Password == "123456" {
        return &LoginRes{Token: "abc-123", User: "1"}, nil
    }
    
    // Return an error. httpx automatically maps HTTP Status to a Business Code.
    // E.g., 401 -> "UNAUTHORIZED"
    // You can also define custom codes: httpx.NewError(401, "LOGIN_FAILED", "...")
    return nil, &HttpError{HttpCode: http.StatusUnauthorized, BizCode: CodeUnauthorized, Msg: "Unauthorized"} // httpx.ErrUnauthorized
}
3. Register Routes

Use httpx.NewHandler to convert business functions into standard http.Handlers.

func main() {
    mux := http.NewServeMux()

    // Automatic processing: Bind -> Validate -> Logic -> Response
    mux.Handle("POST /login", httpx.NewHandler(LoginHandler))

    // Add middleware: Recovery -> Logger -> CORS -> Handler
    handler := httpx.Chain(mux, 
        httpx.Recovery(nil),
        httpx.Logger(nil), // Uses httpsnoop
        httpx.DefaultCORS(),
    )

    http.ListenAndServe(":8080", handler)
}

Example Response:

{
    "code": "OK",
    "message": "success",
    "data": {
        "token": "abc-123",
        "user_id": "1"
    }
}

Error Response:

{
    "code": "UNAUTHORIZED",
    "message": "Unauthorized",
    "data": null
}

Core Features

1. Cooperative Binding

httpx.Bind is smart enough to handle multiple data sources simultaneously.

  • Metadata: URL Query parameters are always parsed (if the struct has matching tags).
  • Body: Automatically selects JSON or Form binding based on Content-Type.
  • Conflict Resolution: If a field exists in both Query and Body, the Body takes precedence.
2. Hybrid Validation

To balance development efficiency and runtime performance, httpx supports two validation modes:

  • Reflection Mode (Slow Path): Uses struct tags (validate:"required"). Fast development, but incurs reflection overhead.
  • Interface Mode (Fast Path): Implements the SelfValidatable interface. Completely reflection-free, suitable for high-traffic endpoints.
// Fast Validation: httpx calls this method first, skipping reflection
func (r *LoginReq) Validate(ctx context.Context) error {
    if len(r.Username) < 3 {
        return errors.New("username too short")
    }
    return nil
}
3. Semantic Error Handling

We separate Transport State (HTTP Status Code) from Business State (String Code).

  • If you return httpx.NewError(404, "USER_NOT_FOUND", "..."):
    • HTTP Status: 404
    • JSON Body: {"code": "USER_NOT_FOUND", ...}
  • If you return standard errors (e.g., os.ErrNotExist), httpx infers a default code (e.g., "INTERNAL_ERROR").
4. Flexible Response Options
  • Standard Mode: Default wrapper in {code: "OK", msg, data}.
  • No Envelope Mode: Use the httpx.NoEnvelope() option to return data directly (suitable for standard protocols like OAuth).
  • Streamable Response: Return values implementing the Streamable interface (e.g., FileResponse) handle file downloads or streaming writes automatically. Filenames in headers are safely escaped.

Middleware Ecosystem

httpx provides a set of middleware based on standard library interfaces:

Middleware Description
Chain Composes multiple middleware into an onion model.
Recovery Captures Panics to prevent service crashes; supports custom Hooks.
Logger Based on httpsnoop, accurately records status codes and latency; compatible with WebSocket/SSE.
CORS Flexible Cross-Origin Resource Sharing configuration.
RateLimit Simple rate limiting interface integration.
AuthBearer Bearer Token extraction. Supports custom Realm.
AuthBasic Basic Auth extraction. Supports custom Realm.

Best Practices

It is recommended to use httpx with netx (network layer extensions) and o11y (observability) to build complete Go services:

// server/run.go
func Run() {
    // 1. Network Layer (netx) - Limits, KeepAlive, Context
    ln := netx.Listen(":8080") 
    
    // 2. Application Layer (httpx) - Routing, Validation, Logic
    h := httpx.NewHandler(MyLogic)
    
    // 3. Start
    http.Serve(ln, h)
}

Documentation

Index

Constants

View Source
const (
	// CodeOK 表示成功
	CodeOK = "OK"

	// CodeInternalError 服务器内部错误 (500)
	CodeInternalError = "INTERNAL_ERROR"

	// CodeBadRequest 请求参数错误 (400)
	CodeBadRequest = "BAD_REQUEST"

	// CodeUnauthorized 未认证 (401)
	CodeUnauthorized = "UNAUTHORIZED"

	// CodeForbidden 无权限 (403)
	CodeForbidden = "FORBIDDEN"

	// CodeNotFound 资源不存在 (404)
	CodeNotFound = "NOT_FOUND"

	// CodeTooManyRequests 请求过多 (429)
	CodeTooManyRequests = "TOO_MANY_REQUESTS"

	// CodeConflict 资源冲突 (409)
	CodeConflict = "CONFLICT"

	// CodeValidation 校验失败 (400)
	CodeValidation = "VALIDATION_FAILED"
)
View Source
const IdentityKey contextKey = "identity"

Variables

View Source
var (
	ErrBadRequest      = &HttpError{HttpCode: http.StatusBadRequest, BizCode: CodeBadRequest, Msg: "Bad Request"}
	ErrUnauthorized    = &HttpError{HttpCode: http.StatusUnauthorized, BizCode: CodeUnauthorized, Msg: "Unauthorized"}
	ErrForbidden       = &HttpError{HttpCode: http.StatusForbidden, BizCode: CodeForbidden, Msg: "Forbidden"}
	ErrNotFound        = &HttpError{HttpCode: http.StatusNotFound, BizCode: CodeNotFound, Msg: "Not Found"}
	ErrTooManyRequests = &HttpError{HttpCode: http.StatusTooManyRequests, BizCode: CodeTooManyRequests, Msg: "Too Many Requests"}
	ErrInternal        = &HttpError{HttpCode: http.StatusInternalServerError, BizCode: CodeInternalError, Msg: "Internal Server Error"}
)
View Source
var Binders = []Binder{
	&queryBinder{},
	&jsonBinder{},
	&formBinder{},
}

Binders 默认链。 建议顺序:先 Meta 后 Body。这样如果字段冲突,Body 的值通常会覆盖 Query 的值(取决于 decoder 实现,但在逻辑上 Body 优先级更高)。

View Source
var ErrorHook func(ctx context.Context, err error) = nil

ErrorHook 是一个回调函数,用于处理错误的副作用(如记录日志)。 用户可以在 NewHandler 的 Option 中覆盖它。

View Source
var GetTraceID func(ctx context.Context) string = nil
View Source
var SchemaDecoder = schema.NewDecoder()
View Source
var Validator = validator.New()

Validator 是默认的验证器实例

Functions

func Bind

func Bind(r *http.Request, v any, binders ...Binder) error

Bind 执行绑定逻辑 (支持协同)

func Chain

func Chain(h http.Handler, mws ...Middleware) http.Handler

Chain 组合多个中间件

func Error

func Error(w http.ResponseWriter, r *http.Request, err error, errhooks ...func(ctx context.Context, err error))

Error 负责将 error 转换为 HTTP 响应并写入 ResponseWriter。

func GetIdentity

func GetIdentity(ctx context.Context) any

GetIdentity 从 Context 中获取身份信息。

func NewHandler

func NewHandler[Req any, Res any](fn HandlerFunc[Req, Res], opts ...Option) http.HandlerFunc

func Validate

func Validate(ctx context.Context, v any, validators ...*validator.Validate) error

Validate 执行验证逻辑。 v: 待验证的结构体指针 validatorInstance: 可选的验证器实例,如果为 nil 则使用 DefaultValidator

Types

type AuthValidator

type AuthValidator func(ctx context.Context, token string) (any, error)

AuthValidator 定义验证回调函数签名。 返回的 any 将被注入到 Context 中(例如 User 对象)。

type BasicValidator

type BasicValidator func(ctx context.Context, user, pass string) (any, error)

BasicValidator 定义 Basic Auth 验证回调。

type Binder added in v0.2.0

type Binder interface {
	Name() string
	Type() BinderType
	Match(r *http.Request) bool
	Bind(r *http.Request, v any) error
}

type BinderType added in v0.2.0

type BinderType int

BinderType 定义绑定器类型

const (
	// BinderMeta 表示绑定 URL Query, Header 等元数据 (非互斥)
	BinderMeta BinderType = iota
	// BinderBody 表示绑定 Request Body (互斥,流只能读一次)
	BinderBody
)

type BizCoder added in v0.2.0

type BizCoder interface {
	BizStatus() string
}

BizCoder 定义了如何提取业务错误码 (String)。 任何实现了此接口的 error,httpx 都会使用其返回的字符串作为响应体中的 code 字段。

type CORSOptions

type CORSOptions struct {
	AllowedOrigins   []string
	AllowedMethods   []string
	AllowedHeaders   []string
	ExposedHeaders   []string
	AllowCredentials bool
	MaxAge           int
}

CORSOptions 定义 CORS 配置

type ErrorCoder

type ErrorCoder interface {
	HTTPStatus() int
}

ErrorCoder 定义了如何提取 HTTP 状态码。 任何实现了此接口的 error,httpx 都会使用其返回的状态码,而不是默认的 500。

type FileResponse

type FileResponse struct {
	Content io.Reader
	Name    string
	Size    int64
	Type    string
}

FileResponse 是一个实现了 Streamable 的文件响应辅助类。

func (*FileResponse) Headers

func (f *FileResponse) Headers() map[string]string

func (*FileResponse) WriteTo

func (f *FileResponse) WriteTo(w io.Writer) (int64, error)

type HandlerFunc

type HandlerFunc[Req any, Res any] func(ctx context.Context, req *Req) (Res, error)

type HttpError

type HttpError struct {
	HttpCode int
	BizCode  string
	Msg      string
}

HttpError 是一个通用的错误实现,同时满足 error, ErrorCoder 和 BizCoder 接口。

func NewError added in v0.2.0

func NewError(httpCode int, bizCode string, msg string) *HttpError

NewError 创建一个新的 HttpError。 httpCode: HTTP 状态码 (如 404) bizCode: 业务错误码 (如 "USER_NOT_FOUND") msg: 错误描述

func (*HttpError) BizStatus added in v0.2.0

func (e *HttpError) BizStatus() string

func (*HttpError) Error

func (e *HttpError) Error() string

func (*HttpError) HTTPStatus

func (e *HttpError) HTTPStatus() int

type Limiter

type Limiter interface {
	Allow(r *http.Request) bool
}

Limiter 接口定义了限流器的行为。 Allow 应该非阻塞地返回是否允许请求。

type LogFunc added in v0.2.0

type LogFunc func(r *http.Request, metrics httpsnoop.Metrics)

LogFunc 定义日志回调签名

type Middleware

type Middleware func(http.Handler) http.Handler

Middleware 标准中间件定义

func AuthBasic

func AuthBasic(validator BasicValidator, realm string) Middleware

AuthBasic Basic Auth 认证中间件。

func AuthBearer

func AuthBearer(validator AuthValidator, realm string) Middleware

AuthBearer Bearer Token 认证中间件。 realm: 认证域名称,例如 "MyAPI"。如果为空,默认为 "Restricted"。

func CORS

func CORS(opts CORSOptions) Middleware

CORS 跨域资源共享中间件。

func DefaultCORS

func DefaultCORS() Middleware

DefaultCORS 返回一个宽容的 CORS 中间件(开发环境常用)。

func Logger

func Logger(logFunc LogFunc) Middleware

Logger 使用 httpsnoop 包装 ResponseWriter

func RateLimit

func RateLimit(limiter Limiter, errhooks ...func(ctx context.Context, err error)) Middleware

RateLimit 返回一个限流中间件。

func Recovery

func Recovery(panicHook func(ctx context.Context, err interface{})) Middleware

Recovery 捕获 Panic 防止服务崩溃

type Option

type Option func(*config)

func AddBinders added in v0.2.0

func AddBinders(b ...Binder) Option

AddBinders 在默认 Binder 链之前添加自定义 Binder

func NoEnvelope

func NoEnvelope() Option

NoEnvelope 指示 Handler 不要使用标准的 {code, msg, data} 封口

func WithBinders added in v0.2.0

func WithBinders(b ...Binder) Option

WithBinders 设置自定义的 Binder 链(将覆盖默认链)

func WithErrorHook added in v0.2.0

func WithErrorHook(hook func(ctx context.Context, err error)) Option

WithErrorHook 设置该 Handler 专属的错误处理 Hook

func WithValidator added in v0.2.0

func WithValidator(v *validator.Validate) Option

WithValidator 设置自定义的 Validator 实例

type Response

type Response[T any] struct {
	// Code 是业务错误码 (字符串),例如 "OK", "INVALID_PARAM", "USER_BANNED"。
	// 它与 HTTP Status Code 分离,由前端用于展示具体的错误文案。
	Code    string `json:"code"`
	Message string `json:"message"`
	Data    T      `json:"data,omitempty"`
	TraceID string `json:"trace_id,omitempty"`
}

Response 是默认的统一响应信封。

type SelfValidatable

type SelfValidatable interface {
	Validate(ctx context.Context) error
}

SelfValidatable 是高性能验证接口。 如果 Request 结构体实现了此接口,将跳过反射验证。

type Streamable

type Streamable interface {
	Headers() map[string]string
	WriteTo(w io.Writer) (int64, error)
}

Streamable 接口用于指示该结构体是流式响应(文件下载、SSE)。

Jump to

Keyboard shortcuts

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