Skip to content

Latest commit

 

History

History
234 lines (161 loc) · 10 KB

MinimalAPI.md

File metadata and controls

234 lines (161 loc) · 10 KB

CircleDI.MinimalAPI

CircleDI wired up with ASP.NET Core minimal API. The extra Blazor features are

  • EndpointAttribute to register methods as minimal API endpoint.

This package includes all functionalities of CircleDI and including also the plain CircleDI package will cause errors.



Get Started

  1. Add PackageReference to your .csproj file.
<ItemGroup>
  <PackageReference Include="CircleDI.MinimalAPI" Version="{latest version}" PrivateAssets="all" />
</ItemGroup>
  1. Create a partial class with the ServiceProviderAttribute.
using CircleDIAttributes;

[ServiceProvider]
[ScopedProvider(ThreadSafe = false)]
public partial class MyMinimalAPIServiceProvider;
  1. Register to the built-in provider the ServiceProvider as Singleton and the ScopeProvider as Scoped.
builder.Services.AddSingleton<IMyMinimalAPIServiceProvider, MyMinimalAPIServiceProvider>();
builder.Services.AddScoped<IMyMinimalAPIServiceProvider.IScope, MyMinimalAPIServiceProvider.Scope>();
  1. Map all endpoints decorated with the EndpointAttribute.
using CircleDIAttributes;

...
app.MapCircleDIEndpoints();
  1. Create an endpoint handler by using the EndpointAttribute.
using CircleDIAttributes;

public static class EndpointCollection {
    [Endpoint("/", Http.Get)]
    public static string MyFirstEndpoint() {
        return "Hello World!";
    }
}



Inject Service to Endpoint method

To inject a service to an endpoint from a ServiceProvider generated by CircleDI, you must explicitly use the [Dependency] attribute. This works only with endpoints mapped with the EndpointAttribute.

using CircleDIAttributes;

public sealed class MyService : IMyService;
public interface IMyService;

[ServiceProvider]
[Singleton<IMyService, MyService>]
public partial class MyMinimalAPIServiceProvider;


public static class EndpointCollection {
    [Endpoint("/dependency", Http.Get)]
    public static string EndpointWithDependency([Dependency] IMyService myService) {
        return myService.ToString();
    }
}



Custom Constructor -> Custom Registration

You can also define a constructor inside your ServiceProvider/ScopeProvider that takes parameter. In that case you register the provider with the method overload that takes the implementationFactory parameter.

using CircleDIAttributes;

public partial interface IMyMinimalAPIServiceProvider;

[ServiceProvider]
public sealed partial class MyMinimalAPIServiceProvider {
    private readonly string _connectionString;

    public MyMinimalAPIServiceProvider(string connectionString, IServiceProvider builtinProvider) {
        _connectionString = connectionString;
        InitServices(builtinProvider);
    }


    public sealed partial class Scope {
        private readonly string _connectionStringScoped;

        public Scope(string connectionStringScoped, [Dependency] IMyMinimalAPIServiceProvider serviceProvider, IServiceProvider builtinProvider) {
            _connectionStringScoped = connectionStringScoped;
            InitServices(serviceProvider, builtinProvider);
        }
    }
}

...

string connectionString = GetConnectionString();
string connectionStringScoped = GetConnectionStringScoped();

builder.Services.AddSingleton<IMyMinimalAPIServiceProvider>((IServiceProvider sp) => new MyMinimalAPIServiceProvider(connectionString, sp));
builder.Services.AddScoped<IMyMinimalAPIServiceProvider.IScope>((IServiceProvider sp) => sp.GetRequiredService<IMyMinimalAPIServiceProvider>().CreateScope(connectionStringScoped, sp));



Endpoint

The endpoint attribute has 3 parameters: pattern, httpMethod, RouteBuilder. The first two are required, the third one is optional.

pattern is the same parameter as in the normal app.Map() call and httpMethod maps to the equivalent app.Map() call. Following table shows some examples:

Normal Endpoint CircleDI Endpoint
app.Map("/", ...); [Endpoint("/", Http.Any)]
app.MapGet("/books/{id}", ...); [Endpoint("/books/{id}", Http.Get)]
app.MapPost("/book", ...); [Endpoint("/book", Http.Post)]
app.MapDelete("/book", ...); [Endpoint("/book", Http.Delete)]

The third parameter is used to specify a method that is normally added behind the map call e.g. .ShortCircuit();, .AddEndpointFilter(...) or .WithName("...").WithOpenApi();

Following is the Weatherforecast example as CircleDI endpoint.

public static class WeatherforecastEndpoint {
    public static void WeatherForecastRouteBuilder(RouteHandlerBuilder routeHandlerBuilder) {
        routeHandlerBuilder.WithName("GetWeatherForecast").WithOpenApi();
    }

    [Endpoint("/weatherforecast", Http.Get, RouteBuilder = nameof(WeatherForecastRouteBuilder))]
    public static WeatherForecast[] WeatherForecast() {
        WeatherForecast[] forecast = Enumerable.Range(1, 5).Select(index =>
            new WeatherForecast {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = SummaryList[Random.Shared.Next(SummaryList.Length)]
            })
            .ToArray();
        return forecast;
    }

    private static readonly string[] SummaryList = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"];
}



EndpointProvider

The ServiceProviderAttribute has an additional property EndpointProvider. Per default this value is true, so every created ServiceProvider will be the provider used for resolving dependencies for [Endpoint]-methods. Since in most cases there is only one ServiceProvider, this works fine, but as soon as another ServiceProvider is defined, you get an error that only one EndpointProvider is allowed. By setting the EndpointProvider value of one ServiceProivder to false, that ServiceProvider becomes a normal provider and the other one is used for the [Endpoint]-methods.

[Endpoint]-methods also work without any EndpointProvider, as long there is no [Dependency]-parameter.



Additional/Changed Types



ServiceProviderAttribute / ServiceProviderAttribute<TInterface>

Same as ServiceProviderAttribute + following properties:

Properties

Name Type Description
EndpointProvider bool Indicates that this provider is taken for resolving [Dependency]-parameters in [Endpoint]-methods. Default is true.



EndpointAttribute

Adds a RouteEndpoint to the IEndpointRouteBuilder that matches HTTP requests for the specified pattern with the specified HTTP method.

The method must be static and non-generic. Dependencies must be explicitly marked with [Dependency].

Constructor Parameters

Name Type Description
pattern string The pattern for the route or url.
httpMethod Http The HTTP method for this endpoint.

Properties

Name Type Description
RouteBuilder string The name of a method that takes RouteHandlerBuilder to configure the endpoint. The method must be in the same class.



Http (enum)

HTTP method of the endpoint.

Members

Name Description
Any Matches any HTTP requests for the specified pattern.
Get Matches HTTP GET requests for the specified pattern.
Post Matches HTTP POST requests for the specified pattern.
Put Matches HTTP PUT requests for the specified pattern.
Patch Matches HTTP PATCH requests for the specified pattern.
Delete Matches HTTP DELETE requests for the specified pattern.