DRN.Framework.Hosting 0.7.0

Prefix Reserved
dotnet add package DRN.Framework.Hosting --version 0.7.0
                    
NuGet\Install-Package DRN.Framework.Hosting -Version 0.7.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DRN.Framework.Hosting" Version="0.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DRN.Framework.Hosting" Version="0.7.0" />
                    
Directory.Packages.props
<PackageReference Include="DRN.Framework.Hosting" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DRN.Framework.Hosting --version 0.7.0
                    
#r "nuget: DRN.Framework.Hosting, 0.7.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DRN.Framework.Hosting@0.7.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DRN.Framework.Hosting&version=0.7.0
                    
Install as a Cake Addin
#tool nuget:?package=DRN.Framework.Hosting&version=0.7.0
                    
Install as a Cake Tool

master develop Quality Gate Status

Security Rating Maintainability Rating Reliability Rating Vulnerabilities Bugs Lines of Code

DRN.Framework.Hosting

Application shell for DRN web applications with security-first design, structured lifecycle, and type-safe routing.

TL;DR

  • Secure by Default - MFA enforced (Fail-Closed), strict CSP with Nonces, HSTS automatic
  • Opinionated Startup - DrnProgramBase with 20+ overrideable lifecycle hooks
  • Type-Safe Routing - Typed Endpoint and Page accessors replace magic strings
  • Zero-Config Infrastructure - Auto-provision Postgres/RabbitMQ in Debug mode
  • Frontend Integration - TagHelpers for Vite manifest, CSRF for HTMX, secure assets

Table of Contents


QuickStart: Beginner

DRN web applications inherit from DrnProgramBase<TProgram> to implement standard lifecycle hooks and behaviors.

using DRN.Framework.Hosting.DrnProgram;
using DRN.Framework.Hosting.HealthCheck;

namespace Sample.Hosted;

public class Program : DrnProgramBase<Program>, IDrnProgram
{
    // Entry Point (Runs the opinionated bootstrapping)
    public static async Task Main(string[] args) => await RunAsync(args);

    // [Required] Service Registration Hook
    protected override Task AddServicesAsync(WebApplicationBuilder builder, IAppSettings appSettings, IScopedLog scopedLog)
    {
        builder.Services.AddSampleInfraServices(appSettings);
        builder.Services.AddSampleApplicationServices();
        return Task.CompletedTask;
    }
}

// Immediate API endpoint for testing and health checks (Inherits [AllowAnonymous] and Get())
[Route("[controller]")]
public class WeatherForecastController : WeatherForecastControllerBase;

QuickStart: Advanced

Test your application using DRN.Framework.Testing to spin up the full pipeline including databases.

[Theory, DataInline]
public async Task WeatherForecast_Should_Return_Data(DrnTestContext context, ITestOutputHelper outputHelper)
{
    // Arrange
    var client = await context.ApplicationContext.CreateClientAsync<Program>(outputHelper);
    
    // Act
    var response = await client.GetAsync("WeatherForecast");
    
    // Assert
    response.StatusCode.Should().Be(HttpStatusCode.OK);
    var data = await response.Content.ReadFromJsonAsync<IEnumerable<WeatherForecast>>();
    data.Should().NotBeEmpty();
}

Directory Structure

DRN.Framework.Hosting/
├── DrnProgram/          # DrnProgramBase, options, actions, conventions
├── Endpoints/           # EndpointCollectionBase, PageForBase, type-safe accessors
├── Auth/                # Policies, MFA configuration, requirements
├── BackgroundServices/  # StaticAssetWarmService (pre-warm compressed assets)
├── Consent/             # GDPR cookie consent management
├── Extensions/          # Configuration, controller context, endpoint helpers
├── HealthCheck/         # WeatherForecastControllerBase for quick health checks
├── Identity/            # Identity integration and scoped user middleware
├── Middlewares/         # HttpScopeLogger, exception handling, security middlewares
├── Nexus/               # NexusClient for inter-service HTTP communication
├── TagHelpers/          # Razor TagHelpers (Vite, Nonce, CSRF, Auth-Only, Anon-Only)
├── Utils/               # AppStartupStatus, ServerSettings, Vite manifest, ResourceExtractor
├── Areas/               # Framework-provided Razor Pages (e.g., Error pages)
├── wwwroot/             # Framework style and script assets

Lifecycle & Execution Flow

DrnProgramBase manages application startup sequence to ensure security headers, logging scopes, and validation logic execute in order. Use DrnProgramActions to intercept these phases without modifying the primary Program class.

