MitMediator.AutoApi 9.0.0-alfa-7

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

Build and Test NuGet .NET 9.0 License

MitMediator.AutoApi

Minimal API registration and http client for MitMediator

πŸš€ Installation

1. Add package

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

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

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

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

πŸ”§ Transformation Logic

HTTP Method is inferred from the leading verb in the type 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

Resource Name (main tag) is derived from the first noun after the verb, and pluralized:

  • Book β†’ /books
  • Author β†’ /authors

Route suffix includes any remaining parts of the type name, converted to lowercase and kebab-case:

  • Count β†’ /count
  • WithAuthor β†’ /with-author

Custom suffix will be added as is

Suffixes Command, Query, and Request are automatically removed from the end

Version prefix (v1, v2, etc.) is prepended to the route as part of the base path

Default version is v1

πŸ§ͺ Examples

Request name Endpoint
GetBookCountQuery GET /v1/books/count
CreateBookCommand POST /v1/book
RemoveBookWithAuthorCommand DELETE /v1/books/with-author
UpdateAuthorBioRequest PUT /v1/authors/bio

GET endpoint with query params

// 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; }
}

POST endpoint with 201 response

// 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; }
}

If you need to use rout parameter, implement one of interfaces IKeyRequest<TKey>, IKeyRequest<TKey1,TKey2>, etc. (max 7 keys)

GET endpoint with suffix and key in url

// 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;
}

πŸ› οΈ Change default mapping

Use attributes for the request type to change default mapping

Supported attributes:

Attribute Template Result for GetBookRequest
TagAttribute [Tag("CustomTag")] GET v1/custom-tag
VersionAttribute [Version("v2")] GET v2/books
SuffixAttribute [Suffix("cover")] GET v1/books/cover
PatternAttribute [Pattern("api/v3/books/{key1}/page/{key2}")] GET 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 🚫

When [PatternAttribute] is applied, it overrides the entire route. The base route, version attribute, and tag attribute will be ignored

Examples

GET endpoint with custom tag and custom suffix

// 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

// Custom URL pattern overrides base route
// URL: 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;
}

GET endpoint png file ("image/png")

// Api URL: GET /v1/books/123/сover
[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 a request implements IFileRequest or inherits from FileRequest, it will be bound using [FromForm]. File parameters fron IFileRequest will be automatically inferred via IFromFile. Apply [DisableAntiforgery] to skip CSRF protection for file upload endpoints or non-browser clients

Examples

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

// Api URL: GET /v1/books/123/text
[DisableAntiforgery]
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

// Api URL: POST /v1/documents/word
public class ImportDocumentWordCommand : FileRequest, IRequest<FileResponse>
{
}

πŸ”’ 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()

πŸ“ Location header

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

🎯 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

πŸ“œ MIT License

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-7 88 9/4/2025

9.0.0-alfa-7
Fix multi keys requests