NPv.AspNetCore.Endpoints 0.2.0

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

NPv.AspNetCore.Endpoints

Reusable ASP.NET Core endpoint infrastructure for applications built with the NPv CQS and Result Pattern abstractions.

The package provides:

  • an IEndpoint contract for self-contained endpoint modules;
  • discovery and registration of endpoint modules from application assemblies;
  • concise Minimal API mappings for commands with and without results;
  • command mappings that construct a context using the current IUserContext;
  • conversion of Result and Result<T> values to HTTP responses;
  • Data Annotations request validation through an endpoint filter.

Authentication and authorization policies remain the responsibility of the host application. The package does not depend on a particular authentication scheme, Auth domain, or application-specific model.

Status

This package is at an early stage (0.x). Its public API and runtime behavior may change between minor versions.

Installation

dotnet add package NPv.AspNetCore.Endpoints

The consuming application uses ASP.NET Core and must provide the NPv CQS, execution-context, and result-pattern services required by its endpoints.

Defining an endpoint

Implement IEndpoint in an application or feature assembly:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using NPv.AspNetCore.Endpoints.Abstractions;
using NPv.AspNetCore.Endpoints.Mappings;

public sealed class CreateItemEndpoint : IEndpoint
{
    public RouteHandlerBuilder MapEndpoint(IEndpointRouteBuilder endpoints) =>
        endpoints
            .MapCommand<CreateItemCommand>("/api/items")
            .RequireAuthorization()
            .WithTags("items");
}

Authorization is applied explicitly by the consuming endpoint and is not imposed by the package.

Discovering endpoints

Register endpoint modules from one or more assemblies in the application host:

using NPv.AspNetCore.Endpoints.Discovery;

app.MapEndpoints([
    "MyApplication.Features",
    "MyApplication.Pages"
]);

Discovered endpoint modules must implement IEndpoint and have a parameterless constructor. Registration attaches ValidationFilter only to the RouteHandlerBuilder returned by MapEndpoint. If a module maps several routes, the other builders do not receive the filter automatically. Attach it explicitly to each additional route that needs validation. Calling MapCommand or MapCommandWithUser without discovery does not attach the filter either.

Validation boundary

ValidationFilter runs Data Annotations Validator.TryValidateObject on each non-null bound endpoint argument before invoking the handler. It stops at the first invalid argument, returning 400 Bad Request with that argument's errors; the handler is not invoked. Null arguments are skipped.

The filter does not recursively traverse nested objects, collections or graphs. It does not validate objects constructed later inside the handler, including the context created by a MapCommandWithUser factory. Its coverage is limited to the route builder to which it is attached and the arguments present at invocation.

Required command/application validation belongs to the CQS/Application boundary; see CQS execution and validation boundary.

Result responses

ToIResult converts successful and failed NPv results into ASP.NET Core responses:

using NPv.AspNetCore.Endpoints.Helpers;

var result = await executor.ExecuteAsync(command, cancellationToken);
return result.ToIResult();

A successful Result produces 200 OK with an empty body. A successful Result<T> produces 200 OK with its value serialized as JSON when non-null. Result<T>.Success(null) also produces 200 OK, with an empty body (not JSON null), through ASP.NET Core Results.Ok(null).

Success is determined by IsSuccess, not by the presence of a value. The Result model permits nullable T and preserves success for both Success(null) and implicit conversion of a null value. Use a nullable type argument when null is an intended successful value.

Compatibility: null-valued typed success previously produced 400 with {"errors":{}}. It now produces 200 with an empty body, consistent with the Result model; clients relying on the old failure response must adjust.

Failed Result and Result<T> values produce 400 Bad Request with the envelope below. These are reusable defaults; product-specific statuses and response bodies belong to the consuming endpoint.

HTTP error envelope

With default ASP.NET Core JSON options, both ToIResult failures and ValidationFilter failures produce 400 with this shape:

{ "errors": { "property": ["code"] } }

errors is lowercase. Each key maps to an array of codes, including repeated codes; this is not an ASP.NET Core Problem Details body. Binding failures and other host responses are outside this envelope's scope. Host JSON configuration can change serialization, including dictionary-key casing.

Source Field key Array values
ToIResult Grouped by Error.PropertyName, preserving explicit names and casing (Title stays Title). Null becomes request; empty stays "". Error.Code unchanged. A failure without errors yields {"errors":{}}.
ValidationFilter Grouped by the first ValidationResult.MemberNames entry; absent/null becomes request, empty stays "". Only the first character is lowercased (Titletitle, URLuRL); additional member names are ignored. Mapped from the exact ValidationResult.ErrorMessage string using the table below.

The casing difference is retained for compatibility; clients must not assume identical field keys across these two failure paths. The filter groups before lowercasing, so distinct names that collapse to one key (such as Title and title) cause dictionary construction to throw instead of merging errors.

Exact validation error message HTTP code
RequiredAttribute validation.required
EmailAddressAttribute validation.invalid_format
StringLengthAttribute or MinLengthAttribute validation.too_short
Any other message, including null, default/localized messages or an existing code validation.invalid_value

The filter matches message text, not attribute type. For example, [Required(ErrorMessage = nameof(RequiredAttribute))] maps to validation.required; ordinary [Required] uses a default message and maps to validation.invalid_value.

Author's Note

This library grew out of my long-standing personal interest in structuring and publishing open source packages. Over time, I’ve revisited and refined earlier internal utilities and ideas, giving them a more consistent shape and preparing them for wider reuse. Along the way, I’ve also taken the opportunity to explore how open source distribution and licensing work in the .NET ecosystem.

It’s a small step toward something I’ve always wanted to try — sharing practical, minimal tools that reflect years of learning, experimentation, and refinement.

Hopefully, someone finds it useful.

Nikolai 😛

⚖️ License

MIT — you are free to use this in commercial and open-source software.

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 NPv.AspNetCore.Endpoints:

Package Downloads
NPv.Auth.AspNetCore

ASP.NET Core endpoints, cookie handling, claims helpers, and service registration for NPv authentication modules.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0 107 9/13/2026
0.1.0 227 7/31/2026