ErrorPage - 508364/NetLeaf GitHub Wiki

ErrorPage 模块

概述

ErrorPage 模块是从 NetLeaf 主库分离出来的独立库,用于支持自定义错误页面模板,强制预留变量区域,并可与 Auto-route 模块联动显示路由建议。

版本: 与主库共用版本号

平台支持:

  • ✅ Windows
  • ✅ Linux
  • ✅ macOS

重要声明

⚠️ 该模块是独立的

  • 其他模块不能使用此模块的功能
  • 如果该模块未编译/未加载,错误页面功能将不可用,但不会影响主库运行
  • 必须预留变量区域,否则模板无效

功能特性

1. 模板系统

支持自定义HTML错误页面模板,提供灵活的错误页面定制能力。

2. 强制变量预留

自定义模板必须使用项目统一的变量格式 {{<var>变量名</var>}},包含以下变量:

变量 说明 是否必需
{{<var>ERROR_CODE</var>}} HTTP状态码(如404) ✅ 必需
{{<var>ERROR_MESSAGE</var>}} 状态码描述(如"Not Found") ✅ 必需
{{<var>REQUESTED_PATH</var>}} 请求的路径 ✅ 必需
{{<var>SERVER_VERSION</var>}} 服务器版本号 ✅ 必需
{{<var>TIMESTAMP</var>}} 错误发生时间戳 ✅ 必需
{{<var>SUGGESTION</var>}} 路由建议(可选) 可选

注意: 此变量格式与项目中 nl_web_add_html_with_vars() 等函数使用的格式保持一致。

3. 灵活启用方式

支持多种启用/禁用方式:

  • 整数: 1 (启用), 0 (禁用)
  • 字符串: "true", "false", "on", "off", "yes", "no" (大小写不敏感)

5. 与Auto-route联动

可以与 Auto-route 模块联动,在404错误页面中显示路由建议。

API 参考

初始化与配置

函数 说明
nl_errorpage_init() 初始化模块
nl_errorpage_is_available() 检查模块是否可用
nl_errorpage_version() 获取模块版本

启用/禁用

函数 说明
nl_errorpage_enable(int) 启用/禁用模块(整数)
nl_errorpage_enable_ex(const char*) 启用/禁用模块(字符串)
nl_errorpage_is_enabled() 检查模块是否启用

模板加载与验证

函数 说明
nl_errorpage_load_template() 从文件加载模板
nl_errorpage_set_template() 设置模板内容
nl_errorpage_get_template() 获取指定状态码的模板
nl_errorpage_validate_template() 验证模板是否包含所有必需变量

错误页面渲染

函数 说明
nl_errorpage_render() 使用自定义模板渲染错误页面
nl_errorpage_render_default() 使用内置默认模板渲染

HTTP响应生成

函数 说明
nl_errorpage_make_response() 生成完整的HTTP错误响应
nl_errorpage_make_response_custom() 使用自定义模板生成响应
nl_errorpage_quick_response() 快速生成简单错误响应
nl_errorpage_404_with_suggestion() 生成带路由建议的404响应

辅助函数

函数 说明
nl_errorpage_status_message() 获取状态码对应的默认消息
nl_errorpage_is_cacheable() 检查状态码是否可缓存

使用示例

基础使用

#include "netleaf_errorpage.h"

int main() {
    // 初始化模块
    nl_errorpage_init();
    
    // 启用模块
    nl_errorpage_enable_ex("true");
    
    // 生成简单错误响应
    char* response = nl_errorpage_quick_response(404, "Not Found", "/badpath");
    
    // 使用完毕后释放内存
    free(response);
    return 0;
}

使用自定义模板

#include "netleaf_errorpage.h"

int main() {
    nl_errorpage_init();
    nl_errorpage_enable_ex("true");
    
    // 设置自定义模板(必须包含所有必需变量,使用统一格式 {{<var>VAR</var>}})
    const char* template = 
        "<!DOCTYPE html>\n"
        "<html><head><title>{{<var>ERROR_CODE</var>}} {{<var>ERROR_MESSAGE</var>}}</title></head>\n"
        "<body>\n"
        "<h1>{{<var>ERROR_CODE</var>}} - {{<var>ERROR_MESSAGE</var>}}</h1>\n"
        "<p>您访问的路径: <code>{{<var>REQUESTED_PATH</var>}}</code> 不存在</p>\n"
        "<p>{{<var>SUGGESTION</var>}}</p>\n"
        "<footer>Server: {{<var>SERVER_VERSION</var>}} | Time: {{<var>TIMESTAMP</var>}}</footer>\n"
        "</body></html>";
    
    nl_errorpage_set_template(404, template);
    
    // 生成错误响应
    nl_errorpage_vars_t vars = {
        .status_code = 404,
        .error_message = "Not Found",
        .requested_path = "/api/user",
        .suggestion = "/api/users",  // 来自Auto-route模块
        .server_version = "NetLeaf v2.4.0"
    };
    
    char* response = nl_errorpage_make_response(404, &vars);
    
    free(response);
    return 0;
}

