ErrorOr.Http 2.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package ErrorOr.Http --version 2.0.0
                    
NuGet\Install-Package ErrorOr.Http -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="ErrorOr.Http" Version="2.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ErrorOr.Http" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="ErrorOr.Http">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 ErrorOr.Http --version 2.0.0
                    
#r "nuget: ErrorOr.Http, 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 ErrorOr.Http@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=ErrorOr.Http&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=ErrorOr.Http&version=2.0.0
                    
Install as a Cake Tool

ErrorOr.Http

NuGet License

Source generator for ASP.NET Core Minimal APIs that transforms ErrorOr<T> handlers into type-safe endpoints with automatic ProblemDetails responses and OpenAPI metadata.

Features

  • Attribute-driven routing with [Get], [Post], [Put], [Delete], [Patch]
  • Compile-time parameter binding validation prevents runtime errors
  • Automatic error mapping from ErrorOr types to RFC 7807 ProblemDetails
  • OpenAPI metadata inferred from return types and error paths
  • NativeAOT compatible with generated JSON serialization contexts
  • Streaming support for IAsyncEnumerable<T> via Server-Sent Events

Quick Start

dotnet add package ErrorOr.Http
using ErrorOr.Http;

public static class TodoEndpoints
{
    [Get("/todos")]
    public static ErrorOr<Todo[]> GetAll() => Database.Todos;

    [Get("/todos/{id}")]
    public static ErrorOr<Todo> GetById(int id) =>
        Database.Find(id) ?? Error.NotFound();

    [Post("/todos")]
    public static ErrorOr<Created> Create([FromBody] Todo todo)
    {
        Database.Add(todo);
        return Result.Created;
    }
}
using ErrorOr.Http.Generated;

var app = builder.Build();
app.MapErrorOrEndpoints();
app.Run();

Usage

HTTP Method Attributes

[Get("/resources")]
public static ErrorOr<Resource[]> List() => ...;

[Get("/resources/{id}")]
public static ErrorOr<Resource> Get(int id) => ...;

[Post("/resources")]
public static ErrorOr<Created> Create([FromBody] Resource r) => ...;

[Put("/resources/{id}")]
public static ErrorOr<Updated> Update(int id, [FromBody] Resource r) => ...;

[Patch("/resources/{id}")]
public static ErrorOr<Updated> Patch(int id, [FromBody] ResourcePatch p) => ...;

[Delete("/resources/{id}")]
public static ErrorOr<Deleted> Delete(int id) => ...;

For non-standard HTTP methods:

[ErrorOrEndpoint("OPTIONS", "/resources")]
public static ErrorOr<string[]> Options() => new[] { "GET", "POST" };

[ErrorOrEndpoint("HEAD", "/resources/{id}")]
public static ErrorOr<Success> Head(int id) => Result.Success;

Parameter Binding

Route parameters are inferred from the template. All other sources require explicit attributes.

[Get("/users/{id}")]
public static ErrorOr<User> GetUser(
    int id,                                       // Route (matches {id})
    [FromQuery] string? search,                   // Query string
    [FromHeader(Name = "X-Api-Version")] int v,   // Header
    [FromServices] IUserService service)          // DI container
    => ...;

Body Binding

[Post("/users")]
public static ErrorOr<Created> CreateUser([FromBody] CreateUserRequest request)
    => ...;

Form Binding

[Post("/upload")]
public static ErrorOr<Created> Upload(
    [FromForm] string title,
    [FromForm] int version,
    IFormFile document)
    => ...;

[Post("/upload-multiple")]
public static ErrorOr<Created> UploadMany(
    [FromForm] UploadRequest request,
    IFormFileCollection attachments)
    => ...;

public record UploadRequest(string Title, string Description);

Grouped Parameters

[Get("/search")]
public static ErrorOr<SearchResult> Search([AsParameters] SearchRequest request)
    => ...;

public record SearchRequest(
    [FromQuery] string Query,
    [FromQuery] int Page = 1,
    [FromHeader(Name = "X-Api-Key")] string ApiKey,
    [FromServices] ISearchService Service);

Async Handlers

[Get("/users/{id}")]
public static async Task<ErrorOr<User>> GetUser(
    int id,
    [FromServices] IUserRepository repo,
    CancellationToken ct)
    => await repo.GetByIdAsync(id, ct) ?? Error.NotFound();

Streaming (Server-Sent Events)

[Get("/events")]
public static ErrorOr<IAsyncEnumerable<Event>> StreamEvents()
{
    return GetEvents();

    static async IAsyncEnumerable<Event> GetEvents()
    {
        while (true)
        {
            yield return await GetNextEvent();
        }
    }
}

Multiple Routes

[Get("/users/{id}")]
[Get("/users/by-email/{email}")]
public static ErrorOr<User> GetUser(int? id = null, string? email = null)
    => ...;

Error Handling

ErrorOr types map to HTTP status codes automatically:

ErrorOr Type HTTP Status Response Type
Error.Validation() 400 HttpValidationProblemDetails
Error.Unauthorized() 401 ProblemDetails
Error.Forbidden() 403 ProblemDetails
Error.NotFound() 404 ProblemDetails
Error.Conflict() 409 ProblemDetails
Error.Failure() 422 ProblemDetails
Error.Unexpected() 500 ProblemDetails

Success types:

ErrorOr Type HTTP Status
ErrorOr<T> 200
ErrorOr<Created> 201
ErrorOr<Success> 204
ErrorOr<Updated> 204
ErrorOr<Deleted> 204

NativeAOT Support

For AOT publishing, register a JSON serialization context:

[JsonSerializable(typeof(Todo))]
[JsonSerializable(typeof(Todo[]))]
[JsonSerializable(typeof(ProblemDetails))]
[JsonSerializable(typeof(HttpValidationProblemDetails))]
internal partial class AppJsonContext : JsonSerializerContext { }
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.TypeInfoResolver = AppJsonContext.Default;
});

The generator creates ErrorOrJsonContext.suggested.cs in obj/Generated/ with all required types.

Diagnostics

Code Severity Description
EOE003 Error Unsupported parameter type
EOE004 Error Ambiguous parameter requires explicit binding attribute
EOE005 Error Multiple [FromBody] parameters
EOE006 Error Multiple body sources ([FromBody], [FromForm], Stream)
EOE007 Error Multiple [FromForm] DTO parameters
EOE008 Error Unsupported [FromForm] DTO shape
EOE009 Warning Non-nullable IFormFile may be missing at runtime
EOE010 Info Form endpoint may receive non-form requests
EOE011 Error Multiple [FromForm] complex type parameters
EOE013 Error IFormCollection requires explicit [FromForm]
EOE014 Error Type cannot be form-bound
EOE021 Warning Error type not documented in OpenAPI metadata
EOE022 Warning Type not registered in JsonSerializerContext

Requirements

  • .NET 10.0 or later
  • Handlers must be static methods
  • Return type must be ErrorOr<T>, Task<ErrorOr<T>>, or ValueTask<ErrorOr<T>>

License

MIT

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

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 93 1/3/2026 3.0.0 is deprecated because it is no longer maintained.
2.0.0 83 1/2/2026
1.0.0 84 1/2/2026