MitMediator.AutoApi.HttpMediator 9.0.0-alfa-6

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

Build and Test NuGet .NET 9.0 License

MitMediator.AutoApi

Minimal API registration and http client for MitMediator

πŸ”— Extension for MitMediator

πŸš€ Installation

1. Add package

# for ASP.NET API projects
dotnet add package MitMediator.AutoApi -v 9.0.0-alfa-6

# for application layer
dotnet add package MitMediator.AutoApi.Abstractions -v 9.0.0-alfa-6

# for client application (MAUI, Blazor, UWP, etc.)
dotnet add package MitMediator.AutoApi.HttpMediator -v 9.0.0-alfa-6

2. Use extension for IEndpointRouteBuilder

var builder = WebApplication.CreateBuilder(args);
// Register handlers and IMediator.
builder.Services.AddMitMediator(); 

var app = builder.Build();

// Automatically maps endpoints based on IRequest
app.UseAutoApi(); 

app.Run();

To use base path "api" and select assemblies to scan: app.UseAutoApi("api", new []{typeof(GetQuery).Assembly});

3. Done! All public requests have endpoints

βš™οΈ How It Works

  1. Scans all loaded assemblies for IRequest types
  2. Dynamically generates and registers endpoints for each match
  3. Maps routes using MapPost, MapGet, MapPut, and MapDelete, internally calling IMediator.SendAsync

The HTTP method type is determined automatically according to the request name:

Request name start with HTTP Method
get GET
load GET
download GET
fetch GET
update PUT
change PUT
edit PUT
modify PUT
put PUT
post POST
import POST
upload POST
add POST (201 response)
create POST (201 response)
delete DELETE
remove DELETE
drop DELETE

The first word after the method will be the main tag. Second and other - suffix. For example:

GetBookCountQuery - Get - http method GET, Book - main tag (books in url), Count - suffix (/count in url)

RemoveBookWithAuthorCommand - Remove - http method DELETE, Book - main tag (books in url), WithAuthor - suffix ( /with-author in url)

Words command, query, and request in the end of request type name will be deleted from url

If you need to change the generated method, use attributes for the request type

πŸ§ͺ Examples

GET endpoint

// Get - http method
// Books - main tag
// Api URL: GET /v1/books?limit=1&offset=1&freeText=clara
public struct GetBooksQuery : IRequest<Book[]>
{
    public int? Limit { get; init; }
    
    public int? Offset { get; init; }
    
    public string? FreeText { get; init; }
}

GET endpoint with suffix and key in url

// Use IKeyRequest<> to set and get key from ur.
// Get - http method
// Book - main tag (books in url)
// Api URL: GET /v1/books/123/cover
public struct GetBookCoverQuery : IRequest<Book>, IKeyRequest<int>
{
    internal int BookId { get; private set; }

    public void SetKey(int key)
    {
        BookId = key;
    }
    
    public int GetKey() => BookId;
}

POST endpoint with 201 response

// Create - POST http method, return 201
// Book - main tag (books in url)
// Api URL: POST /v1/books
public class CreateBookCommand : IRequest<Book>
{
    public string Title { get; init; }
    
    public int AuthorId { get; init; }

    public string GenreName { get; init; }
}

PUT endpoint with key in url

// Update - PUT http method
// Book - main tag (books in url)
// Api URL: PUT /v1/books/123
public class UpdateBookCommand : IRequest<Book>, IKeyRequest<int>
{
    internal int BookId { get; private set; }
    
    public string Title { get; init; }
    
    public int AuthorId { get; init; }

    public string GenreName { get; init; }

    public void SetKey(int key)
    {
        BookId = key;
    }
    
    public int GetKey() => BookId;
}

GET endpoint text file ("application/octet-stream")

// Get - GET http method
// Book - main tag (books in url)
// Text - action suffix
// Api URL: GET /v1/books/123/text
public class GetBookTextQuery: IRequest<byte[]>, IKeyRequest<int>
{
    internal int BookId { get; private set; }
    
    public void SetKey(int key)
    {
        BookId = key;
    }
    
    public int GetKey() => BookId;
}

POST upload and download file with file name

// Import - POST http method
// Document - main tag (documents in url)
// Word - action suffix
// Api URL: POST /v1/documents/word
public class ImportDocumentWordCommand : FileRequest, IRequest<FileResponse>
{
}

Change default mapping

Use attributes for the request type to change default mapping

Supported attributes:

Attribute Template Result
TagAttribute [Tag("CustomTag")] v1/custom-tag
VersionAttribute [Version("v2")] v2/books
SuffixAttribute [Suffix("cover")] v1/books/cover
PatternAttribute [Pattern("api/v3/books/{key1}/page/{key2}")] api/v3/books/{key1}/page/{key2}
MethodAttribute [Method(MethodType.Post)] POST v1/books
DescriptionAttribute [Description("My custom description")] See description (in xml doc or swagger)
DisableAntiforgeryAttribute [DisableAntiforgery] Antiforgery protection has been disabled for the endpoint
ResponseContentTypeAttribute [ResponseContentType("image/png")] Result will have selected content type
IgnoreRequestAttribute [IgnoreRequest] Request will be ignored 🚫

GET endpoint with custom tag and custom suffix

// Get - http method
// Books - main tag (books in url)
// Api URL: GET v1/my-books/favorite?limit=1&offset=1&freeText=clara
[Tag("MyBooks")]
[Suffix("favorite")]
public struct GetBooksQuery : IRequest<Book[]>
{
    public int? Limit { get; init; }
    
    public int? Offset { get; init; }
    
    public string? FreeText { get; init; }
}

GET endpoint with version, custom tag, URL pattern and specified HTTP method