与Auto-route联动

#include "netleaf_errorpage.h"
#include "netleaf_autoroute.h"

void handle_404(const char* requested_path) {
    // 获取路由建议(来自Auto-route模块)
    char* suggestion = NULL;
    if (nl_autoroute_is_available() && nl_autoroute_is_enabled()) {
        nl_route_matcher_t* matcher = nl_autoroute_get_global_matcher();
        suggestion = nl_route_matcher_find_similar(matcher, requested_path, 0.4);
    }
    
    // 生成带建议的404响应
    char* response = nl_errorpage_404_with_suggestion(requested_path, suggestion);
    
    // 发送响应...
    
    free(suggestion);
    free(response);
}

内置默认模板

404模板

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{<var>ERROR_CODE</var>}} {{<var>ERROR_MESSAGE</var>}}</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 40px; background: #f5f5f5; }
        .error-container { max-width: 600px; margin: 0 auto; background: white; border-radius: 8px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { color: #e74c3c; margin: 0 0 20px 0; font-size: 72px; }
        h2 { color: #333; margin: 0 0 20px 0; font-size: 24px; }
        p { color: #666; margin: 10px 0; }
        .path { background: #f8f9fa; padding: 10px; border-radius: 4px; font-family: monospace; }
        .suggestion { background: #e8f5e9; padding: 15px; border-radius: 4px; margin-top: 20px; }
        .suggestion a { color: #2e7d32; font-weight: bold; }
        .footer { margin-top: 30px; color: #999; font-size: 12px; }
    </style>
</head>
<body>
    <div class="error-container">
        <h1>{{<var>ERROR_CODE</var>}}</h1>
        <h2>{{<var>ERROR_MESSAGE</var>}}</h2>
        <p>The requested path could not be found:</p>
        <p class="path">{{<var>REQUESTED_PATH</var>}}</p>
        {{<var>SUGGESTION</var>}}
        <div class="footer">
            Server: {{<var>SERVER_VERSION</var>}} | Time: {{<var>TIMESTAMP</var>}}
        </div>
    </div>
</body>
</html>

500模板

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{<var>ERROR_CODE</var>}} {{<var>ERROR_MESSAGE</var>}}</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 40px; background: #f5f5f5; }
        .error-container { max-width: 600px; margin: 0 auto; background: white; border-radius: 8px; padding: 40px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { color: #e74c3c; margin: 0 0 20px 0; font-size: 72px; }
        h2 { color: #333; margin: 0 0 20px 0; font-size: 24px; }
        p { color: #666; margin: 10px 0; }
        .footer { margin-top: 30px; color: #999; font-size: 12px; }
    </style>
</head>
<body>
    <div class="error-container">
        <h1>{{<var>ERROR_CODE</var>}}</h1>
        <h2>{{<var>ERROR_MESSAGE</var>}}</h2>
        <p>An internal server error occurred. Please try again later.</p>
        <p>Path: <code>{{<var>REQUESTED_PATH</var>}}</code></p>
        <div class="footer">
            Server: {{<var>SERVER_VERSION</var>}} | Time: {{<var>TIMESTAMP</var>}}
        </div>
    </div>
</body>
</html>

模板验证

模块提供模板验证功能,确保自定义模板包含所有必需变量:

const char* template = "...";
if (nl_errorpage_validate_template(template)) {
    printf("模板验证通过\n");
    nl_errorpage_set_template(404, template);
} else {
    printf("模板缺少必需变量\n");
}

支持的状态码

模块支持以下常见HTTP状态码的自定义模板:

状态码 说明
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
405 Method Not Allowed
408 Request Timeout
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout

编译选项

BUILD_ERRORPAGE=ON   # 默认启用

注意事项

  1. 模块独立性: ErrorPage模块是独立的,其他模块不能使用其功能
  2. 变量强制性: 自定义模板必须包含所有必需变量,否则验证失败
  3. 联动限制: 只能与Auto-route模块联动显示路由建议
  4. 错误处理: 如果模块未加载,错误页面功能不可用,但不会导致崩溃
  5. 内存管理: 生成的响应字符串需要调用者手动释放

与Auto-route的联动流程

用户请求不存在的路径
    ↓
服务器返回404
    ↓
Auto-route模块查找相似路由
    ↓
生成路由建议(如"/api/users")
    ↓
ErrorPage模块渲染错误页面
    ↓
在错误页面中显示"您可能在找: /api/users"
⚠️ **GitHub.com Fallback** ⚠️