Linger.Results.AspNetCore 2.0.0-preview.1

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

Linger.Results.AspNetCore

Breaking changes and 2.0 migration notes are documented in the Linger migration guide.

Linger.Results.AspNetCore provides extension methods that seamlessly integrate the Linger.Results library with ASP.NET Core framework, allowing API controllers to easily return results in a unified format.

Features

  • Elegantly convert Result and Result<T> objects to ASP.NET Core ActionResult
  • Support for Minimal API - convert Result and Result<T> to IResult using modern Results static class
  • Automatically select appropriate HTTP status codes based on result status
  • Support for custom success and failure status codes
  • Provide RFC 7807 standard ProblemDetails format output

Supported Framework Versions

  • .NET 8.0+

Installation

dotnet add package Linger.Results.AspNetCore

Basic Usage

Using in Controllers

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    [HttpGet("{id}")]
    public async Task<ActionResult<UserDto>> GetUser(int id)
    {
        var result = await _userService.GetUserByIdAsync(id);
        return result.ToActionResult(); // Success returns UserDto, failure returns ProblemDetails
    }

    [HttpPost]
    public async Task<ActionResult<UserDto>> CreateUser(CreateUserRequest request)
    {
        var result = await _userService.CreateUserAsync(request);
        return result.ToActionResult(HttpStatusCode.Created);
    }

    [HttpDelete("{id}")]
    public async Task<ActionResult> DeleteUser(int id)
    {
        var result = await _userService.DeleteUserAsync(id);
        return result.ToActionResult(HttpStatusCode.NoContent); // Success returns 204, failure returns ProblemDetails
    }

    // Compatible with methods returning IActionResult type signature
    // This only applies to non-generic Result; Result<T> should use ActionResult<T>
    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateUser(int id, UpdateUserRequest request)
    {
        var result = await _userService.UpdateUserAsync(id, request);
      return result.ToActionResult(); // Non-generic Result can be returned directly as IActionResult
    }
}

Using in Minimal API

var app = WebApplication.Create();

app.MapGet("/api/users/{id}", async (int id, IUserService userService) =>
{
    var result = await userService.GetUserByIdAsync(id);
    return result.ToHttpResult(); // Automatic status code mapping
});

app.MapPost("/api/users", async (CreateUserRequest request, IUserService userService) =>
{
    var result = await userService.CreateUserAsync(request);
    // Check IsSuccess because accessing result.Value requires the result to be successful
    return result.IsSuccess
      ? result.ToCreatedResult($"/api/users/{result.Value.Id}")
      : result.ToHttpResult(); // Failure returns ProblemDetails automatically
});

app.MapDelete("/api/users/{id}", async (int id, IUserService userService) =>
{
    var result = await userService.DeleteUserAsync(id);
    return result.ToNoContentResult(); // Success returns 204 No Content
});

API Method Comparison

Scenario Controller API Minimal API
Auto status codes result.ToActionResult() result.ToHttpResult()
Custom success code result.ToActionResult(successStatusCode: HttpStatusCode.Created) result.ToHttpResult(successStatusCode: HttpStatusCode.Created)
Custom both codes result.ToActionResult(HttpStatusCode.Created, HttpStatusCode.Conflict) result.ToHttpResult(HttpStatusCode.Created, HttpStatusCode.Conflict)
Specific created response result.ToActionResult(HttpStatusCode.Created) result.ToCreatedResult("/api/users/123")
No content response result.ToActionResult(HttpStatusCode.NoContent) result.ToNoContentResult()

When to use

  • Use ToActionResult() / ToHttpResult() for most standard CRUD operations (default auto-mapping handles status codes correctly)
  • Use ToCreatedResult() for POST endpoints to return 201 Created with location header
  • Use ToNoContentResult() for DELETE/PUT endpoints to return 204 No Content on success

ToActionResult() and ToHttpResult() both use ProblemDetails for failures. A successful non-generic Result returns only the status code with an empty body; a successful Result<T> returns its Value.

Status Code Parameters:

  • ToActionResult() / ToActionResult<T>() / ToHttpResult() / ToHttpResult<T>(): Both successStatusCode and failureStatusCode are optional. When not specified, successStatusCode defaults to 200 OK; failureStatusCode is auto-determined by Result.Status (e.g., NotFound → 404, others → 400)