// Auto-generated DELETE endpoint.
// Base tag: "books"
// API version: "v3"
// Custom URL pattern overrides base route: DELETE with-keys/{key1}/field/{key2}
[Tag("books")]
[Pattern("with-keys/{key1}/field/{key2}")]
[Version("v3")]
[Method(MethodType.Delete)]
public class DoSomeWithBookAndDeleteCommand : IRequest<Book[]>, IKeyRequest<int, Guid>
{
    internal int BookId { get; private set; }
    
    internal Guid GuidId { get; private set; }

    public void SetKey1(int key)
    {
        BookId = key;
    }
    
    public int GetKey1() => BookId;
    
    public void SetKey2(Guid key)
    {
        GuidId = key;
    }
    
    public Guid GetKey2() => GuidId;
}

Default version is v1

GET endpoint png file ("image/png")

// Get - GET http method
// Book - main tag (books in url)
// Cover - action suffix
// Api URL: GET /v1/books/сover/123
[ResponseContentType("image/png")]
public class GetBookCoverQuery: IRequest<byte[]>, IKeyRequest<int>
{
    internal int BookId { get; private set; }
    
    public void SetKey(int key)
    {
        BookId = key;
    }
    
    public int GetKey() => BookId;
}

πŸ“„ Upload and download files

For requests implementing IRequest<byte[]>/IRequest<Stream>, or IRequest<FileResponse>/ IRequest<FileStreamResponse>, the response will use "application/octet-stream" by default. To specify a download file name, use the FileResponse or FileStreamResponse response. Use [ResponseContentType("image/png")] attribute for custom content type

When requests implementing IFileRequest or inheriting from FileRequest, the request will be bound using [FromForm], and file parameters will be inferred via IFromFile by default

πŸ”’ X-Total-Count Header

To include the X-Total-Count header in the HTTP response, implement the ITotalCount interface in your response type. This is useful for paginated endpoints or any scenario where the client needs to know the total number of items available.

public class GetBooksResponse : ITotalCount
{
    public Book[] Items { get; init; }

    private int _totalCount;
    
    public int GetTotalCount() => _totalCount;

    public void SetTotalCount(int totalCount)
    {
        _totalCount = totalCount;
    }
}

When this interface is implemented, MitMediator.AutoApi will automatically include the X-Total-Count header in the response, reflecting the value returned by GetTotalCount()

πŸ“ Resource ID in Location header for 201 Created responses

To insert the correct ID into the Location header of a 201 Created response, implement the IResourceKey interface in your response type:

// Api URL: POST /books
public class CreateBookCommand : IRequest<CreatedBookResponse>
{
    public string Title { get; init; }
}

// Location Header: /books/{BookId}
public class CreatedBookResponse : IResourceKey
{
    public int BookId { get; private set; }
    
    public string Title { get; private set; }
    
    public string GetResourceKey() => BookId.ToString();
}

If you do not implement the IResourceKey interface, the Location header will default to the format /books/{key}, where {key} is a placeholder string

🎯 Use auto client: HttpMediator

You can reuse your IRequest types to seamlessly send HTTP requests to a server-side API using HttpMediator

HttpMediator supports IClientPipelineBehavior<TRequest, TResponse> for middleware-like extensibility, and IHttpHeaderInjector<TRequest, TResponse> for injecting custom headers per request

πŸ”§ Sample usage:

var mediator = new HttpMediator(serviceProvider, baseUrl: "https://api.example.com");
var response = await mediator.SendAsync<MyRequest, MyResponse>(new MyRequest(), cancellationToken);

πŸ”§ More

var baseApiUrl = "api";
var httpClientName = "baseHttpClient";
var serviceCollection = new ServiceCollection();
serviceCollection.AddHttpClient(httpClientName, client => { client.BaseAddress = new Uri("https://localhost:7127/"); });
serviceCollection.AddScoped(typeof(IHttpHeaderInjector<,>), typeof(AuthorizationHeaderInjection<,>));
serviceCollection.AddScoped<IClientMediator, HttpMediator>(c => new HttpMediator(c, baseApiUrl, httpClientName));

var provider = serviceCollection.BuildServiceProvider();
var httpMediator = provider.GetRequiredService<IClientMediator>();

var query = new GetBookQuery();
query.SetKey(12);
var data = await httpMediator.SendAsync<GetBookQuery, Book>(query, CancellationToken.None);

public class AuthorizationHeaderInjection<TRequest, TResponse> : IHttpHeaderInjector<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
    public ValueTask<(string, string)?> GetHeadersAsync(CancellationToken cancellationToken)
    {
        var result = ("Authorization", "Bearer token");
        return ValueTask.FromResult<(string, string)?>(result);
    }
}

πŸ“ See samples

πŸ’‘ Features

  • Declarative routing via request class name or attributes
  • Automatic tag and version grouping
  • Built-in handling of Unit results (no payload responses)
  • Custom route patterns with up to 7 composite keys
  • Key binding via IKeyRequest<TKey1, TKey2, ...> (pattern {key1}/{key2}/.../{key7})
  • Supports custom patterns (custom-route/my-route, with-keys/{key1}/{key2}/field/{key3})
  • Auto client HttpMediator for clients applications
  • File response (IRequest<byte[]> or IRequest<FileResponse>)
  • Upload file stream: public class UploadCoverCommand: FileRequest {}
  • X-Total-Count header (implement ITotalCount in response type)

πŸ“œ License

MIT

Product Compatible and additional computed target framework versions.
.NET 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 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
9.0.0-alfa-6 0 9/3/2025
9.0.0-alfa-5 0 9/3/2025
9.0.0-alfa-4 145 8/29/2025
7.0.0-alfa-8 209 8/6/2025

9.0.0-alfa-6
Fix IClientPipelineBehavior