Decode.AspNetCore 3.0.0

dotnet add package Decode.AspNetCore --version 3.0.0
                    
NuGet\Install-Package Decode.AspNetCore -Version 3.0.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="Decode.AspNetCore" Version="3.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Decode.AspNetCore" Version="3.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Decode.AspNetCore" />
                    
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 Decode.AspNetCore --version 3.0.0
                    
#r "nuget: Decode.AspNetCore, 3.0.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 Decode.AspNetCore@3.0.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=Decode.AspNetCore&version=3.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Decode.AspNetCore&version=3.0.0
                    
Install as a Cake Tool

Decode.AspNetCore

ASP.NET Core extensions for the Decode ecosystem, providing standardized infrastructure for APIs, including OpenAPI 3.0 Swagger configuration, User Context resolution, global exception handling, and base controllers integrated with Domain Notifications.

🚀 Features

  • Swagger / OpenAPI 3.0 Integration: 1-line setup (AddDecodeSwagger) with OpenAPI 3.0 HTTP Bearer authentication, environment security guard (UseDecodeSwagger), auto-discovery of XML documentation files, and native Swashbuckle escape hatches.
  • IUserContext & UserContext: Web API implementation of IUserContext using IHttpContextAccessor, safely resolving UserId, client IP address, User-Agent, Bearer access token, and custom claims.
  • GlobalExceptionMiddleware: A robust safety net that captures unhandled exceptions, logs them with a unique errorId, and returns a standardized JSON response.
  • ApiControllerBase: A base controller that simplifies response handling by automatically checking for domain notifications and returning appropriate HTTP status codes.
  • ApiResponse: A unified response model for all API endpoints: { success, data, errors, errorId }.

📦 Installation

Add the package to your API project:

dotnet add package Decode.AspNetCore

🛠️ Setup

1. Register Services in Program.cs

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

// 1. Add Decode ASP.NET Core infrastructure (Domain Notifications + IUserContext)
builder.Services.AddDecodeAspNetCore();

// 2. Add Decode Swagger/OpenAPI 3.0
builder.Services.AddDecodeSwagger(options =>
{
    options.Title = "My Enterprise API";
    options.Version = "v1";
    options.Description = "Authentication and Core API endpoints.";
    options.EnableJwtBearer = true;      // Enables native HTTP Bearer lock icon
    options.IncludeXmlComments = true;   // Auto-discovers all *.xml docs in execution folder
});

2. Configure HTTP Pipeline

WebApplication app = builder.Build();

// 1. Add global exception middleware at top of pipeline
app.UseGlobalExceptionMiddleware();

// 2. Enable Swagger UI (auto-disabled in Production unless forced)
app.UseDecodeSwagger();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();

📖 Usage Examples

1. Using ApiControllerBase with Notifications

Inherit from ApiControllerBase to get automatic notification handling:

using Decode.AspNetCore.Controllers;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ApiControllerBase
{
    private readonly IProductService _service;

    public ProductsController(IProductService service)
    {
        _service = service;
    }

    [HttpPost]
    public IActionResult Create(Product product)
    {
        _service.Create(product);

        // If _service added notifications to IDomainNotificationContext,
        // CreatedResponse() will automatically return 400 Bad Request (or custom status code).
        // Otherwise, it returns 201 Created.
        return CreatedResponse($"/api/products/{product.Id}", product);
    }
}

2. Using IUserContext in Controllers or Services

using Decode.Context.Abstractions;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProfileController(IUserContext userContext) : ControllerBase
{
    [HttpGet]
    public IActionResult GetMyProfile()
    {
        Guid currentUserId = userContext.UserId;
        string clientIp = userContext.IpAddress;

        return Ok(new
        {
            UserId = currentUserId,
            Ip = clientIp,
            IsAuthenticated = userContext.IsAuthenticated
        });
    }
}
Client IP address and proxies (changed in 2.0.0)

IpAddress returns Connection.RemoteIpAddress and does not read X-Forwarded-For by default.

Any client can set that header on its own request, so trusting it unconditionally — as versions before 2.0.0 did — let a caller choose the address recorded in audit logs, rate limiters and abuse heuristics.

The recommended setup is ASP.NET Core's own ForwardedHeadersMiddleware, which validates against a configured list of known proxies and rewrites RemoteIpAddress before UserContext ever reads it:

builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.1"));
});

app.UseForwardedHeaders();

If you must read the header directly, opt in explicitly — only when the application sits behind a proxy you control that overwrites it:

builder.Services.AddDecodeAspNetCore(options => options.TrustForwardedHeaders = true);

IPv6 addresses are also preserved as-is. Earlier versions applied MapToIPv4() to every remote address, which reinterprets the last four bytes of a genuine IPv6 address: 2001:db8::1 was reported as 0.0.0.1. Only IPv4-mapped addresses (::ffff:203.0.113.10) are unwrapped now.

Standard Response Format

Success (200 OK / 201 Created):

{
  "success": true,
  "data": { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "name": "Product A" },
  "errors": null,
  "errorId": null
}

Domain Notification Error (400 Bad Request):

{
  "success": false,
  "data": null,
  "errors": ["Product name is required"],
  "errorId": null
}

Unhandled Error (500 Internal Server Error):

{
  "success": false,
  "data": null,
  "errors": ["An internal server error occurred. Please try again later."],
  "errorId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

errorId is Activity.Current?.Id when an activity is in flight — the W3C trace id above, which correlates the response with the distributed trace and the logged [{ErrorId}] entry. It falls back to HttpContext.TraceIdentifier (the shorter 0HMA1B2C3D4E5:00000001 form) when no activity exists.

Development shows more. Outside Development the errors array carries only the generic message above. In Development it is replaced by the full exception chain — Type: message for the exception and every inner exception — plus a final StackTrace: ... entry. That is deliberate for debugging, and it is why the environment check exists: do not run a public deployment with ASPNETCORE_ENVIRONMENT=Development.

Two cases do not produce this envelope. If the client disconnects mid-request the middleware logs at Information and returns nothing, since there is nobody left to receive it. If the response has already started, the original exception is rethrown rather than overwritten — touching the status code at that point would throw from inside the catch and replace the real fault with a misleading one.

📄 License

This project is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.0.0 98 9/14/2026
2.1.0 96 9/13/2026
2.0.2 90 9/13/2026
2.0.1 91 9/13/2026
2.0.0 127 7/28/2026
1.0.3 132 6/19/2026
1.0.2 127 5/26/2026
1.0.1 118 4/26/2026