CleanResult.Swashbuckle 1.4.2

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

CleanResult.Swashbuckle

CleanResult

<div align="center">

NuGet License: MIT .NET

Build Testing

Swagger/OpenAPI integration for CleanResult

Clean OpenAPI documentation by automatically unwrapping Result types

Main Documentation โ€ข Features โ€ข Usage โ€ข Examples

</div>


๐Ÿ“ฆ Installation

dotnet add package CleanResult.Swashbuckle

Requirements:

  • .NET 8.0, 9.0, or 10.0
  • CleanResult 1.2.8+
  • Swashbuckle.AspNetCore 6.0+

โœจ Features

  • ๐ŸŽฏ Automatic Unwrapping - Removes Result<T> wrapper from OpenAPI schemas
  • ๐Ÿ“„ Clean Documentation - Shows actual return types in Swagger UI
  • ๐Ÿ”„ HTTP Status Mapping - Correctly maps Result.Ok() to 204, Result<T>.Ok() to 200
  • ๐Ÿงน Schema Cleanup - Removes Result wrapper schemas from definitions
  • โšก Zero Configuration - Works automatically after registration

๐Ÿš€ Usage

Registration

Add the CleanResult filters to your Swagger configuration:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSwaggerGen(options =>
{
    // โœ… Add CleanResult filters - automatically unwraps Result types
    options.AddCleanResultFilters();

    // Your other Swagger configuration...
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "My API",
        Version = "v1"
    });
});

var app = builder.Build();

app.UseSwagger();
app.UseSwaggerUI();

app.Run();

What It Does

The filters perform three key transformations:

  1. Operation Filter - Unwraps Result types in endpoint responses
  2. Schema Filter - Marks Result wrapper schemas for deletion
  3. Document Filter - Removes marked schemas from final OpenAPI document

๐Ÿ’ก Examples

Before Integration

Without CleanResult.Swashbuckle, your Swagger documentation shows the Result wrapper:

paths:
  /api/users/{id}:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResultOfUser'

components:
  schemas:
    ResultOfUser:
      type: object
      properties:
        success: { type: boolean }
        successValue: { $ref: '#/components/schemas/User' }
        internalErrorValue: { $ref: '#/components/schemas/Error' }

After Integration

With CleanResult.Swashbuckle, documentation is clean and shows actual types:

paths:
  /api/users/{id}:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

components:
  schemas:
    User:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        email: { type: string }

Controller Examples

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    // โœ… Swagger shows: 200 OK โ†’ User schema
    [HttpGet("{id}")]
    public Result<User> GetUser(int id)
    {
        return Result.Ok(new User { Id = id, Name = "John" });
    }

    // โœ… Swagger shows: 204 No Content
    [HttpPost]
    public Result CreateUser([FromBody] CreateUserDto dto)
    {
        return Result.Ok();
    }

    // โœ… Swagger shows: 200 OK โ†’ List<User> schema
    [HttpGet]
    public Result<List<User>> GetUsers()
    {
        return Result.Ok(new List<User>());
    }

    // โœ… Swagger shows: 200 OK โ†’ PagedResult<User> schema
    [HttpGet("paged")]
    public Result<PagedResult<User>> GetPagedUsers([FromQuery] int page = 1)
    {
        return Result.Ok(new PagedResult<User>());
    }
}

Response Status Mapping

The filter correctly maps Result types to HTTP status codes:

Method Return Type Swagger Status Content-Type Schema
Result 204 No Content - None
Result<User> 200 OK application/json User
Result<string> 200 OK text/plain string
Result<byte[]> 200 OK application/octet-stream binary
Task<Result> 204 No Content - None
Task<Result<T>> 200 OK varies T

๐Ÿ”ง How It Works

Three-Phase Filtering

1. Operation Filter (CleanResultReturnTypeFilter)

// Examines controller method return types
// For Result: Changes 200 โ†’ 204 (No Content)
// For Result<T>: Keeps 200 but replaces schema with T's schema
// Unwraps from Task<Result> or Task<Result<T>>

2. Schema Filter (CleanResultSchemaFilter)

// Marks Result type schemas with special title: "SchemaToDelete"
// Prevents Result wrapper types from appearing in schema definitions

3. Document Filter (CleanResultReturnDocumentFilter)

// Final cleanup phase
// Removes all schemas marked "SchemaToDelete"
// Ensures clean OpenAPI document

Architecture

Swagger Generation
        โ†“
Operation Filter
  (unwrap types)
        โ†“
Schema Filter
  (mark wrappers)
        โ†“
Document Filter
  (remove marked)
        โ†“
Clean OpenAPI Doc

๐ŸŽฏ Best Practices

โœ… Do's

// โœ… Use Result types directly
[HttpGet("{id}")]
public Result<User> GetUser(int id)
    => _service.GetById(id);

// โœ… Works with async
[HttpGet("{id}")]
public async Task<Result<User>> GetUserAsync(int id)
    => await _service.GetByIdAsync(id);

// โœ… Complex types are unwrapped correctly
[HttpGet]
public Result<PagedList<UserDto>> GetPagedUsers([FromQuery] PagingParams params)
    => _service.GetPaged(params);

โŒ Don'ts

// โŒ Don't wrap Result in IActionResult
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
    var result = _service.GetById(id);
    return Ok(result);  // Bad: Swagger won't unwrap
}

// โŒ Don't return Result as object
[HttpGet("{id}")]
public object GetUser(int id)
    => _service.GetById(id);  // Bad: type information lost

๐Ÿ“– Advanced Configuration

Custom Swagger Options

builder.Services.AddSwaggerGen(options =>
{
    // โœ… Add CleanResult filters first
    options.AddCleanResultFilters();

    // Then add your custom configuration
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "My API",
        Version = "v1",
        Description = "API with CleanResult integration"
    });

    // Add XML comments
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    options.IncludeXmlComments(xmlPath);

    // Add authorization
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.Http,
        Scheme = "bearer",
        BearerFormat = "JWT"
    });
});

Response Examples

/// <summary>
/// Gets a user by ID
/// </summary>
/// <param name="id">User ID</param>
/// <returns>The user if found</returns>
/// <response code="200">Returns the user</response>
/// <response code="404">User not found</response>
[HttpGet("{id}")]
[ProducesResponseType(typeof(User), 200)]
[ProducesResponseType(typeof(Error), 404)]
public Result<User> GetUser(int id)
{
    var user = _repository.FindById(id);
    if (user == null)
        return Result<User>.Error("User not found", 404);

    return Result.Ok(user);
}


๐Ÿ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


<div align="center">

โฌ† Back to Top โ€ข Main Documentation

</div>

<div align="center"> Gwynbleid85 ยฉ 2025 </div>

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
1.4.2 95 5/4/2026
1.4.1 259 4/17/2026
1.4.0 117 4/16/2026
1.3.4 189 1/5/2026
1.3.3 233 11/25/2025
1.3.2 1,653 11/25/2025
1.3.1 221 11/25/2025
1.3.0 505 11/4/2025
1.2.9 228 10/9/2025
1.2.8 398 10/8/2025
1.2.7 507 8/25/2025
1.2.6 241 8/11/2025
1.2.5 441 7/28/2025
1.2.4 553 7/24/2025
1.2.3 611 7/23/2025
1.2.2 221 7/17/2025
1.2.1 211 7/17/2025