flowchart TD
    subgraph CONTAINER [" "]
        direction TB
        Start(["RunAsync()"]) --> CAB["CreateApplicationBuilder()"]
        
        subgraph BUILDER ["1. Builder Phase"]
            direction TB
            B_NOTE["Note: Handles Services & Config"]
            CAB --> CSO["ConfigureSwaggerOptions()"]
            CAB --> CDSH["ConfigureDefaultSecurityHeaders()"]
            CDSH --> CDCSP["ConfigureDefaultCsp()"]
            CAB --> CSHPB["ConfigureSecurityHeaderPolicyBuilder()"]
            CAB --> CCP["ConfigureCookiePolicy()"]
            CAB --> CSFO["ConfigureStaticFileOptions()"]
            CAB --> CRCO["ConfigureResponseCachingOptions()"]
            CAB --> CRCMO["ConfigureResponseCompressionOptions()"]
            CAB --> CCP2["ConfigureCompressionProviders()"]
            CAB --> CFHO["ConfigureForwardedHeadersOptions()"]
            CAB --> CMVCB["ConfigureMvcBuilder()"]
            CAB --> CAO["ConfigureAuthorizationOptions()"]
            CAB --> ASA["AddServicesAsync()"]
            ASA --> ABC["ApplicationBuilderCreatedAsync (Action)"]
        end

        ABC --> Build["builder.Build()"]
        
        subgraph APPLICATION ["2. Application Phase"]
            direction TB
            A_NOTE["Note: Handles Middleware Pipeline"]
            Build --> CA["ConfigureApplication()"]
            CA --> CAPS["ConfigureApplicationPipelineStart() (HSTS/Headers)"]
            CAPS --> CAPR["ConfigureApplicationPreScopeStart() (Caching/Compression/Static)"]
            CAPR --> HSM["HttpScopeMiddleware (TraceId/Logging)"]
            HSM --> CPSS["ConfigureApplicationPostScopeStart()"]
            CPSS --> UR["UseRouting()"]
            UR --> CAPREA["ConfigureApplicationPreAuthentication()"]
            CAPREA --> AUTH["UseAuthentication()"]
            AUTH --> SUM["ScopedUserMiddleware"]
            SUM --> CAPOSTA["ConfigureApplicationPostAuthentication()"]
            CAPOSTA --> MFAE["MfaExemptionMiddleware"]
            CAPOSTA --> MFAR["MfaRedirectionMiddleware"]
            MFAE --> UA["UseAuthorization()"]
            MFAR --> UA
            UA --> CPSTAZ["ConfigureApplicationPostAuthorization() (Swagger UI)"]
            CPSTAZ --> MAE["MapApplicationEndpoints()"]
        end

        MAE --> ABA["ApplicationBuiltAsync (Action)"]
        ABA --> VE["ValidateEndpoints()"]
        VE --> VSA["ValidateServicesAsync()"]
        VSA --> AVA["ApplicationValidatedAsync (Action)"]
        AVA --> Run(["application.RunAsync()"])
    end

    %% WCAG AA Compliant Styling
    %% Outer Container
    style CONTAINER fill:#F0F8FF,stroke:#B0C4DE,stroke-width:2px,color:#4682B4

    %% Subgraph Backgrounds (Direct styling)
    style BUILDER fill:#E1F5FE,stroke:#0288D1,stroke-width:2px,color:#01579B
    style APPLICATION fill:#E8EAF6,stroke:#3F51B5,stroke-width:2px,color:#1A237E

    %% Node Styles (White for contrast against subgraph)
    classDef builderNode fill:#FFFFFF,stroke:#0288D1,stroke-width:2px,color:#01579B
    classDef appNode fill:#FFFFFF,stroke:#3F51B5,stroke-width:2px,color:#1A237E
    classDef action fill:#FFE0B2,stroke:#F57C00,stroke-width:2px,color:#E65100
    classDef core fill:#E8F5E9,stroke:#43A047,stroke-width:2px,color:#1B5E20
    classDef note fill:#FFF9C4,stroke:#F57C00,stroke-width:1px,color:#E65100,stroke-dasharray: 5 5
    classDef decision fill:#FFE0B2,stroke:#E65100,stroke-width:3px,color:#E65100

    %% Apply Styles
    class CAB,CSO,CDSH,CDCSP,CSHPB,CCP,CSFO,CRCO,CRCMO,CCP2,CFHO,CMVCB,CAO,ASA builderNode
    class CA,CAPS,CAPR,HSM,CPSS,UR,CAPREA,AUTH,SUM,CAPOSTA,MFAE,MFAR,UA,CPSTAZ,MAE appNode
    class ABC,ABA,AVA action
    class Start,Build,VE,VSA,Run core
    class B_NOTE,A_NOTE note

    %% Link Styles for Decision Paths (Grey Arrows)
    linkStyle default stroke:#666,stroke-width:2px

