Copilot commented on code in PR #13380:
URL: https://github.com/apache/apisix/pull/13380#discussion_r3270689779


##########
apisix/plugins/error-page.lua:
##########
@@ -0,0 +1,106 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core        = require("apisix.core")
+local plugin      = require("apisix.plugin")
+local plugin_name = "error-page"
+local ngx         = ngx
+
+
+local metadata_schema = {
+    type = "object",
+    patternProperties = {
+        ["^error_[1-5][0-9][0-9]$"] = {
+            type = "object",
+            properties = {
+                body = {type = "string", minLength = 1},
+                content_type = {type = "string", default = "text/html"},
+            },
+        },
+    },
+}
+
+local schema = {}
+
+local _M = {
+    version         = 0.1,
+    priority        = 450,
+    name            = plugin_name,
+    schema          = schema,
+    metadata_schema = metadata_schema,
+}
+
+
+function _M.check_schema(conf, schema_type)
+    if schema_type == core.schema.TYPE_METADATA then
+        return core.schema.check(metadata_schema, conf)
+    end
+    return core.schema.check(schema, conf)
+end
+
+
+-- return metadata only if the response should be modified
+local function get_metadata(ctx)
+    local status = ngx.status
+    if ctx.var.upstream_status then
+        return nil
+    end
+
+    if status < 400 then
+        return nil
+    end
+
+    local metadata = plugin.plugin_metadata(plugin_name)
+    if not metadata then
+        core.log.info("failed to read metadata for ", plugin_name)
+        return nil
+    end
+    core.log.info(plugin_name, " metadata: ", core.json.delay_encode(metadata))
+    metadata = metadata.value
+
+    local err_page = metadata["error_" .. status]
+    if not err_page or not (err_page.body and #err_page.body > 0) then
+        core.log.info("error page for error_", status, " not defined, default 
will be used.")

Review Comment:
   Logging full plugin metadata at `info` on every error response 
(`core.log.info(... delay_encode(metadata))`) is likely to be noisy and can 
dump large/custom bodies into logs. Consider switching these logs to `debug` 
(or removing the metadata dump entirely) and avoid per-request info logs when 
metadata is missing / no page is configured.
   



##########
apisix/plugins/error-page.lua:
##########
@@ -0,0 +1,106 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core        = require("apisix.core")
+local plugin      = require("apisix.plugin")
+local plugin_name = "error-page"
+local ngx         = ngx
+
+
+local metadata_schema = {
+    type = "object",
+    patternProperties = {
+        ["^error_[1-5][0-9][0-9]$"] = {
+            type = "object",
+            properties = {
+                body = {type = "string", minLength = 1},
+                content_type = {type = "string", default = "text/html"},
+            },
+        },
+    },
+}
+
+local schema = {}
+
+local _M = {
+    version         = 0.1,
+    priority        = 450,
+    name            = plugin_name,
+    schema          = schema,
+    metadata_schema = metadata_schema,
+}
+
+
+function _M.check_schema(conf, schema_type)
+    if schema_type == core.schema.TYPE_METADATA then
+        return core.schema.check(metadata_schema, conf)
+    end
+    return core.schema.check(schema, conf)
+end
+
+
+-- return metadata only if the response should be modified
+local function get_metadata(ctx)
+    local status = ngx.status
+    if ctx.var.upstream_status then

Review Comment:
   `ctx.var.upstream_status` is an NGINX var that can be an empty string (still 
truthy in Lua), so this early-return can accidentally disable the plugin even 
when the response was generated by APISIX/NGINX (no upstream response). 
Consider using `core.response.get_response_source(ctx)` and only skipping when 
the source is `"upstream"` (while allowing `"nginx"`/`"apisix"` errors), or at 
least treating empty/"-" upstream_status as not set.
   



##########
apisix/plugins/error-page.lua:
##########
@@ -0,0 +1,106 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core        = require("apisix.core")
+local plugin      = require("apisix.plugin")
+local plugin_name = "error-page"
+local ngx         = ngx
+
+
+local metadata_schema = {
+    type = "object",
+    patternProperties = {
+        ["^error_[1-5][0-9][0-9]$"] = {
+            type = "object",
+            properties = {
+                body = {type = "string", minLength = 1},
+                content_type = {type = "string", default = "text/html"},
+            },
+        },
+    },
+}
+

Review Comment:
   PR description/example configuration mentions a top-level `enable` flag in 
plugin metadata, but the metadata schema and implementation don’t accept/use 
it. Either add `enable` to `metadata_schema` + gate `get_metadata()` on it, or 
remove/update the documentation/description to avoid confusing users.



##########
apisix/plugins/error-page.lua:
##########
@@ -0,0 +1,106 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core        = require("apisix.core")
+local plugin      = require("apisix.plugin")
+local plugin_name = "error-page"
+local ngx         = ngx
+
+
+local metadata_schema = {
+    type = "object",
+    patternProperties = {
+        ["^error_[1-5][0-9][0-9]$"] = {
+            type = "object",
+            properties = {
+                body = {type = "string", minLength = 1},
+                content_type = {type = "string", default = "text/html"},
+            },
+        },
+    },
+}
+
+local schema = {}
+
+local _M = {
+    version         = 0.1,
+    priority        = 450,
+    name            = plugin_name,
+    schema          = schema,
+    metadata_schema = metadata_schema,
+}

Review Comment:
   `schema` is currently an empty table, which means per-route config may 
accept arbitrary keys without validation. Since this plugin doesn’t support any 
route/service attributes, consider defining `schema` as an object with no 
properties (and `additionalProperties=false`) to catch user 
typos/misconfigurations early.



##########
docs/zh/latest/plugins/error-page.md:
##########
@@ -0,0 +1,156 @@
+---
+title: error-page
+keywords:
+  - Apache APISIX
+  - API 网关
+  - Plugin
+  - Error page
+  - error-page
+description: error-page 插件允许自定义 APISIX 生成的 HTTP 
错误响应的响应体和内容类型,例如路由不匹配或上游不可达时返回的错误页面。
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## 描述
+
+`error-page` 插件允许自定义 APISIX 本身生成的 HTTP 
错误响应(例如,路由不匹配或上游不可达时)的响应体和内容类型。来自上游服务的响应不会受到影响。
+
+该插件通过[插件元数据](../terminology/plugin-metadata.md)进行全局配置,无需在路由上配置属性。启用后,它会拦截错误响应并将响应体替换为配置的自定义内容。
+

Review Comment:
   This new plugin doc is missing the canonical link `<head>...</head>` block 
that other plugin docs include near the top (e.g. 
`docs/zh/latest/plugins/response-rewrite.md:31-33`). Please add the canonical 
link for consistency and SEO.



##########
apisix/plugins/error-page.lua:
##########
@@ -0,0 +1,106 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core        = require("apisix.core")
+local plugin      = require("apisix.plugin")
+local plugin_name = "error-page"
+local ngx         = ngx
+
+
+local metadata_schema = {
+    type = "object",
+    patternProperties = {
+        ["^error_[1-5][0-9][0-9]$"] = {
+            type = "object",
+            properties = {
+                body = {type = "string", minLength = 1},
+                content_type = {type = "string", default = "text/html"},
+            },
+        },
+    },
+}
+
+local schema = {}
+
+local _M = {
+    version         = 0.1,
+    priority        = 450,
+    name            = plugin_name,
+    schema          = schema,
+    metadata_schema = metadata_schema,
+}
+
+
+function _M.check_schema(conf, schema_type)
+    if schema_type == core.schema.TYPE_METADATA then
+        return core.schema.check(metadata_schema, conf)
+    end
+    return core.schema.check(schema, conf)
+end
+
+
+-- return metadata only if the response should be modified
+local function get_metadata(ctx)
+    local status = ngx.status
+    if ctx.var.upstream_status then
+        return nil
+    end
+
+    if status < 400 then
+        return nil
+    end

Review Comment:
   Implementation only applies to `ngx.status >= 400` (line 62), but the 
metadata schema/docs claim support for status codes 100–599. Either restrict 
the schema/docs to 4xx/5xx (recommended for an error page plugin) or extend the 
runtime logic to handle <400 statuses consistently.



##########
apisix/plugins/error-page.lua:
##########
@@ -0,0 +1,106 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements.  See the NOTICE file distributed with
+-- this work for additional information regarding copyright ownership.
+-- The ASF licenses this file to You under the Apache License, Version 2.0
+-- (the "License"); you may not use this file except in compliance with
+-- the License.  You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+local core        = require("apisix.core")
+local plugin      = require("apisix.plugin")
+local plugin_name = "error-page"
+local ngx         = ngx
+
+
+local metadata_schema = {
+    type = "object",
+    patternProperties = {
+        ["^error_[1-5][0-9][0-9]$"] = {
+            type = "object",
+            properties = {
+                body = {type = "string", minLength = 1},
+                content_type = {type = "string", default = "text/html"},
+            },
+        },
+    },
+}
+
+local schema = {}
+
+local _M = {
+    version         = 0.1,
+    priority        = 450,
+    name            = plugin_name,
+    schema          = schema,
+    metadata_schema = metadata_schema,
+}
+
+
+function _M.check_schema(conf, schema_type)
+    if schema_type == core.schema.TYPE_METADATA then
+        return core.schema.check(metadata_schema, conf)
+    end
+    return core.schema.check(schema, conf)
+end
+
+
+-- return metadata only if the response should be modified
+local function get_metadata(ctx)
+    local status = ngx.status
+    if ctx.var.upstream_status then
+        return nil
+    end
+
+    if status < 400 then
+        return nil
+    end
+
+    local metadata = plugin.plugin_metadata(plugin_name)
+    if not metadata then
+        core.log.info("failed to read metadata for ", plugin_name)
+        return nil
+    end
+    core.log.info(plugin_name, " metadata: ", core.json.delay_encode(metadata))
+    metadata = metadata.value
+
+    local err_page = metadata["error_" .. status]
+    if not err_page or not (err_page.body and #err_page.body > 0) then
+        core.log.info("error page for error_", status, " not defined, default 
will be used.")
+        return nil
+    end
+
+    return metadata
+end
+
+
+function _M.header_filter(conf, ctx)
+    ctx.plugin_error_page_meta = get_metadata(ctx)
+    if not ctx.plugin_error_page_meta then
+        return
+    end
+    local status = ngx.status
+    local err_page = ctx.plugin_error_page_meta["error_" .. status]
+    core.response.set_header("content-type", err_page.content_type)
+    core.response.set_header("content-length", #err_page.body)
+end

Review Comment:
   `content_type` has a schema default, but `core.schema.check()` does not 
apply defaults to runtime data, so `err_page.content_type` may be nil when 
omitted. Consider defaulting to `text/html` in code and calling 
`core.response.clear_header_as_body_modified()` before overriding the body (to 
clear Content-Encoding/ETag/etc. similar to `response-rewrite`).



##########
docs/en/latest/plugins/error-page.md:
##########
@@ -0,0 +1,154 @@
+---
+title: error-page
+keywords:
+  - Apache APISIX
+  - API Gateway
+  - Plugin
+  - Error page
+description: The error-page Plugin customizes the HTTP error response body and 
content type for APISIX-generated error responses, such as when no route 
matches or when APISIX itself encounters an error.
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Description
+
+The `error-page` Plugin customizes the response body and content type for HTTP 
error responses generated by APISIX itself (for example, when no route matches 
or when the upstream is unreachable). Responses from upstream services are not 
affected.
+
+This Plugin uses [Plugin metadata](../terminology/plugin-metadata.md) for 
global configuration and requires no per-route attributes. When enabled, it 
intercepts error responses and replaces their body with the configured content.
+

Review Comment:
   This new plugin doc is missing the canonical link `<head>...</head>` block 
that other plugin docs include near the top (e.g. 
`docs/en/latest/plugins/response-rewrite.md:31-33`). Please add the canonical 
link for consistency and SEO.



##########
t/plugin/error-page.t:
##########
@@ -0,0 +1,333 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+no_shuffle();
+log_level('info');
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    my $user_yaml_config = <<_EOC_;
+plugins:
+  - error-page
+  - serverless-post-function
+_EOC_
+    $block->set_value("extra_yaml_config", $user_yaml_config);
+
+    if (!defined $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+
+    $block;
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: set global rule to enable plugin
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/global_rules/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "plugins": {
+                        "error-page": {}
+                    }
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 2: set route with serverless-post-function plugin to inject error 
status
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                 ngx.HTTP_PUT,
+                 [[{
+                    "plugins": {
+                        "serverless-post-function": {
+                            "functions" : ["return function() if 
ngx.var.http_x_test_status ~= nil 
then;ngx.exit(tonumber(ngx.var.http_x_test_status));end;end"]
+                        }
+                    },
+                    "upstream": {
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        },
+                        "type": "roundrobin"
+                    },
+                    "uri": "/*"
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 3: without plugin metadata, error response should not be modified
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 502
+--- error_code: 502
+--- response_headers
+content-type: text/html
+--- response_body_like
+.*openresty.*
+--- error_log
+failed to read metadata for error-page
+
+
+
+=== TEST 4: set plugin metadata with custom error pages
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/plugin_metadata/error-page',
+                ngx.HTTP_PUT,
+                [[{
+                    "error_500": {"body": "<html><body><h1>500 Internal Server 
Error</h1></body></html>"},
+                    "error_404": {"body": "<html><body><h1>404 Not 
Found</h1></body></html>"},
+                    "error_502": {"body": "<html><body><h1>502 Bad 
Gateway</h1></body></html>"},
+                    "error_503": {"body": "<html><body><h1>503 Service 
Unavailable</h1></body></html>"}
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 5: custom error page for 500
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 500
+--- error_code: 500
+--- response_headers
+content-type: text/html
+--- response_body_like eval
+qr/<body><h1>500 Internal Server Error<\/h1><\/body>/
+
+
+
+=== TEST 6: custom error page for 502
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 502
+--- error_code: 502
+--- response_headers
+content-type: text/html
+--- response_body_like eval
+qr/<body><h1>502 Bad Gateway<\/h1><\/body>/
+
+
+
+=== TEST 7: custom error page for 503
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 503
+--- error_code: 503
+--- response_headers
+content-type: text/html
+--- response_body_like eval
+qr/<body><h1>503 Service Unavailable<\/h1><\/body>/
+
+
+
+=== TEST 8: custom error page for 404
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 404
+--- error_code: 404
+--- response_headers
+content-type: text/html
+--- response_body_like eval
+qr/<body><h1>404 Not Found<\/h1><\/body>/
+
+
+
+=== TEST 9: error page not configured for status 405
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 405
+--- error_code: 405
+--- error_log
+error page for error_405 not defined
+
+
+
+=== TEST 10: set metadata with empty body for a status code
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/plugin_metadata/error-page',
+                ngx.HTTP_PUT,
+                [[{
+                    "error_405": {"content_type": "text/html"}
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 11: error page body not set falls back to default
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 405
+--- error_code: 405
+--- error_log
+error page for error_405 not defined
+
+
+
+=== TEST 12: delete plugin metadata
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/plugin_metadata/error-page',
+                ngx.HTTP_DELETE
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 13: after metadata deleted, error response not modified
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 500
+--- error_code: 500
+--- response_headers
+content-type: text/html
+--- response_body_like
+.*openresty.*
+--- error_log
+failed to read metadata for error-page
+
+
+
+=== TEST 14: set metadata with custom content-type
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/plugin_metadata/error-page',
+                ngx.HTTP_PUT,
+                [[{
+                    "error_500": {
+                        "body": "{\"code\": 500, \"message\": \"Internal 
Server Error\"}",
+                        "content_type": "application/json"
+                    }
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 15: custom content-type is returned in response
+--- request
+GET /hello
+--- more_headers
+X-Test-Status: 500
+--- error_code: 500
+--- response_headers
+content-type: application/json
+
+
+
+=== TEST 16: upstream errors are not intercepted
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/plugin_metadata/error-page',
+                ngx.HTTP_PUT,
+                [[{
+                    "error_500": {"body": "<html><body><h1>500 
custom</h1></body></html>"}
+                }]]
+                )
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed

Review Comment:
   TEST 16 claims to verify that upstream errors are not intercepted, but it 
currently only updates plugin metadata and never performs a request/assertion. 
Add a request that produces an upstream response/error and assert the body is 
unchanged (or only APISIX/NGINX-generated errors are modified).



##########
docs/en/latest/plugins/error-page.md:
##########
@@ -0,0 +1,154 @@
+---
+title: error-page
+keywords:
+  - Apache APISIX
+  - API Gateway
+  - Plugin
+  - Error page

Review Comment:
   Front matter `keywords` typically includes the plugin name itself (e.g. 
`grpc-web` includes `grpc-web`). Consider adding `error-page` to the keywords 
list for consistency/searchability.
   



##########
docs/zh/latest/plugins/error-page.md:
##########
@@ -0,0 +1,156 @@
+---
+title: error-page
+keywords:
+  - Apache APISIX
+  - API 网关
+  - Plugin
+  - Error page
+  - error-page
+description: error-page 插件允许自定义 APISIX 生成的 HTTP 
错误响应的响应体和内容类型,例如路由不匹配或上游不可达时返回的错误页面。
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## 描述
+
+`error-page` 插件允许自定义 APISIX 本身生成的 HTTP 
错误响应(例如,路由不匹配或上游不可达时)的响应体和内容类型。来自上游服务的响应不会受到影响。
+
+该插件通过[插件元数据](../terminology/plugin-metadata.md)进行全局配置,无需在路由上配置属性。启用后,它会拦截错误响应并将响应体替换为配置的自定义内容。
+
+## 插件元数据
+
+该插件不支持在路由、服务或其他资源上配置属性,所有配置均通过插件元数据完成。
+
+| 名称                               | 类型    | 必选项 | 默认值     | 描述                
                                                                                
           |
+| ---------------------------------- | ------- | ------ | ---------- | 
--------------------------------------------------------------------------------------------------------------
 |
+| error_`{status_code}`              | object  | 否     |            | 指定 HTTP 
状态码的自定义错误页面配置,例如 `error_404` 对应 404 响应。支持 100–599 范围内的任意 HTTP 状态码。  |

Review Comment:
   文档提到支持 100–599 范围内的任意 HTTP 状态码,但插件实现目前仅在 `ngx.status >= 400` 
时才会替换响应体。请将文档描述与实际行为保持一致(或扩展实现以支持文档所述范围)。
   



##########
docs/en/latest/plugins/error-page.md:
##########
@@ -0,0 +1,154 @@
+---
+title: error-page
+keywords:
+  - Apache APISIX
+  - API Gateway
+  - Plugin
+  - Error page
+description: The error-page Plugin customizes the HTTP error response body and 
content type for APISIX-generated error responses, such as when no route 
matches or when APISIX itself encounters an error.
+---
+
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+## Description
+
+The `error-page` Plugin customizes the response body and content type for HTTP 
error responses generated by APISIX itself (for example, when no route matches 
or when the upstream is unreachable). Responses from upstream services are not 
affected.
+
+This Plugin uses [Plugin metadata](../terminology/plugin-metadata.md) for 
global configuration and requires no per-route attributes. When enabled, it 
intercepts error responses and replaces their body with the configured content.
+
+## Plugin Metadata
+
+There are no attributes to configure this Plugin on Routes, Services, or other 
resources. All configuration is done through Plugin metadata.
+
+| Name                        | Type    | Required | Default    | Description  
                                                                                
                    |
+| --------------------------- | ------- | -------- | ---------- | 
----------------------------------------------------------------------------------------------------------------
 |
+| error_`{status_code}`       | object  | False    |            | Custom error 
page configuration for the given HTTP status code. For example, `error_404` for 
404 responses. Any HTTP status code in the range 100–599 is supported. |
+| error_`{status_code}`.body  | string  | False    |            | Response 
body to return for the given status code. If empty or not set, the default 
APISIX/nginx error page is used. |
+| error_`{status_code}`.content_type | string | False | text/html | Content 
type of the response body.                                                      
                        |

Review Comment:
   Docs state that any HTTP status code in the range 100–599 is supported, but 
the plugin implementation currently only modifies responses when `ngx.status >= 
400`. Please align the documentation with the actual behavior (or extend the 
implementation to match the documented range).
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to