Response Format Examples

Success Responses

// Result<UserDto> success
{
  "id": 123,
  "name": "John Doe",
  "email": "john.doe@example.com"
}

// Result success has an empty response body

Failure Responses

// ToActionResult() and ToHttpResult() use the same format
{
  "type": null,
  "title": "One or more validation errors occurred",
  "status": 400,
  "detail": "Please refer to the errors property for additional details.",
  "errors": {
    "User.InvalidEmail": ["The email format is invalid"],
    "User.WeakPassword": ["The password is too weak"]
  }
}

See detailed request/response mapping and error contract in REQUEST_RESPONSE_MAPPING.zh-CN.md.

Short summary: errors values are arrays of strings per field; CreateProblemDetails groups Error items by Code and writes string[] into errors. The client will prioritize detail for the global message, otherwise the first errors entry. Note: errors is an RFC 7807 extension member and uses string arrays (e.g. {"Field": ["msg1", "msg2"]}) so a single field can carry multiple validation messages. Errors without a code use an empty-string key and are not discarded. The server adds it via ProblemDetails.Extensions["errors"]; ASP.NET Core serializes Extensions entries as top-level properties, so you will see a top-level errors field.

Client mapping notes:

  • The library exposes ProblemDetails with errors where each value is an array of strings. CreateProblemDetails groups Error items by Code and sets each group's messages as string[] in errors.
  • Consumers should expect errors values to be arrays and handle multiple messages per field. Clients receive errors that map to ApiResult.Errors, where each array element becomes an Error item (Error.Code = field, Error.Message = single message).

Status Code Mapping

  • Result.Success() → 200 OK
  • Result.NotFound() → 404 Not Found
  • Result.Failure() → 400 Bad Request

Best Practices

  1. Separation of Concerns: Service layer returns Result, controller handles HTTP response conversion
  2. Consistent API Responses: Maintain unified error formats across the application
  3. Use ProblemDetails: Prefer RFC 7807 format for client-facing APIs
  4. Choose Right API Style: Use Controllers for complex scenarios, Minimal API for simple cases

Design Decision: Why No IActionResult Conversion Methods?

ActionResult and ActionResult<T> are the recommended modern approach, and we intentionally do not provide IActionResult conversion methods for the following reasons:

  • Type Safety: ActionResult<T> is generic and preserves the complete type information of return values, facilitating framework features like OpenAPI/Swagger generation and type checking. IActionResult is a non-generic interface that loses generic type information, hindering tool chain support.

  • Seamless Compatibility: Non-generic Result converts directly to ActionResult, so when your method signature needs to return IActionResult you can return ToActionResult() directly; Result<T> should use ActionResult<T> as the return type.

  • Consistency: We follow ASP.NET Core official best practices by prioritizing the use of generic ActionResult<T> over non-generic IActionResult.

Summary: If your method return type is IActionResult, you can directly use ToActionResult() for non-generic Result; if you are returning Result<T>, change the method signature to ActionResult<T>.

Using with Linger.Results

This library is an extension of Linger.Results. Please also refer to the Linger.Results documentation to learn more about result object functionality.

Product 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. 
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-preview.1 49 8/29/2026
1.6.4 96 8/16/2026
1.6.3 107 8/5/2026
1.6.2 120 8/2/2026
1.6.0 114 7/25/2026
1.5.5 113 7/23/2026
1.5.4-preview 85 7/21/2026
1.5.3-preview 103 7/20/2026
1.5.2-preview 101 7/19/2026
1.5.1-preview 109 7/15/2026
1.5.0-preview 93 7/14/2026
1.4.4-preview 111 6/16/2026
1.4.3-preview 102 6/15/2026
1.4.2 120 5/20/2026
1.4.1-preview 109 5/12/2026
1.4.0 115 5/6/2026
1.3.3-preview 90 5/5/2026
1.3.2-preview 112 4/29/2026
1.3.1-preview 108 4/28/2026
1.3.0-preview 107 4/27/2026
Loading failed