DrnProgramBase Deep Dive

This section details the hooks for customizing the application lifecycle. DrnProgramBase implements a Hook Method pattern where the base defines the workflow and specific logic is injected via overrides.

1. Configuration Hooks (Builder Phase)

These hooks run while the WebApplicationBuilder is active, allowing you to configure the DI container and system options.

Category Method Purpose
OpenAPI ConfigureSwaggerOptions Customize Swagger UI title, version, and visibility settings.
MVC ConfigureMvcBuilder Add ApplicationParts, custom formatters, or enable Razor Runtime Compilation.
MVC ConfigureMvcOptions Add global filters, conventions, or customize model binding.
Auth ConfigureAuthorizationOptions Define security policies. Note: Sets MFA as the default/fallback by default.
Security ConfigureDefaultSecurityHeaders Define global headers (HSTS, CSP, FrameOptions).
Security ConfigureDefaultCsp Customize CSP directives (Script, Image, Style sources).
Security ConfigureSecurityHeaderPolicyBuilder Advanced conditional security policies (e.g., per-route CSP).
Cookies ConfigureCookiePolicy Set GDPR consent logic and security attributes for all cookies.
Cookies ConfigureCookieTempDataProvider Configure TempData cookie settings (HttpOnly, IsEssential).
Identity ConfigureSecurityStampValidatorOptions Customize security stamp validation and claim preservation.
Infras. ConfigureStaticFileOptions Customize caching (default: 1 year) and HTTPS compression.
Infras. ConfigureForwardedHeadersOptions Configure proxy/load-balancer header forwarding.
Infras. ConfigureRequestLocalizationOptions Configure culture providers and supported cultures.
Infras. ConfigureHostFilteringOptions Configure allowed hosts for host header validation.
Infras. ConfigureResponseCachingOptions Configure server-side response caching with sensible defaults (16MB max body size, case-insensitive paths).
Infras. ConfigureResponseCompressionOptions Configure response compression (Brotli/Gzip) for static assets. HTTPS compression disabled by default for BREACH prevention.
Infras. ConfigureCompressionProviders Configure Brotli and Gzip compression provider options including compression levels.
Infras. ConfigureBrotliCompressionLevel Customize Brotli compression level (default: SmallestSize for static assets).
Infras. ConfigureGzipCompressionLevel Customize Gzip compression level (default: SmallestSize for static assets).
Global AddServicesAsync [Required] The primary place to register your application services.

2. Pipeline Hooks (Application Phase)

These hooks define the request processing middleware sequence.

Order Hook Typical Usage
1 ConfigureApplicationPipelineStart UseForwardedHeaders, UseHostFiltering, UseCookiePolicy.
2 ConfigureApplicationPreScopeStart UseResponseCaching, UseResponseCompression, UseStaticFiles. Caching placed before compression for efficiency.
3 ConfigureApplicationPostScopeStart Add middleware that needs access to IScopedLog but runs before routing.
4 ConfigureApplicationPreAuthentication UseRequestLocalization. Runs before the user identity is resolved.
5 ConfigureApplicationPostAuthentication MfaRedirectionMiddleware, MfaExemptionMiddleware. Logic that runs after the user is known but before access checks.
6 ConfigureApplicationPostAuthorization UseSwaggerUI. Runs after access is granted but before the final endpoint.
7 MapApplicationEndpoints MapControllers, MapRazorPages, MapHubs.

3. Verification Hooks

Hook Purpose
ValidateEndpoints Ensures all type-safe endpoint accessors match actual mapped routes.
ValidateServicesAsync Scans the container for [Attribute] based registrations and ensures they are resolvable at startup via ValidateServicesAddedByAttributesAsync.

4. MFA Configuration Hooks

Hook Purpose
ConfigureMFARedirection Configure MFA setup and login redirection URLs. Returns null to disable.
ConfigureMFAExemption Configure authentication schemes exempt from MFA requirements. Returns null to disable.

5. Internal Wiring (Automatic)

  • Service Validation: Calls ValidateServicesAsync to scan [Attribute]-registered services and ensure they are resolvable at startup.
  • Secure JSON: Enforces HtmlSafeWebJsonDefaults to prevent XSS via JSON serialization.
  • Endpoint Accessor: Registers IEndpointAccessor for typed access to EndpointCollectionBase.

6. Properties

