xfy 827d26c2b5 feat(handler,middleware,errorintercept): 新增自定义错误页面支持
- ErrorPageManager 预加载错误页面到内存,运行时无文件 I/O
- ErrorIntercept 中间件拦截 4xx/5xx 响应并应用自定义页面
- 支持特定状态码页面和默认页面配置
- 支持可选的响应状态码覆盖(如返回 200 OK)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 17:49:59 +08:00

118 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package errorintercept 提供 HTTP 错误拦截中间件,用于应用自定义错误页面。
//
// 该文件包含错误拦截相关的核心功能,包括:
// - ErrorIntercept 中间件:拦截 HTTP 错误响应并应用自定义错误页面
// - 错误状态码检测
// - 错误页面内容替换
//
// 主要用途:
//
// 在 HTTP 响应返回错误状态码时,自动替换为预加载的自定义错误页面内容。
//
// 注意事项:
// - 错误页面在启动时预加载,运行时不进行文件 I/O
// - 支持可选的响应状态码覆盖
// - 只拦截 4xx 和 5xx 错误状态码
//
// 作者xfy
package errorintercept
import (
"github.com/valyala/fasthttp"
"rua.plus/lolly/internal/handler"
)
// ErrorIntercept 错误拦截中间件。
//
// 拦截 HTTP 错误响应4xx 和 5xx并使用预加载的自定义错误页面内容替换响应。
type ErrorIntercept struct {
// manager 错误页面管理器
manager *handler.ErrorPageManager
}
// New 创建错误拦截中间件。
//
// 参数:
// - manager: 错误页面管理器(已预加载错误页面)
//
// 返回值:
// - *ErrorIntercept: 创建的中间件实例
//
// 使用示例:
//
// interceptor := errorintercept.New(errorPageManager)
func New(manager *handler.ErrorPageManager) *ErrorIntercept {
return &ErrorIntercept{
manager: manager,
}
}
// Name 返回中间件名称。
//
// 返回值:
// - string: 中间件名称
func (ei *ErrorIntercept) Name() string {
return "ErrorIntercept"
}
// Process 实现中间件接口。
//
// 拦截错误状态码响应并应用自定义错误页面。
//
// 参数:
// - next: 下一个请求处理器
//
// 返回值:
// - fasthttp.RequestHandler: 包装后的请求处理器
func (ei *ErrorIntercept) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler {
// 如果没有配置错误页面,直接返回下一个处理器
if ei.manager == nil || !ei.manager.IsConfigured() {
return next
}
return func(ctx *fasthttp.RequestCtx) {
// 先执行下一个处理器
next(ctx)
// 检查是否是错误状态码4xx 或 5xx
statusCode := ctx.Response.StatusCode()
if !isErrorStatusCode(statusCode) {
return
}
// 查找对应的错误页面
content, found, responseCode := ei.manager.GetPage(statusCode)
if !found {
return
}
// 替换响应内容为自定义错误页面
ctx.Response.SetBody(content)
ctx.Response.Header.SetContentType("text/html; charset=utf-8")
// 如果配置了响应状态码覆盖,使用覆盖值
if responseCode != statusCode {
ctx.Response.SetStatusCode(responseCode)
}
}
}
// isErrorStatusCode 检查状态码是否为错误状态码。
//
// 参数:
// - code: HTTP 状态码
//
// 返回值:
// - bool: 是否为错误状态码4xx 或 5xx
func isErrorStatusCode(code int) bool {
return code >= 400 && code < 600
}
// GetManager 返回错误页面管理器。
//
// 返回值:
// - *handler.ErrorPageManager: 错误页面管理器
func (ei *ErrorIntercept) GetManager() *handler.ErrorPageManager {
return ei.manager
}