Skip to content

Architecture and Request Lifecycle

Mike Christensen edited this page Aug 28, 2026 · 1 revision

Architecture and request lifecycle

Imp is one terminal ASP.NET Core middleware component with four main responsibilities:

  1. Resolve a request path to a page type.
  2. Construct that page with ASP.NET Core dependency injection.
  3. bind request state and run lifecycle hooks.
  4. Render a cached embedded template or call the page directly.

Components

Component Responsibility
UseImp Creates ImpConfiguration and registers ImpMiddleware.
ImpMiddleware Coordinates routing, authentication, lifecycle, postbacks, compilation, and rendering.
Request Converts a path into a page type and binds query properties.
BasePage Base class containing Request, PreRender, and direct Render.
ResourceTemplateManager Loads embedded page/layout resources from the configured assembly.
PageCompiler Converts XML template commands into cached static/dynamic chunks.

Request sequence

For every request reaching Imp:

  1. Imp sets the response content type to text/html and initially sets status 200.
  2. Request derives a page type name from the path.
  3. If the type is missing, OnNotFound may return a custom page type; otherwise the configured or built-in 404 page is used.
  4. The page is constructed with ActivatorUtilities using HttpContext.RequestServices.
  5. BasePage.Request is assigned and supported query values are bound to matching properties.
  6. If the page has [SecurePage] and an Authenticate callback is configured, the callback runs. Returning false stops the request.
  7. The global OnPreRender callback runs.
  8. The page's PreRender method runs.
  9. For POST, Imp invokes IAsyncPostable.PostbackAsync or IPostable.Postback.
  10. A [PageTemplate] resource is compiled once per page type and rendered; without the attribute, BasePage.Render runs.

The order matters. A postback occurs after both pre-render callbacks, and the resulting page is rendered afterward. Design validation messages and response status changes accordingly.

Template cache

Compiled templates are held in a process-wide concurrent dictionary keyed by page type. Static XML is parsed and reflected once, while dynamic methods execute for every render. Changes to embedded templates require rebuilding and restarting the application.

The configured fallback not-found page is also cached by the current implementation. Keep that page stateless: do not inject scoped services into it or depend on request-specific constructor state. Data-driven fallback pages returned by OnNotFound are constructed normally for each request.

Middleware placement

Imp does not call the next middleware delegate. A typical pipeline is:

app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseImp(/* configuration */);

Do not place static files or authentication after UseImp; they will not see requests handled by it. If the application mixes endpoint routing and Imp, branch the pipeline or map endpoints before the terminal Imp branch deliberately.

What Imp does not provide

Imp is not an ORM, model binder for request bodies, authentication system, authorization policy engine, HTML sanitizer, validation framework, or client-side UI library. Use ordinary ASP.NET Core/application services for those responsibilities.

Clone this wiki locally