Property Default Purpose
AppBuilderType DrnDefaults Controls builder creation. Use Slim for minimal APIs.
DrnProgramSwaggerOptions (Object) Toggles Swagger generation. Defaults to IsDevelopmentEnvironment.
NLogOptions (Object) Controls NLog bootstrapping (e.g., replace logger factory).

Configuration

Configuration Precedence: Environment > Secrets > AppSettings. Always use User Secrets for local connection strings to avoid committing credentials.

Layering

  1. appsettings.json
  2. appsettings.{Environment}.json
  3. User Secrets (Development only)
  4. Environment Variables (ASPNETCORE_, DOTNET_)
  5. Mounted Directories (e.g. /app/config)
  6. Command Line Arguments

Reference Configurations

NLog (Logging)

Standard configuration for Console and Graylog output.

{
  "NLog": {
    "throwConfigExceptions": true,
    "targets": {
      "async": true,
      "console": {
        "type": "Console",
        "layout": "${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}"
      }
    },
    "rules": [
      { "logger": "*", "minLevel": "Info", "writeTo": "console" }
    ]
  }
}
Kestrel (Server)
{
  "Kestrel": {
    "EndpointDefaults": { "Protocols": "Http1" },
    "Endpoints": {
      "All": { "Url": "http://*:5988" }
    }
  }
}

Security Features

DRN Hosting enforces a "Fail-Closed" security model. If you forget to configure something, it remains locked.

1. MFA Enforcement (Fail-Closed)

The framework sets the FallbackPolicy for the entire application to require a Multi-Factor Authentication session.

  • Result: Any new controller or page you add is secure by default.
  • Opt-Out: Use [AllowAnonymous] or [Authorize(Policy = AuthPolicy.MfaExempt)] for single-factor pages like Login or MFA Setup.

2. MFA Configuration

Configure MFA behavior by overriding these hooks in your DrnProgramBase implementation:

// Configure MFA redirection URLs
protected override MfaRedirectionConfig ConfigureMFARedirection()
    => new(
        mfaSetupUrl: Get.Page.User.EnableAuthenticator,
        mfaLoginUrl: Get.Page.User.LoginWith2Fa,
        loginUrl: Get.Page.User.Login,
        logoutUrl: Get.Page.User.Logout,
        allowedUrls: Get.Page.All
    );

// Exempt specific authentication schemes from MFA
protected override MfaExemptionConfig ConfigureMFAExemption()
    => new(exemptSchemes: ["ApiKey", "Certificate"]);

Disabling MFA Entirely

To disable MFA enforcement for your entire application (e.g., for internal tools or development):

public class Program : DrnProgramBase<Program>, IDrnProgram
{
    // Return null to disable MFA redirection middleware
    protected override MfaRedirectionConfig? ConfigureMFARedirection() => null;

    // Return null to disable MFA exemption middleware  
    protected override MfaExemptionConfig? ConfigureMFAExemption() => null;

    // Override authorization to remove MFA requirement from fallback policy
    protected override void ConfigureAuthorizationOptions(AuthorizationOptions options)
    {
        // Remove MFA enforcement - authenticated users can access without MFA
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
    }
}

Disabling MFA removes a critical security layer. Only do this for internal applications on secured networks.

3. Content Security Policy (Nonce-based)

DRN automatically generates a unique cryptographic nonce for every request.

  • Automatic Protection: Scripts and styles without a matching nonce are blocked by the browser, stopping most XSS attacks.
  • Usage: Use the NonceTagHelper (see below) to automatically inject these nonces.

4. Transparent Security Headers

Standard security headers are injected into every response:

  • HSTS: Strict-Transport-Security (2 years, includes subdomains).
  • FrameOptions: DENY (prevents clickjacking).
  • ContentTypeOptions: nosniff.
  • ReferrerPolicy: strict-origin-when-cross-origin.

Cookies are configured with SameSite=Strict and HttpOnly by default to mitigate CSRF and session hijacking. The ConsentCookie system ensures compliance with privacy regulations.

6. Per-Route Security Headers

Customize security headers for specific routes by overriding ConfigureSecurityHeaderPolicyBuilder:

protected override void ConfigureSecurityHeaderPolicyBuilder(
    HeaderPolicyCollection policies, 
    IAppSettings appSettings)
{
    base.ConfigureSecurityHeaderPolicyBuilder(policies, appSettings);
    
    // Add route-specific CSP for embedding external content
    policies.AddContentSecurityPolicy(builder =>
    {
        builder.AddFrameAncestors().Self();
        builder.AddScriptSrc().Self().UnsafeInline(); // Only for specific legacy routes
    }, 
    // Apply only to specific paths
    context => context.Request.Path.StartsWithSegments("/legacy"));
}

