- ErrorPageManager 预加载错误页面到内存,运行时无文件 I/O - ErrorIntercept 中间件拦截 4xx/5xx 响应并应用自定义页面 - 支持特定状态码页面和默认页面配置 - 支持可选的响应状态码覆盖(如返回 200 OK) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
118 lines
3.1 KiB
Go
118 lines
3.1 KiB
Go
// 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
|
||
}
|