Nedo.AspNet.ApiContracts 2.0.0

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

Nedo.AspNet.ApiContracts

Standardized API contracts for ASP.NET Core. One uniform request/response envelope, RFC 9457 error responses, W3C Trace Context / OpenTelemetry correlation, and Swagger filters that work out of the box. Built on top of Nedo.AspNet.Request.Validation.

Install

dotnet add package Nedo.AspNet.ApiContracts

30-second setup

using Nedo.AspNet.ApiContracts.Extensions;
using Nedo.AspNet.ApiContracts.Swagger;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddApiContracts();             // controllers + validation + suppressions
builder.Services.AddGlobalExceptionHandler();   // RFC 9457 problem details on throw

builder.Services.AddSwaggerGen(o =>
{
    o.OperationFilter<StandardHeaderFilter>();
    o.AddStandardAuthorization();
});

var app = builder.Build();
app.UseExceptionHandler();
app.UseSwagger();
app.UseSwaggerUI();
app.MapControllers();
app.Run();

What you get

Every endpoint returns the same envelope:

// success
{
  "success": true,
  "title":   "Products retrieved",
  "status":  200,
  "code":    "SUC-RTV-001",
  "data":    [ /* … */ ],
  "pagination": { "page": 1, "page_size": 20, "total_count": 100, "total_pages": 5, "has_next_page": true },
  "info": {
    "trace_id":   "4bf92f3577b34da6a3ce929d0e0e4736",
    "span_id":    "00f067aa0ba902b7",
    "trace_flags":"01",
    "service_name":"nedo.product",
    "correlation_id": "req-abc-123",
    "language":   "id-ID",
    "timezone":   "Asia/Jakarta"
  }
}

// error (RFC 9457; Content-Type: application/problem+json)
{
  "success":  false,
  "type":     "urn:nedo:problems:ERR-NF-002",
  "title":    "Product not found",
  "status":   404,
  "detail":   "No product with id=42",
  "instance": "/api/products/42",
  "code":     "ERR-NF-002",
  "info":     { /* same trace fields */ }
}

Throw any exception inside an action and GlobalExceptionHandler produces the right envelope. Decorate request DTOs with validation attributes and the validation filter produces the same shape on bad input. No per-controller error-handling code.

Validation & safety defaults

The query/request contracts ship with validation attributes already applied. Malformed payloads are rejected at model binding before they reach your code.

Built-in on the query DSL (no extra setup):

  • [SafeIdentifier] on every field, table, column, navigation, alias, on slot — rejects anything outside ^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$ (max 128 chars). Defense-in-depth against SQL/LINQ injection through caller-supplied identifiers; doesn't replace per-entity allowlisting at the resolver, but ensures the shape is safe.
  • [AllowedValues] on operator, logic, direction, function, search_mode, join.type. Unknown values are rejected.
  • Bounded pagination: page ≥ 1, page_size 1–1000, cursor limit 1–1000, include.take 1–1000.
  • Bounded batches: BatchRequest.dataMaxBatchItems (1000), BatchQueryRequest.queriesMaxBatchQueries (50).
  • Bounded filter nesting: FilterGroup recursion ≤ FilterGroupLimits.MaxDepth (10).
  • [Required] + [MaxLength(256)] on UpdateRequest.id and BatchItem.id.
  • AggregateItem.field is conditionally required when function ∈ {Sum, Avg, Min, Max}.

Exception → response mapping (GlobalExceptionHandler):

Throws Status / code
ValidationException (System.ComponentModel.DataAnnotations) 400 + structured field_errors / form_errors
KeyNotFoundException 404 ERR-NF-001
UnauthorizedAccessException 401 ERR-AUTH-001
ArgumentException 400 ERR-BIZ-001
InvalidOperationException 409 ERR-BIZ-003
OperationCanceledException (when RequestAborted is cancelled) 499 ERR-CLI-001, no body
anything else 500 ERR-SRV-001

Header validation (opt-in middleware):

builder.Services.AddStandardHeaderValidation(opts =>
{
    // Optional: tighten Tenant-Id to a UUID
    opts.AddOrReplace(new HeaderRule {
        Name = StandardHeaders.TenantId,
        MaxLength = 36,
        Pattern = new Regex(@"^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$")
    });
});

// Place BEFORE UseExceptionHandler / MapControllers
app.UseStandardHeaderValidation();

Defaults rule out control characters (CR/LF — response-splitting / log-injection) and apply per-header length + format checks for X-Request-Id, X-Correlation-Id, X-Idempotency-Key, X-Tenant-Id, X-Client-Id, X-Role-Id, X-Client-Version, Accept-Language, X-Timezone. Authorization is intentionally not validated (owned by the auth middleware). Rejected requests get a 400 RFC 9457 problem with ERR-VAL-HDR-001.

Documentation

Full documentation lives in docs/. Eleven topic-focused guides:

# Doc
01 Concept Mental model, standards followed
02 Installation & Setup NuGet install, what each call does
03 Standard Headers Header reference + when to use each
04 Requests BaseRequest<T>, the seven request types
05 Queries Filter / search / sort / pagination DSL
06 Responses BaseResponse<T> + per-operation info
07 Error Handling RFC 9457 problem details + global handler
08 Observability W3C Trace Context + OpenTelemetry
09 Swagger / OpenAPI Filters, security schemes, multi-doc
10 Response Codes Full code reference
11 Recipes Copy-paste patterns (CRUD, batch, tests)

New to the library? Read 01-Concept02-Installation11-Recipes in that order.

A runnable sample lives in samples/Nedo.AspNet.ApiContracts.Sample/ — every recipe wired to working code with .http files you can run from your IDE.

Standards

Concern Standard
Error responses RFC 9457 — Problem Details for HTTP APIs
Trace correlation W3C Trace Context
Service identity OpenTelemetry — Resource Semantic Conventions
Date/time RFC 3339 / ISO 8601
Language tags BCP 47
HTTP semantics RFC 9110

Versioning & Release

This project uses git tags for versioning. The GitLab CI/CD pipeline builds and publishes to NuGet.org on tag push.

git tag -a v2.0.0 -m "Release v2.0.0"
git push origin v2.0.0

The tag must start with v (e.g., v1.0.0, not 1.0.0). Prereleases follow SemVer: v1.1.0-beta.1.

Contributing

dotnet build
dotnet test          # tests live under tests/Nedo.AspNet.ApiContracts.Tests

Open an MR against develop. Add tests for behavior changes — the suite uses xUnit and asserts envelope shape and OTel field correctness.

License

MIT

Product Compatible and additional computed target framework versions.
.NET 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 was computed.  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 (2)

Showing the top 2 NuGet packages that depend on Nedo.AspNet.ApiContracts:

Package Downloads
Nedo.AspNet.Authentication.Local

Local username/password authentication for Nedo.AspNet.Authentication with EF Core persistence.

Nedo.AspNet.Exception.AspNetCore

ASP.NET Core integration for Nedo.AspNet.Exception: IExceptionHandler emitting Nedo.AspNet.ApiContracts BaseResponse<T> envelopes (RFC 9457), DI extensions, optional middleware.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 421 5/2/2026
1.1.5 87 5/2/2026
1.1.4 100 4/23/2026
1.1.3 103 4/23/2026
1.1.2 117 2/13/2026
1.1.1 111 2/13/2026
1.1.0 108 2/12/2026
1.0.0 111 2/11/2026