7. Rate Limiting

Configure rate limiting by overriding AddServicesAsync:

protected override Task AddServicesAsync(
    WebApplicationBuilder builder, 
    IAppSettings appSettings, 
    IScopedLog scopedLog)
{
    builder.Services.AddRateLimiter(options =>
    {
        options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(
            context => RateLimitPartition.GetFixedWindowLimiter(
                partitionKey: context.User?.Identity?.Name ?? context.Request.Headers.Host.ToString(),
                factory: _ => new FixedWindowRateLimiterOptions
                {
                    AutoReplenishment = true,
                    PermitLimit = 100,
                    Window = TimeSpan.FromMinutes(1)
                }));
        
        options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
    });
    
    return Task.CompletedTask;
}

protected override void ConfigureApplicationPostAuthorization(WebApplication application)
{
    base.ConfigureApplicationPostAuthorization(application);
    application.UseRateLimiter();
}

Endpoint Management

Avoid "magic strings" in your code. DRN provides a type-safe way to reference routes that is verified at startup.

1. Define Your Accessors

Create a class inheriting from EndpointCollectionBase<Program> or PageCollectionBase<Program>.

public class Get : EndpointCollectionBase<Program>
{
    public static UserEndpoints User { get; } = new();
}

public class UserEndpoints : ControllerForBase<UserController>
{
    // Template: /Api/User/[controller]/[action]
    public UserEndpoints() : base("/Api/User/[controller]") { }

    // Properties matching Controller Action names
    public ApiEndpoint Login { get; private set; } = null!;
    public ApiEndpoint Profile { get; private set; } = null!;
}

2. Usage in Code

Resolve routes at compile-time with full IDE support (intellisense).

// Get the typed endpoint object
ApiEndpoint endpoint = Get.User.Login;

// Generate the path string
string url = endpoint.Path(); // "/Api/User/User/Login"

// Generate path with route parameters
string profileUrl = Get.User.ProfileDetail.Path(new() { ["id"] = userId.ToString() });

Razor TagHelpers

TagHelper Target Purpose
ViteScriptTagHelper <script src="buildwww/..."> Resolves Vite manifest entries, adds subresource integrity (SRI), and automatic nonce.
ViteLinkTagHelper <link href="buildwww/..."> Resolves Vite manifest entries for CSS assets, adds SRI.
NonceTagHelper <script>, <style>, <link>, <iframe> Automatically injects the request-specific CSP nonce.
CsrfTokenTagHelper hx-post, hx-put, etc. Automatically adds RequestVerificationToken to HTMX headers for non-GET requests.
AuthorizedOnlyTagHelper *[authorized-only] Renders the element only if the user has an active MFA session.
AnonymousOnlyTagHelper *[anonymous-only] Renders the element only if the user is not authenticated.
PageAnchorTagHelper <a asp-page="..."> Automatically adds active CSS class if the link matches current page.
ScriptDefaultsTagHelper <script> Modern defaults: defer for external scripts, type="module" for inline scripts. Opt-out via defer="false" or explicit type.

Developer Diagnostics

DRN Hosting provides deep observability into application failures, especially during the critical startup phase.

Startup Exception Reports

If the application fails to start during RunAsync, it generates a StartupExceptionReport.html in the execution directory. This report includes:

  • Full stack traces with source code highlighting (if symbols available).
  • Environment details and configuration snapshots.
  • Scoped logs leading up to the crash.

Custom Error Pages

The framework includes built-in Razor Pages for developer-time exception handling:

  • RuntimeExceptionPage: Detailed breakdown of unhandled exceptions with request state and logs.
  • CompilationExceptionPage: Visualizes Razor or code compilation errors with line-specific highlighting.

Request Body Buffering

RequestBufferingState provides size-gated request body capture for diagnostic error pages. It follows a producer/consumer pattern:

  1. ProducerTryEnableBuffering runs in HttpScopeMiddleware early in the pipeline. For POST, PUT, and PATCH requests with a known Content-Length within the configured limit, it enables Request.EnableBuffering() so the body stream becomes seekable.
  2. ConsumerReadBodyAsync is called by the error page model builder (ExceptionUtils.CreateErrorPageModelAsync) to include the request body in diagnostic reports.

Security design:

  • Size gate — requests exceeding the buffer limit are silently skipped (no buffering, no memory risk)
  • Method filter — only POST/PUT/PATCH are buffered; GET/HEAD/DELETE/OPTIONS carry no semantic body
  • Chunked transfer — requests without Content-Length (chunked encoding) are skipped to prevent unbounded DoS
  • Kestrel enforcement — Content-Length is validated per-protocol (HTTP/1.1 slicing, HTTP/2 PROTOCOL_ERROR, HTTP/3 QUIC framing)

