TinyHelpers.AspNetCore
4.2.12
dotnet add package TinyHelpers.AspNetCore --version 4.2.12
NuGet\Install-Package TinyHelpers.AspNetCore -Version 4.2.12
<PackageReference Include="TinyHelpers.AspNetCore" Version="4.2.12" />
<PackageVersion Include="TinyHelpers.AspNetCore" Version="4.2.12" />
<PackageReference Include="TinyHelpers.AspNetCore" />
paket add TinyHelpers.AspNetCore --version 4.2.12
#r "nuget: TinyHelpers.AspNetCore, 4.2.12"
#:package TinyHelpers.AspNetCore@4.2.12
#addin nuget:?package=TinyHelpers.AspNetCore&version=4.2.12
#tool nuget:?package=TinyHelpers.AspNetCore&version=4.2.12
Tiny Helpers for ASP.NET Core
TinyHelpers.AspNetCore is a small collection of practical helpers for ASP.NET Core applications: claims handling, configuration binding, localization, middleware support, validation attributes, route helpers, OpenAPI helpers, and exception handling.
The package is designed to reduce code duplication and keep common behavior centralized in one place.
Upgrade from 3.x to 4.x
Swashbuckle / Swagger support has been moved to the separate TinyHelpers.AspNetCore.Swashbuckle package.
If you were using extensions such as AddAcceptLanguageHeader() and AddDefaultProblemDetailsResponse() together with AddSwaggerGen, you now need to install that package as well.
Compatibility
The package targets:
- .NET 8
- .NET 9
- .NET 10
OpenAPI features are available when the consuming project targets .NET 9 or .NET 10. Some extensions are also framework-specific:
EnableEnumSupport()is available only on .NET 9+UseStrictNumericSchemas()is available only on .NET 10+WithResponseDescription()andWithLocationHeader()are available only on .NET 10+
Installation
Install the package from NuGet:
dotnet add package TinyHelpers.AspNetCore
Or search for TinyHelpers.AspNetCore in the Visual Studio Package Manager.
Contents
- Claims handling
- Configuration and options
- Localization and pipeline
- Validation and authorization attributes
- Route and Minimal API helpers
- OpenAPI
- Exception handling
- Quick examples
Claims handling
This area contains two groups of extensions:
- helpers for
IList<Claim>that modify the collection - helpers for
ClaimsPrincipalthat read values without repeating the same lookup logic
ClaimsExtensions
| Method | What it does | When to use it |
|---|---|---|
Update(string type, string value) |
Removes the first claim with that type and adds a new one with the specified value. | When you want to replace an identity value without leaving logical duplicates. |
Remove(string type) |
Removes the first claim with that type. | When you want to remove a specific value from the list. |
ClaimsPrincipalExtensions
| Method | What it does | When to use it |
|---|---|---|
GetClaimValues(string type) |
Returns all claim values as string?. |
When a claim can have multiple values. |
GetClaimValues<T>(string type) |
Returns all values converted to type T. |
When you need typed conversion of claim values. |
GetClaimValue(string type) |
Returns the first matching value as string?. |
When the claim is single-value. |
GetClaimValue<T>(string type) |
Returns the first matching value converted to T?. |
When you want a typed value with default fallback. |
HasClaim(string type) |
Checks whether at least one claim with that type exists. | When you need a quick presence check. |
Example
using System.Security.Claims;
using TinyHelpers.AspNetCore.Extensions;
var claims = new List<Claim>
{
new(ClaimTypes.Name, "Marco"),
new(ClaimTypes.Role, "Admin")
};
claims.Update(ClaimTypes.Name, "Mario");
claims.Remove(ClaimTypes.Role);
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims));
var name = principal.GetClaimValue(ClaimTypes.Name);
var roles = principal.GetClaimValues(ClaimTypes.Role);
var hasName = principal.HasClaim(ClaimTypes.Name);
Configuration and options
IConfigurationExtensions
| Method | What it does | When to use it |
|---|---|---|
GetSection<T>(sectionName) |
Reads a configuration section and materializes it as object T. |
When you want a strongly typed object without registering it immediately in the container. |
ServiceCollectionExtensions
| Method | What it does | When to use it |
|---|---|---|
ConfigureAndGet<T>(configuration, sectionName) |
Binds a section and also registers the options in the container, returning the created instance. | When you want to configure and read the same options during startup. |
Replace<TService, TImplementation>(lifetime) |
Replaces a registered service with another implementation. | When you want to swap a service without rewriting the whole registration. |
AddDefaultProblemDetails() |
Registers ProblemDetails with a consistent and centralized configuration. |
When you want uniform RFC 7807 responses across the app. |
AddDefaultExceptionHandler() |
Registers the library's default IExceptionHandler and ensures ProblemDetails is available too. |
When you want unhandled exceptions to become a standard payload. |
UseDefaults(ProblemDetailsContext context) |
Applies the library's default values to a ProblemDetailsContext. |
When you customize CustomizeProblemDetails but still want the same base behavior. |
AddRequestLocalization(params string[] cultures) |
Registers localization using only the list of supported cultures. The first culture becomes the default. | When you do not need to configure providers manually. |
AddRequestLocalization(IEnumerable<string> cultures, Action<IList<IRequestCultureProvider>>? providersConfiguration) |
Registers localization and lets you customize the culture-provider chain. The first culture becomes the default. | When you want to change the provider order or composition. |
Example
var settings = builder.Services.ConfigureAndGet<MySettings>(builder.Configuration, "MySettings");
builder.Services.AddRequestLocalization("it-IT", "en-US");
builder.Services.AddDefaultProblemDetails();
builder.Services.AddDefaultExceptionHandler();
Localization and pipeline
NavigationManagerExtensions
| Method | What it does | When to use it |
|---|---|---|
TryGetQueryString<T>(key, out value) |
Reads a value from the current query string and tries to convert it to int, string, decimal, or bool. |
When you work in Blazor or components that need to read URL parameters. |
ApplicationBuilderExtensions
| Method | What it does | When to use it |
|---|---|---|
UseRequestRewind() |
Enables request-body buffering so the body can be read more than once. | When a middleware or filter must inspect the body without consuming it permanently. |
EnableRequestRewindMiddleware
The internal middleware used by UseRequestRewind() calls Request.EnableBuffering() before passing control to the next middleware. It is useful for audit, validation, or request-signature scenarios.
Example
app.UseRequestRewind();
if (navManager.TryGetQueryString<int>("page", out var page))
{
// use page
}
Validation and authorization attributes
AllowedExtensionsAttribute
Validates that an IFormFile uses one of the allowed file extensions.
- Constructor:
AllowedExtensionsAttribute(params string[] extensions) FormatErrorMessage(string name): generates the error message with the allowed extensions
ContentTypeAttribute
Validates the Content-Type of an IFormFile.
ContentTypeAttribute(params string[] validContentTypes): uses an explicit MIME type listContentTypeAttribute(FileType fileType): uses a predefined groupFormatErrorMessage(string name): generates the error message with the allowed MIME types
FileType
Supporting enum for ContentTypeAttribute:
ImageVideoAudio
FileSizeAttribute
Validates that an IFormFile does not exceed a maximum size.
- Constructor:
FileSizeAttribute(int maxFileSizeInBytes) FormatErrorMessage(string name): generates the error message with the limit in bytes
RoleAuthorizeAttribute
Automatically builds the Roles list of AuthorizeAttribute from one or more roles.
- Constructor:
RoleAuthorizeAttribute(params string[] roles)
Example
using TinyHelpers.AspNetCore.DataAnnotations;
public sealed class UploadModel
{
[AllowedExtensions("jpg", "png")]
[ContentType(FileType.Image)]
[FileSize(2_000_000)]
public IFormFile File { get; set; } = default!;
}
[RoleAuthorize("Admin", "Manager")]
public IActionResult SecretArea()
{
return Ok();
}
Route and Minimal API helpers
RouteHandlerBuilderExtensions
| Method | What it does | Availability |
|---|---|---|
ProducesDefaultProblem(params int[] statusCodes) |
Adds ProblemDetails responses to the route metadata for the specified status codes. |
.NET 8+ |
WithResponseDescription(int statusCode, string description) |
Updates the description of an existing OpenAPI response. | .NET 10+ |
WithLocationHeader(string description, int statusCode) |
Adds a Location header to the specified creation response. |
.NET 10+ |
These helpers keep Minimal API configuration close to the route instead of scattering OpenAPI metadata in separate places.
Example
app.MapPost("/orders", () => Results.Created("/orders/1", new { Id = 1 }))
.ProducesDefaultProblem(StatusCodes.Status400BadRequest, StatusCodes.Status500InternalServerError);
.NET 10 example
app.MapPost("/orders", () => Results.Created("/orders/1", new { Id = 1 }))
.WithResponseDescription(StatusCodes.Status201Created, "Order created successfully")
.WithLocationHeader();
OpenAPI
The OpenAPI extensions are available when the consuming project uses the package on .NET 9 or .NET 10.
OpenApiExtensions
| Method | What it does | Notes |
|---|---|---|
AddOpenApiOperationParameters(setupAction) |
Registers reusable OpenAPI parameters inside OpenApiOperationOptions.Parameters. |
Lets you declare common parameters once. |
AddAcceptLanguageHeader() |
Adds the Accept-Language header to documented operations. |
Useful when the app uses request localization. |
AddDefaultProblemDetailsResponse() |
Adds a default error response based on ProblemDetails. |
In .NET 9 it also adds the document transformer. |
AddOperationParameters() |
Adds the parameters configured through OpenApiOperationOptions. |
The bridge between the options bag and the generated document. |
RemoveServerList() |
Removes the server list from the OpenAPI document. | Useful when you want a more portable document across environments. |
WriteNumberAsString() |
Aligns the schema with numbers serialized as strings. | Useful when runtime JSON uses JsonNumberHandling.WriteAsString. |
DescribeAllParametersInCamelCase() |
Converts query parameter names to camel case in the document. | Keeps documentation consistent with JSON naming. |
AddTimeExamples() |
Adds readable examples for TimeSpan and TimeOnly. |
Helps readers understand the expected format. |
EnableEnumSupport() |
Improves enum representation in the document. | .NET 9 only. |
UseFullTypeNameSchemaIds() |
Uses the full type name for schema IDs. | Prevents collisions between types with the same name. |
UseStrictNumericSchemas() |
Removes the string fallback from numeric schemas. |
.NET 10 only. |
OpenApiSchemaHelper
This utility creates ready-to-use schema fragments that can be reused in transformers or other OpenAPI customizations.
.NET 9
CreateStringSchema(string? defaultValue = null)CreateSchema<TValue>(string type, string? format = null)CreateSchema<TValue>(string type, string? format, TValue? defaultValue = null)CreateSchema(IEnumerable<string> values, string? defaultValue = null)CreateSchema<TEnum>(TEnum? defaultValue = null)
.NET 10
CreateStringSchema(string? defaultValue = null)CreateSchema<TValue>(JsonSchemaType type, string? format = null)CreateSchema<TValue>(JsonSchemaType type, string? format, TValue? defaultValue = null)CreateSchema(IEnumerable<string> values, string? defaultValue = null)CreateSchema<TEnum>(TEnum? defaultValue = null)
What they do in practice
CreateStringSchema()creates a simple string schema, optionally with a default.CreateSchema(..., format)creates a schema with explicit type and format.CreateSchema(..., defaultValue)also adds a default to the contract.CreateSchema(IEnumerable<string> values, ...)turns a list of values into a string-based OpenAPI enum.CreateSchema<TEnum>(...)builds an enum schema directly from a CLR enum.
Example
builder.Services.AddOpenApi(options =>
{
options.AddAcceptLanguageHeader();
options.AddDefaultProblemDetailsResponse();
options.AddOperationParameters();
options.RemoveServerList();
options.WriteNumberAsString();
options.DescribeAllParametersInCamelCase();
options.AddTimeExamples();
options.UseFullTypeNameSchemaIds();
#if NET9_0
options.EnableEnumSupport();
#endif
#if NET10_0_OR_GREATER
options.UseStrictNumericSchemas();
#endif
});
Example AddOpenApiOperationParameters()
builder.Services.AddOpenApiOperationParameters(parameters =>
{
parameters.Parameters.Add(new OpenApiParameter
{
Name = "X-Correlation-Id",
In = ParameterLocation.Header,
Required = false,
Description = "Identifier used to correlate requests"
});
});
Example OpenApiSchemaHelper
var schema = OpenApiSchemaHelper.CreateStringSchema("it-IT");
Exception handling
DefaultExceptionHandler
TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) converts unhandled exceptions into a consistent ProblemDetails response.
Main behavior:
- uses the
StatusCodefromBadHttpRequestExceptionwhen present - sets
title,detail,instance, andtraceId - adds
innerExceptionwhen an inner exception exists - includes
stackTraceonly in the Development environment
Example
builder.Services.AddDefaultExceptionHandler();
app.UseExceptionHandler();
If a route throws an exception, the response becomes predictable and easy for clients to document and consume.
Quick examples
Minimal API with ProblemDetails and OpenAPI
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDefaultProblemDetails();
builder.Services.AddDefaultExceptionHandler();
builder.Services.AddRequestLocalization("it-IT", "en-US");
builder.Services.AddOpenApi(options =>
{
options.AddAcceptLanguageHeader();
options.AddDefaultProblemDetailsResponse();
options.UseFullTypeNameSchemaIds();
});
var app = builder.Build();
app.UseExceptionHandler();
app.UseRequestRewind();
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello" }))
.ProducesDefaultProblem(StatusCodes.Status500InternalServerError);
app.Run();
Reading a configuration section
var appSettings = builder.Services.ConfigureAndGet<AppSettings>(builder.Configuration, "AppSettings");
Contribute
The project is continuously evolving. Contributions, issues, and pull requests are welcome.
Work on the develop branch, not on master. Pull requests should target develop.
| Product | Versions 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. |
-
net10.0
- Microsoft.AspNetCore.OpenApi (>= 10.0.9)
-
net8.0
- No dependencies.
-
net9.0
- Microsoft.AspNetCore.OpenApi (>= 9.0.17)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (3)
Showing the top 3 popular GitHub repositories that depend on TinyHelpers.AspNetCore:
| Repository | Stars |
|---|---|
|
marcominerva/ChatGptNet
A ChatGPT integration library for .NET, supporting both OpenAI and Azure OpenAI Service
|
|
|
marcominerva/SqlDatabaseVectorSearch
A Blazor Web App and Minimal API for performing RAG (Retrieval Augmented Generation) and vector search using the native VECTOR type in Azure SQL Database and Azure OpenAI.
|
|
|
marcominerva/MinimalHelpers
A collection of helpers libraries for Minimal API projects.
|
| Version | Downloads | Last Updated |
|---|---|---|
| 4.2.12 | 174 | 6/11/2026 |
| 4.2.3 | 295 | 5/22/2026 |
| 4.1.21 | 725 | 4/15/2026 |
| 4.1.19 | 652 | 3/11/2026 |
| 4.1.18 | 492 | 2/19/2026 |
| 4.1.17 | 322 | 2/11/2026 |
| 4.1.15 | 1,237 | 1/14/2026 |
| 4.1.12 | 815 | 12/10/2025 |
| 4.1.10 | 816 | 11/13/2025 |
| 4.1.8 | 686 | 10/15/2025 |
| 4.1.6 | 2,529 | 9/10/2025 |
| 4.1.4 | 363 | 9/1/2025 |
| 4.1.3 | 490 | 7/30/2025 |
| 4.1.2 | 243 | 7/28/2025 |