Skip to content

feat(web): pipeline exception boundary, status-code pages, 404 terminal via OnError hook [L03.01.01.19] - #920

Merged
dotnetcadet merged 3 commits into
mainfrom
feature/L03.01.01.19-exception-boundary
Jul 20, 2026
Merged

feat(web): pipeline exception boundary, status-code pages, 404 terminal via OnError hook [L03.01.01.19]#920
dotnetcadet merged 3 commits into
mainfrom
feature/L03.01.01.19-exception-boundary

Conversation

@dotnetcadet

Copy link
Copy Markdown
Contributor

Summary

Stack 1/8 of the Web/Http Batch 4b stacked series (base: main).

Rebuilds the pipeline fault story as a consumer of the shipped #864 seams — middleware-first, no IResult (the issue body predates the 2026-07-10 re-scope; the plan's §4 row is the authority):

  • UseErrorHandling() (Web.ErrorHandling): exception-boundary middleware — catches faults escaping downstream middleware, publishes IHttpExceptionFeature, resets the unstarted response (no-clobber guard via IHttpResponseStreamingFeature.HasStarted; started responses abort the exchange via CancelAsync instead), then dispatches the shipped OnError chain (registration order, first-true owns) with a ProblemDetails-500 terminal. Handler faults propagate to the server's last-resort isolation — never masked.
  • UseStatusCodePages() (opt-in): upgrades bodyless 4xx/5xx terminal responses to RFC 9457 problem+json (custom Responder supported); never touches started responses or existing bodies.
  • 404 terminal: WebApplication.Build()'s silent Task.CompletedTask terminal now sets a bodyless 404 on unshaped fall-through responses. Payload-free by design — COHRES002 forbids Web.Hosting referencing Web.ProblemDetails; status-code pages renders the body.
  • Developer-detail toggle (IncludeDeveloperDetails, off by default) enriches only the fallback payload; SuppressDiagnosticsCallback + OnException observer give .NET-10-parity diagnostics suppression.

Review follow-ups included: repaired the pre-existing 6688b71a breaks (Web.Authentication missing using; Web.Routing test double missing ContentRootPath) and the IErrorHandlingFeature naming drift.

Tests

Web.ErrorHandling 37 · Web.Hosting 49 · Web.Routing 250 — all passing locally; full Web regression sweep green (Testing, Query, Diagnostics, ForwardedHeaders, HostFiltering, RequestTimeouts, Authentication, ProblemDetails, Serialization, StaticFiles, Forms, Health).

Notes

  • Three justified catch (Exception) sites carry deviation markers (boundary fault-catch + two best-effort callback guards).
  • docs/Assembly/ pages deferred with the package's existing doc maturity.

Closes #881

🤖 Generated with Claude Code

dotnetcadet and others added 3 commits July 19, 2026 16:05
Commit 6688b71 renamed HttpAuthenticationFeature -> AuthenticationFeature but
left HttpContextAuthenticationExtensions (namespace Assimalign.Cohesion.Web.Authentication)
without a using for the child .Internal namespace where the concrete type lives,
breaking the Web.Authentication build and every project that references it —
including the Web.Hosting test project this task needs to verify. Add the missing using.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…al via OnError hook [L03.01.01.19]

Implements #881 as a CONSUMER of the shipped #864 OnError chain (PR #893),
re-scoped from the issue's IResult design: IResult was withdrawn pre-merge, so the
Web area is middleware-first and there is no result-carrier channel. No IResult, no
Results.Problem/ExecuteResultAsync, no second handler abstraction.

Web.ErrorHandling (new pipeline verbs, homed with AddErrorHandling):
- UseErrorHandling() installs the exception-boundary middleware: catches faults
  escaping downstream, publishes IHttpExceptionFeature (caught exception + path),
  and on an unstarted response resets it and dispatches through the shipped
  IErrorHandlingFeature.Handlers chain (registration order, first-true wins) to the
  ProblemDetails-500 terminal. No-clobber: when IHttpResponseStreamingFeature
  .HasStarted reports the head is committed, it aborts the one exchange via
  IHttpContext.CancelAsync instead of half-writing (connection survives).
- Developer-detail toggle (off by default) enriches only the boundary's terminal
  fallback. OnException observation hook + SuppressDiagnosticsCallback are the
  Cohesion parity for .NET 10 SuppressDiagnosticsCallback (no Microsoft.Extensions
  logging). Handler faults propagate, never masked (shipped OnError semantics);
  an observer fault is swallowed (observation must not defeat rendering).
- UseStatusCodePages() upgrades a bodyless 4xx/5xx terminal response into
  problem+json (or a custom responder).

Web.Hosting:
- The silent Task.CompletedTask pipeline terminal now sets a bodyless 404 for an
  unhandled request (still 200, no body/content-type/location). It stays payload-
  free because COHRES002 forbids the runtime module referencing Web.ProblemDetails;
  UseStatusCodePages upgrades it. A deliberate empty 200 must be terminal (not chain
  to next) — the TLS integration test's terminal handler is adapted to match.

Tests: 22 new Web.ErrorHandling unit tests + 3 end-to-end (UseErrorHandling default,
handler-owns, UseStatusCodePages 404 upgrade) + 3 Web.Hosting terminal tests. Docs:
Web.ErrorHandling DESIGN/OVERVIEW, Web.Hosting DESIGN, Web README updated; fixed the
IHttpErrorHandler/IHttpErrorHandlingFeature naming drift in OVERVIEW.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ature naming drift

Review follow-ups on the #881 branch: implement IWebApplicationContext.ContentRootPath
on the Web.Routing TestWebApplicationContext (stale since 6688b71) and correct the
IHttpErrorHandlingFeature -> IErrorHandlingFeature name drift in the ErrorHandling
csproj description, xmldoc, and DESIGN.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dotnetcadet
dotnetcadet merged commit fcc8142 into main Jul 20, 2026
69 checks passed
@dotnetcadet
dotnetcadet deleted the feature/L03.01.01.19-exception-boundary branch July 20, 2026 11:07
dotnetcadet added a commit that referenced this pull request Jul 20, 2026
…ack reconciliation)

PRs #920-#925 squash-merged to main leave this stacked branch's copy of those
commits content-identical but history-divergent; the only textual conflicts were
Web.Caching's wiring lines sitting alphabetically adjacent to Web.Compression's
in App.props, the CI matrix, both slnx files, and the README project map -
resolved to the union.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[L03.01.01.19] Pipeline exception boundary, status-code pages, and 404 terminal over IResult (supersedes #776)

1 participant