Configuration via DrnAppFeatures (in appsettings.json):

Key Type Default Effect
DisableRequestBuffering bool false Kill switch — disables all body buffering
MaxRequestBufferingSize int 0 (→ 30,000) Max bytes to buffer. Values below 10,000 are ignored
{
  "DrnAppFeatures": {
    "DisableRequestBuffering": false,
    "MaxRequestBufferingSize": 50000
  }
}

When buffering is skipped, ReadBodyAsync returns a descriptive reason string (e.g., "Content-Length exceeded limit") instead of the body, so error pages always display useful context.

Modern HTTP Standards

DRN Hosting enforces modern web standards to improve security and predictability:

  • 303 See Other: The middleware automatically converts 302 Found redirects to 303 See Other. This ensures that following a POST request, the browser correctly uses GET for the redirected URL, adhering to established web patterns.
  • Strict Caching: By default, Cache-Control: no-store, no-cache, must-revalidate is applied to all sensitive responses to prevent data leaking into shared or browser caches.

The framework provides a structured way to handle user privacy choices:

  • ConsentCookie: A strongly-typed model to track analytics and marketing preferences.
  • Middleware Integration: ScopedUserMiddleware automatically extracts consent data and makes it available via ScopeContext.Data, allowing services to check consent status without reaching into the raw cookie.

Example: Secure Script Loading


<script src="buildwww/app/main.ts" crossorigin="anonymous"></script>


<script src="/app/main.abc123.js" 
        integrity="sha256-xyz..." 
        nonce="random_nonce_here" 
        crossorigin="anonymous"></script>

Static Asset Pre-Warming

StaticAssetWarmService is a [HostedService] that populates the ResponseCaching middleware cache with compressed static assets immediately after application startup.

How it works:

  1. Waits for the host to fully start via IAppStartupStatus
  2. Reads all entries from the Vite manifest
  3. Requests each asset with Accept-Encoding: br and Accept-Encoding: gzip against the loopback address (via IServerSettings)
  4. ResponseCaching stores each compressed variant keyed on Vary: Accept-Encoding

Compression defaults — both use CompressionLevel.SmallestSize (maximum compression) since only static files are compressed and the cost is paid once at startup:

Provider Default Level Override Hook
Brotli SmallestSize (Level 11) ConfigureBrotliCompressionLevel()
Gzip SmallestSize ConfigureGzipCompressionLevel()

First request after startup returns pre-compressed content from cache — zero compression latency for end users.

Local Development Infrastructure

Use DRN.Framework.Testing to provision infrastructure (Postgres, RabbitMQ) during local development without manual Docker management.

1. Add Conditional Reference

Add the following to your .csproj file to ensure the testing library (and its heavy dependencies like Testcontainers) is only included during development.

<ItemGroup Condition="'$(Configuration)' == 'Debug'">
    <ProjectReference Include="..\DRN.Framework.Testing\DRN.Framework.Testing.csproj" />
</ItemGroup>

2. Configure Startup Actions

Implement DrnProgramActions to trigger the auto-provisioning.

#if DEBUG
public class SampleProgramActions : DrnProgramActions
{
    public override async Task ApplicationBuilderCreatedAsync<TProgram>(
        TProgram program, WebApplicationBuilder builder,
        IAppSettings appSettings, IScopedLog scopedLog)
    {
        var options = new ExternalDependencyLaunchOptions
        {
            PostgresContainerSettings = new() 
            { 
                Reuse = true, // Faster restarts
                HostPort = 6432 // Avoid conflicts with local Postgres
            }
        };

        // Auto-starts containers if not running and updates AppSettings
        await builder.LaunchExternalDependenciesAsync(scopedLog, appSettings, options);
    }
}
#endif

Hosting Utilities

IAppStartupStatus

Singleton gate for background services that need to wait until the host has fully started before executing.

public class MyWorker(IAppStartupStatus startupStatus) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        if (!await startupStatus.WaitForStartAsync(stoppingToken))
            return; // Cancelled before startup completed

        // Application is fully started — safe to proceed
    }
}

IServerSettings

Resolves bound server addresses from Kestrel. Normalizes wildcard hosts (0.0.0.0, [::], +, *) to localhost for internal self-requests. Prefers HTTP over HTTPS to avoid TLS overhead.

public class MyService(IServerSettings server)
{
    public void LogAddresses()
    {
        var loopback = server.GetLoopbackAddress();   // e.g. "http://localhost:5988"
        var all = server.GetAllAddresses();            // All normalized bound addresses
    }
}

