FluentResults.HttpMapping 2.0.0

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

FluentResults.HttpMapping

FluentResults.HttpMapping is a small, opinionated library that let's you map FluentResults Result and Result<T> to HTTP responses declaratively, consistently, and without leaking HTTP concerns into your domain layer.

The problem this package solves

When using FluentResults in Web APIs, a common problem quickly appears:

  • Domain code returns rich Result objects;
  • HTTP endpoints must translate those results into status codes, headers, and bodies;
  • Mapping logic ends up:
    • Repeated across endpoints;
    • Tightly coupled to ASP.NET;
    • Hard to evolve consistently;

Typical symptoms:

  • if (result.IsFailed) blocks everywhere;
  • Ad-hoc mapping of errors to status codes;
  • Validation, authorization, and infrastructure errors mixed together;
  • Inconsistent error responses across endpoints;

FluentResults.HttpMapping solves this by introducing a centralized mapping layer:

  • Endpoints return Result / Result<T>;
  • A rule-based DSL decides how results become HTTP responses;
  • HTTP behavior is defined once, globally, and reused everywhere;

Design philosophy

This package is intentionally small and strict.

Core principles:

  • Separation of concerns
    • Domain logic knows nothing about HTTP;
    • HTTP mapping lives in one place;
  • Declarative rules
    • You describe what should happen, not how;
  • First match wins
    • Rules are evaluated in order;
  • Pure mapping
    • No dependency injection inside rules;
    • No side effects;
  • Minimal API first
    • Designed for IResult and Results.*;
    • Optional support for MVC controllers;

This is not a framework. It’s a thin, predictable mapping layer.

Installation

dotnet add package FluentResults.HttpMapping

Basic usage

1. Register the mapper

Configure your mapping rules once at startup:

builder.Services.AddHttpResultMapping(mapper =>
{
    mapper
        .WhenFailure()
        .Problem(p => p
            .WithStatus(System.Net.HttpStatusCode.InternalServerError)
            .WithTitle("Unexpected failure"));
});

2. Inject and use in endpoints

Endpoints simply return Result objects:

app.MapGet("/example", ([FromServices] IHttpResultMapper mapper) =>
{
    return mapper.Map(Result.Ok("Hello world"));
});

No if, no branching, no HTTP logic in the endpoint.

Rule-based mapping

Rules are evaluated in order. The first rule that matches produces the HTTP response and no further rules are evaluated. Because of that, more specific rules must come before more generic ones.

First match wins!

Matching by reason type

mapper
    .WhenReason<NotFoundError>()
    .Map(_ => Results.StatusCode(StatusCodes.Status404NotFound));

Mapping to RFC 7807 Problem Details

mapper
    .WhenReason<ValidationError>()
    .Problem(p => p.WithValidationProblem<ValidationError>(
        e => e.Errors
    ));

Produces a standard Problem Details response with validation errors.

Adding headers

Headers are defined parallel to mapping, not inside the body logic:

mapper
    .WhenReason<SecurityError>()
    .WithHeader("WWW-Authenticate", "Bearer")
    .Map(ctx => Results.Json(
        new
        {
            error = "invalid_token",
            error_description = ctx.Result.FirstReason<SecurityError>().Message
        }
    ));

Matching by metadata

Rules can match reasons by metadata keys or values:

mapper
    .WhenReasonWithMetadata<Error>("error-codes")
    .Problem(p => p
        .WithStatus(System.Net.HttpStatusCode.InternalServerError)
        .WithTitle("An internal server error occurred.")
        .WithDetail(ctx =>
            ctx.Result.FirstReasonWithMetadata<Error>("error-codes").Message)
        .WithExtension("error-codes", ctx =>
            ctx.Result.GetMetadata("error-codes"))
    );

Fallback rule

Always define a fallback failure rule last:

mapper
    .WhenFailure()
    .Problem(p => p
        .WithStatus(System.Net.HttpStatusCode.InternalServerError)
        .WithTitle("Unexpected failure"));

Success mapping

Success handling is built in:

  • Result<T>200 OK with body
  • Result204 No Content

You can override this behavior by defining your own success rules:

mapper
    .WhenSuccess()
    .Map(_ => Results.Ok());

Example Program.cs

A complete example configuration:

builder.Services.AddHttpResultMapping(mapper =>
{
    mapper
        .WhenReason<ValidationError>()
        .Problem(p => p.WithValidationProblem<ValidationError>(e => e.Errors));

    mapper
        .WhenReason<SecurityError>()
        .WithHeader("WWW-Authenticate", "Bearer")
        .Map(ctx => Results.Json(
            new
            {
                error = "invalid_token",
                error_description = ctx.Result.FirstReason<SecurityError>().Message
            }
        ));

    mapper
        .WhenReason<NotFoundError>()
        .Map(_ => Results.StatusCode(StatusCodes.Status404NotFound));

    mapper
        .WhenReasonWithMetadata<Error>("error-codes")
        .Problem(p => p
            .WithStatus(System.Net.HttpStatusCode.InternalServerError)
            .WithTitle("An internal server error occurred.")
            .WithDetail(ctx =>
                ctx.Result.FirstReasonWithMetadata<Error>("error-codes").Message)
            .WithExtension("error-codes", ctx =>
                ctx.Result.GetMetadata("error-codes"))
        );

    mapper
        .WhenFailure()
        .Problem(p => p
            .WithStatus(System.Net.HttpStatusCode.InternalServerError)
            .WithTitle("Unexpected failure"));
});

Using with MVC Controllers

Although this package is primarily designed for Minimal APIs, it can also be used in ASP.NET Core applications that rely on MVC controllers.

The HTTP mapping system always produces an ASP.NET IResult. To return that result from a controller action, you can adapt it to an IActionResult using the provided MVC adapter.

Example

using FluentResults;
using FluentResults.HttpMapping.Execution;
using FluentResults.HttpMapping.MVC;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("example")]
public class ExampleController : ControllerBase
{
    private readonly IHttpResultMapper _mapper;

    public ExampleController(IHttpResultMapper mapper)
    {
        _mapper = mapper;
    }

    [HttpGet("success")]
    public IActionResult Success()
    {
        var result = Result.Ok(new { Message = "Hello from MVC" });
        return _mapper.Map(result).ToActionResult();
    }

    [HttpGet("failure")]
    public IActionResult Failure()
    {
        var result = Result.Fail("Something went wrong");
        return _mapper.Map(result).ToActionResult();
    }
}

Why this works

  • The HTTP mapping rules are framework-agnostic
  • Mapping always results in an IResult
  • MVC support is provided via a thin adapter, not a separate mapping system

This ensures the same rules and behavior are shared between Minimal APIs and MVC controllers without duplication or special configuration.

What this package is not

  • ❌ Not an exception handler;
  • ❌ Not a middleware replacement;
  • ❌ Not a validation framework;
  • ❌ Not tied to MVC controllers;

It is a mapping layer — nothing more, nothing less.

Summary

FluentResults.HttpMapping gives you:

  • Clean endpoints;
  • Centralized HTTP behavior;
  • Predictable, testable rules;
  • Zero HTTP concerns in your domain layer;

If you already use FluentResults, this package lets your HTTP layer finally match the same level of clarity.

Supporting

If this project helped you, you can support it:

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

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
2.0.0 114 2/20/2026
1.0.0 135 2/1/2026

- Moved helper methods for defining rules based on the state of the `Result` from the context to it's `Result`.
- Replaced error-based with reason-based rule definition.