feat(surf): branded HTML error pages for browser navigations
A browser navigation (Sec-Fetch-Mode navigate, or GET/HEAD without it; no X-Requested-With; Accept naming text/html above every JSON type; not /api, /_<name> or the admin prefix) that hits a framework-generated 404, 405 or 500 gets a self-contained SummerCMS page: unmatched routes and wrong methods at the ServeMux, failed Where/WhereIn constraints, overlap-family misses, the unpublished uploads route and recovered panics. Every other request keeps the exact stdlib text, wire's opaque JSON 500 or the bare raw-group 500. Details (method, path, X-Request-Id) only with app.debug or outside production. Applications replace the page with a published surf.ErrorPageRenderer; http.error_pages.enabled false turns it off.
This commit is contained in:
@@ -38,14 +38,16 @@ surf wraps every non-raw route in the same layers. From the outside in:
|
||||
1. Optional locale-prefix rewrite, when a `surf.LocaleResolver` is published: an enabled locale as the first path segment is stripped so later layers and routing see the unprefixed path.
|
||||
2. CORS, only for the paths configured under `http.cors.paths`, including preflight requests.
|
||||
3. The route's CORS profile, when it or its group names `cors:<profile>`. It sits here whatever its position in the middleware list, so refusals and recovered panics carry its headers too.
|
||||
4. Panic recovery. A panic becomes an opaque JSON 500 from [wire](../../modules/wire/README.md), and because the response is buffered until the handler returns, the client never receives half a body.
|
||||
4. Panic recovery. A panic becomes an opaque JSON 500 from [wire](../../modules/wire/README.md), or the HTML error page with status 500 when the request is a browser navigation (see [Error pages](../services/routing.md#error-pages)). Because the response is buffered until the handler returns, the client never receives half a body.
|
||||
5. The request locale, taken from the published `surf.LocaleResolver` when one exists, or from the `Accept-Language` header otherwise, and stored in the context.
|
||||
6. The body limit: `http.body_limits.default_bytes`, or the route's own `body.limit:<bytes>`.
|
||||
7. The route's middleware, in the order you listed them: group middleware first, then the route's own (`cors:<profile>` excepted, see step 3).
|
||||
8. The path constraints. A request whose parameter fails `pact.Router.Where` or `pact.Router.WhereIn` gets a 404 before your handler runs.
|
||||
9. Your handler.
|
||||
|
||||
Raw groups, declared with `pact.Router.GroupRaw`, are for webhooks and file streams. They skip the default body limit, refuse house middleware, and a panic in them returns a bare 500.
|
||||
Raw groups, declared with `pact.Router.GroupRaw`, are for webhooks and file streams. They skip the default body limit, refuse house middleware, and a panic in them returns a bare 500, or the HTML error page for a browser navigation.
|
||||
|
||||
A request that no route matches, or that uses a method its path does not accept, is answered by the ServeMux and negotiated the same way: a browser navigation gets the HTML error page with status 404 or 405, every other caller the stdlib text.
|
||||
|
||||
## Request context values
|
||||
|
||||
|
||||
Reference in New Issue
Block a user