Global Usings

Standard global usings for Hosted applications to reduce boilerplate:

global using DRN.Framework.Hosting.DrnProgram;
global using DRN.Framework.Hosting.Endpoints;
global using DRN.Framework.Utils.DependencyInjection;
global using DRN.Framework.Utils.Logging;
global using DRN.Framework.Utils.Settings;
global using Microsoft.AspNetCore.Mvc;

For complete examples, see Sample.Hosted.


Documented with the assistance of DiSCOS


Semper Progressivus: Always Progressive

Commit Info

Author: Duran Serkan KILIÇ
Date: 2026-03-08 23:03:03 +0300
Hash: a79b5357114f874f8a2956315f27911d316b9bac

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on DRN.Framework.Hosting:

Package Downloads
DRN.Framework.Testing

DRN.Framework.Testing package encapsulates testing dependencies and provides practical, effective helpers such as resourceful data attributes and test context. This package enables a new encouraging testing technique called as DTT(Duran's Testing Technique). With DTT, any developer can write clean and hassle-free unit and integration tests without complexity. ## Commit Info Author: Duran Serkan KILIÇ Date: 2026-03-08 23:03:03 +0300 Hash: a79b5357114f874f8a2956315f27911d316b9bac

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.7.0 30 3/8/2026
0.7.0-preview067 34 3/7/2026
0.7.0-preview066 94 2/28/2026
0.7.0-preview065 92 2/25/2026
0.7.0-preview064 94 2/22/2026
0.7.0-preview063 93 2/21/2026
0.7.0-preview062 91 2/11/2026
0.7.0-preview061 117 2/7/2026
0.7.0-preview060 99 1/28/2026
0.7.0-preview059 112 1/26/2026
0.7.0-preview058 107 1/25/2026
0.7.0-preview057 100 1/25/2026
0.7.0-preview056 103 1/10/2026
0.7.0-preview055 284 12/16/2025
0.7.0-preview054 195 12/13/2025
0.7.0-preview053 136 12/12/2025
0.7.0-preview052 455 12/9/2025
0.7.0-preview051 316 12/7/2025
0.7.0-preview050 224 12/7/2025
0.7.0-preview049 213 11/26/2025
Loading failed

Not every version includes changes, features or bug fixes. This project can increment version to keep consistency with other DRN.Framework projects.

## Version 0.7.0

My family celebrates the enduring legacy of Mustafa Kemal Atatürk's enlightenment ideals and honors 8 March, International Women's Day, a cause inseparable from his vision of equality. This release is dedicated to freedom of speech, democracy, women's rights, and Prof. Dr. Ümit Özdağ, a defender of Mustafa Kemal Atatürk’s enlightenment ideals.

> [!WARNING]
> Since v0.6.0 (released 10 November 2024), substantial changes have occurred. This release notes file has been reset to reflect the current state of the project as of 08 March 2026. Previous history has been archived to maintain a clean source of truth based on the current codebase.

### New Features

*   **Security First Architecture**
   *   **Fail-Closed MFA**: `MfaEnforcingAuthorizationPolicyProvider` enforces Multi-Factor Authentication by default. Opt-out via `[AllowAnonymous]` or `[Authorize(Policy = AuthPolicy.MfaExempt)]`.
   *   **Strict CSP & Nonce**: Content Security Policy with automatic nonce generation for all scripts and styles.
   *   **Security & GDPR Headers**: Automatic injection of `HSTS`, `FrameOptions`, `ContentTypeOptions`, and `SameSite=Strict`/`HttpOnly` cookies.
   *   **MFA Hooks**: `ConfigureMFARedirection` and `ConfigureMFAExemption` for customizing authentication flow.
*   **DrnProgramBase Lifecycle Hooks**
   *   **Builder Phase**:
       *   `ConfigureSwaggerOptions`: Customize OpenAPI metadata.
       *   `ConfigureDefaultSecurityHeaders` / `ConfigureDefaultCsp`: Define security policies.
       *   `ConfigureMvcBuilder` / `ConfigureMvcOptions`: Customize MVC conventions and runtime compilation.
       *   `ConfigureStaticFileOptions` / `ConfigureResponseCachingOptions`: Optimize asset delivery with server-side response caching (16MB max, case-insensitive) and automatic static asset caching.
       *   `ConfigureResponseCompressionOptions` / `ConfigureCompressionProviders`: Brotli and Gzip compression for static assets with built-in BREACH/CRIME protection.
       *   `ConfigureCookiePolicy`: Centralized security settings for cookies (HttpOnly, Secure, SameSite) with environment-aware defaults via `IsDevelopmentEnvironment`.
   *   **Pipeline Phase**:
       *   `ConfigureApplicationPipelineStart`: HSTS, Forwarded Headers.
       *   `ConfigureApplicationPreScopeStart`: Static files, caching, and compression.
       *   `ConfigureApplicationPreAuthentication` / `PostAuthentication`: Localization, MFA logic.
       *   `MapApplicationEndpoints`: Route mapping.
   *   **DrnProgramActions**: "Hook Method" pattern for intercepting startup (`ApplicationBuilderCreatedAsync`, `ApplicationBuiltAsync`, `ApplicationValidatedAsync`) without modifying Program.cs.
*   **Type-Safe Routing**
   *   **EndpointCollectionBase**: Strongly-typed API accessors (e.g., `Get.Endpoint.User.Login.Path()`).
   *   **PageCollectionBase**: Type-safe Razor Page navigation (e.g., `Get.Page.User.Profile`).
   *   **Validation**: `ValidateEndpoints` ensures all typed routes match actual mapped endpoints at startup.
*   **Frontend Integration & TagHelpers**
   *   **Asset Management**: `ViteScriptTagHelper` and `ViteLinkTagHelper` for resolving manifest-based assets with integrity checks.
   *   **Security**: `NonceTagHelper` (auto-injects CSP nonce) and `CsrfTokenTagHelper` (auto-injects token for HTMX).
   *   **Conditional Rendering**: `AuthorizedOnlyTagHelper` (MFA-aware) and `AnonymousOnlyTagHelper`.
   *   **Navigation**: `PageAnchorAspPageTagHelper` and `PageAnchorHrefTagHelper` automatically mark active links.
   *   **Modern Defaults**: `ScriptDefaultsTagHelper` applies `defer` for external scripts and `type="module"` for inline scripts by default, with explicit opt-out support.
*   **Advanced Middleware & HTTP Standards**
   *   **Standardized Redirects**: Automatically converts 302 (Found) to 303 (See Other) for modern HTTP/1.1 POST response compliance.
   *   **Security-First Headers**: Default `Cache-Control: no-store` and strictly configured HSTS/CSP/Nonce headers.
   *   **Malicious Request Detection**: Automatically aborts requests to protected developer URIs or suspicious paths.
   *   **Flurl Resilience**: Integrated mapping of `FlurlHttpException` to standard gateway status codes.
*   **Developer Diagnostics**
   *   **Startup Exception Reports**: Generates detailed `StartupExceptionReport.html` if the application fails during initialization (Development only).
   *   **Enhanced Error Pages**: Custom `RuntimeExceptionPage` and `CompilationExceptionPage` with stack trace analysis and model capture.
   *   **Diagnostic Events**: Built-in integration with `DiagnosticSource` for unhandled exception tracking.
*   **Identity & GDPR Consent**
   *   **Consent Integration**: Automatic extraction and propagation of `ConsentCookie` model via `ScopedUserMiddleware`.
   *   **Identity Helpers**: `IdentityApiHelper` for standardized validation problem reporting.
*   **Static Asset Pre-Warming**
   *   **`StaticAssetWarmService`**: `[HostedService]` that populates `ResponseCaching` with Brotli and Gzip compressed Vite manifest assets at startup — zero compression latency for end users.
   *   **Compression**: `SmallestSize` (maximum) for both Brotli (Level 11) and Gzip by default, overrideable via `ConfigureBrotliCompressionLevel()` / `ConfigureGzipCompressionLevel()`.
*   **Infrastructure & Development**
   *   **`IAppStartupStatus`**: Singleton gate for background services to await full host startup before executing.
   *   **`IServerSettings`**: Resolves bound Kestrel addresses with wildcard-to-localhost normalization for internal self-requests.
   *   **Local Provisioning**: `LaunchExternalDependenciesAsync` auto-starts Postgres/RabbitMQ Testcontainers in Debug mode.
   *   **Validation**: `ValidateEndpoints` and `ValidateServicesAddedByAttributesAsync` ensure system integrity at startup.
   *   **Identity Integration**: `IdentityControllerBase` and `ScopedUserMiddleware` for deep identity context propagation.

---

Documented with the assistance of [DiSCOS](https://github.com/duranserkan/DRN-Project/blob/develop/DiSCOS/DiSCOS.md)

---
**Semper Progressivus: Always Progressive**
 
 
## Commit Info  
Author: Duran Serkan KILIÇ  
Date: 2026-03-08 23:03:03 +0300  
Hash: a79b5357114f874f8a2956315f27911d316b9bac