- Notifications
You must be signed in to change notification settings - Fork1
A dotnet library that allows you to build WebApiEndpoints using a vertical slice architecture approach. Built on dotnet 8 and minimal apis.
License
futurum-dev/dotnet.futurum.webapiendpoint.micro
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
The Futurum.WebApiEndpoint.Micro is a powerful .NET library that aids in the construction of WebApiEndpoints in a systematic manner.
It leverages the capabilities of .NET 8 and minimal APIs to facilitate the development of your Web APIs in a well-structured and defined manner. This library not only streamlines the process of Web API development but also provides the flexibility to incorporate new features without modifying any existing code. This is achieved through the implementation of a vertical slice architecture.
In the context of vertical slice architecture, each feature or functionality of the application is developed as an independent slice, from the user interface to the data storage layer. This approach enhances the modularity of the application, making it easier to add, modify, or remove features without affecting the rest of the application. The Futurum.WebApiEndpoint.Micro library embodies this architectural style, making it an excellent tool for developing robust and scalable Web APIs.
[WebApiEndpoint("greeting")]publicpartialclassGreetingWebApiEndpoint{protectedoverridevoidBuild(IEndpointRouteBuilderbuilder){builder.MapGet("/hello",HelloHandler);builder.MapGet("/goodbye",GoodbyeHandler);}privatestaticOk<string>HelloHandler(HttpContextcontext,stringname)=>$"Hello{name}".ToOk();privatestaticOk<string>GoodbyeHandler(HttpContextcontext,stringname)=>$"Goodbye{name}".ToOk();}
- ✅ Structured way of building WebApiEndpoints usingminimal apis
- ✅Easy setup
- ✅ Full support and built on top ofminimal apis
- ✅ Full support for OpenApi
- ✅ Full support for Api Versioning
- ✅ Full support forTypedResults
- ✅ Support for configuring
- ✅Futurum.WebApiEndpoint.Micro
- ✅entire API
- ✅specific API version
- ✅individual WebApiEndpoint(s)
- ✅ individual REST method(s) - as per standard minimal apis
- ✅Supports uploading file(s) with additional JSON payload
- ✅ Built insandbox runner with fullTypedResults support, catching unhandled exceptions and returning aProblemDetails response
- ✅ Autodiscovery of WebApiEndpoint(s), based on Source Generators
- ✅Roslyn Analysers to help build your WebApiEndpoint(s) and ensure best practices
- ✅ Enables Vertical Slice Architecture, giving you the ability to add new features without changing existing code
- ✅ Built on dotnet 8
- ✅ Built in use ofProblemDetails support
- ✅ Built inextendable GlobalExceptionHandler
- ✅ Developer friendly, with a simple API and with a full suite of samples and tests
- ✅Tested solution
- ✅Comprehensive samples
- ✅Convention Customisation
- What is a WebApiEndpoint?
- Easy setup
- Configuration
- Sandbox runner
- Uploading file(s) with additional JSON payload
- Additional helper functions
- Comprehensive samples
- Convention Customisation
- Extendable GlobalExceptionHandler
- Tips & Tricks
- FAQ
- Troubleshooting
- Roslyn Analysers
- It represents a vertical slice or a distinct feature of your application.
- Each vertical slice is a self-contained functional unit.
- It's a collection of Web APIs that share a common route prefix and version. They can also share various aspects such as Security, EndpointFilters, RateLimiting, OutputCaching, and more.
Check out this section for a step-by-step guide to setting up the library for use in your development environment.
- ✅ Add theNuGet package ( futurum.webapiendpoint.micro ) to your project
- ✅ Updateprogram.cs as perhere
Here's an example of how to update yourprogram.cs file:
usingFuturum.WebApiEndpoint.Micro;usingFuturum.WebApiEndpoint.Micro.Sample;varbuilder=WebApplication.CreateBuilder(args);builder.Services.AddWebApiEndpoints(newWebApiEndpointConfiguration{DefaultApiVersion=WebApiEndpointVersions.V1_0.Version,OpenApi=new(){DefaultInfo=new(){Title="Futurum.WebApiEndpoint.Micro.Sample",}}}).AddWebApiEndpointsForFuturumWebApiEndpointMicroSample();varapp=builder.Build();app.UseWebApiEndpoints();if(app.Environment.IsDevelopment()){app.UseWebApiEndpointsOpenApi();}app.Run();
Seeprogram.cs in sample project
This will be automatically created by the source generator.
You need to call this for each project that contains WebApiEndpoints, in order for them to be added to the pipeline.
e.g.
builder.Services.AddWebApiEndpointsForFuturumWebApiEndpointMicroSample();
Adds the WebApiEndpoints to the pipeline and does various other setup needed for the WebApiEndpoints to work.
app.UseWebApiEndpoints();
Register the OpenApi UI (Swagger and SwaggerUI) middleware. This is usually only done in development mode.
app.UseWebApiEndpointsOpenApi();
- Create a new partial class.
- Add theWebApiEndpoint attribute to the class, with theroute prefix for all the REST methods in this WebApiEndpoint. You can also optionally add atag. This is used in the OpenApi documentation. If you do not specify a tag, then the route prefix is used.
- Add theWebApiEndpointVersion attribute to the class, if you want to specify a specificApiVersion. If you do not specify a specificApiVersion, then the defaultApiVersion is used. You can add multipleWebApiEndpointVersion attributes to the class, if you want to support multipleApiVersions.
- Implement theBuild method and addminimal api(s) as per usual.
- Optionally implement theConfigure method to configuration theWebApiEndpoint
You canmap your minimal apis for this WebApiEndpoint in theBuild method.
TheIEndpointRouteBuilder that theBuild method receives has already:
- been configured withconfiguring for the entire API
- been configured with the API versioning
- been configured withconfiguring a specific API version
- been configured with the route prefix and tag
- been through theoptionalConfigure method in the same class
protectedoverridevoidBuild(IEndpointRouteBuilderbuilder){}
[WebApiEndpoint("weather")]publicpartialclassWeatherWebApiEndpoint{privatestaticreadonlystring[]Summaries={"Freezing","Bracing","Chilly","Cool","Mild","Warm","Balmy","Hot","Sweltering","Scorching"};protectedoverridevoidBuild(IEndpointRouteBuilderbuilder){builder.MapGet("/",GetHandler);}privatestaticOk<IEnumerable<WeatherForecastDto>>GetHandler(HttpContexthttpContext,CancellationTokencancellationToken)=>Enumerable.Range(1,5).Select(index=>newWeatherForecastDto(DateOnly.FromDateTime(DateTime.Now.AddDays(index)),Random.Shared.Next(-20,55),Summaries[Random.Shared.Next(Summaries.Length)])).ToOk();}
SeeWeatherWebApiEndpoint in sample project
You canoptionally configure the WebApiEndpoint in theConfigure method.
protectedoverrideRouteGroupBuilderConfigure(RouteGroupBuildergroupBuilder,WebApiEndpointVersionwebApiEndpointVersion){}
This allows you to setup the RouteGroupBuilder. This will effect all minimal apis in this classesBuild method.
You can also configure it differently per ApiVersion.
groupBuilder.AddEndpointFilter<CustomEndpointFilter>();
SeeEndpointFilterWebApiEndpoint in sample project
groupBuilder.RequireRateLimiting(RateLimiting.SlidingWindow.Policy);
SeeRateLimitingWebApiEndpoint in sample project
groupBuilder.CacheOutput(OutputCaching.ExpiryIn10Seconds.Policy);
SeeOutputCachingWebApiEndpoint in sample project
groupBuilder.RequireAuthorization(Authorization.Permission.Admin);
SeeSecurityProtectedWebApiEndpoint in sample project
This section details how to configure Futurum.WebApiEndpoint.Micro to make the most out of its features for specific use cases. Apart from configuring the entire API, it will also show you how to configure a specific API version, and individual WebApiEndpoint(s).
This allows you to configure:
- DefaultApiVersion(mandatory)
- This is used if a ApiVersion is not provided for a specific WebApiEndpoint.
- OpenApi
- DefaultInfo
- This is used if a OpenApiInfo is not provided for a specific ApiVersion
- VersionedOverrideInfo(optional)
- Allows you to have an OpenApiInfo per a specific ApiVersion.
- If youdo not provide an OpenApiInfo for a specific ApiVersion, then the DefaultInfo is used.
- If youdo provide an OpenApiInfo for a specific ApiVersion and provide a property, then that versioned property is used.
- If youdo provide an OpenApiInfo for a specific ApiVersion, but don't provide a property, then the DefaultInfo property is used.
- NOTE:Extensions is a dictionary, an Extensions for a specific ApiVersion is merged with the defaultExtensions, with the specific ApiVersion being used as the override (by key) if necessary.
- Allows you to have an OpenApiInfo per a specific ApiVersion.
- DefaultInfo
- Version
- Prefix(optional)
- Format(optional)
- uses 'Asp.Versioning.ApiVersionFormatProvider'
builder.Services.AddWebApiEndpoints(newWebApiEndpointConfiguration{DefaultApiVersion=WebApiEndpointVersions.V1_0.Version,OpenApi=new(){DefaultInfo=new(){Title="Futurum.WebApiEndpoint.Micro.Sample",},VersionedOverrideInfo={{WebApiEndpointVersions.V3_0.Version,newWebApiEndpointOpenApiInfo{Title="Futurum.WebApiEndpoint.Micro.Sample v3"}}}}});
Seeprogram.cs in sample project
The configuration is applied in the following order:
flowchart TB classDef blackText stroke:#000,color:#000; entire-api[1. Entire API] --> specific-version-api[2. Specific Version API] specific-version-api --> endpoint-configure[3. WebApiEndpoint 'Configure' Method] endpoint-configure --> endpoint-build[4. WebApiEndpoint 'Build' Method] endpoint-build --> minimal-api[5. Individual Minimal API] style entire-api fill:#f9d0c4 style specific-version-api fill:#fcbf49 style endpoint-configure fill:#90be6d style endpoint-build fill:#43aa8b style minimal-api fill:#577590 class entire-api,specific-version-api,endpoint-configure,endpoint-build,minimal-api blackTextThe entire API can be configured to set global parameters. This is an ideal place to set configurations such as:
- Global route prefix: This is a common prefix for all routes in your API.
- Global authorization: This is where you can set the authorization required for all endpoints in your API. Remember to use the AllowAnonymous attribute on individual WebApiEndpoints that should not be secured, such as a Login endpoint.
To configure the entire API, you need to create a class that implements the IGlobalWebApiEndpoint interface.
Note: There can only be one class that implements IGlobalWebApiEndpoint. This is enforced by an analyzer. Analyzers work per project, so if you haveGlobalWebApiEndpoint in more than 1 project, only the first will be registered. The order is dictated by the order of theAddWebApiEndpointsFor calls. Seehere
Note: The configuration set in this class is applied before the version route is created.
Here is an example of how to implement this:
publicclassGlobalWebApiEndpoint:IGlobalWebApiEndpoint{publicIEndpointRouteBuilderConfigure(IEndpointRouteBuilderbuilder,WebApiEndpointConfigurationconfiguration){// Set the global route prefix to "api" and require admin authorization for all endpointsreturnbuilder.MapGroup("api").RequireAuthorization(Authorization.Permission.Admin);}}
SeeGlobalWebApiEndpoint in sample project
In the context of API development, it's often necessary to configure specific API versions. This configuration can include aspects such as:
- Authorization specific to the API version: This is where you can set the authorization required for all endpoints in a specific API version. Don't forget to use the AllowAnonymous attribute on individual WebApiEndpoints that should not be secured, such as a Login endpoint.
To configure a specific API version, you need to create a class that:
- Implements the IWebApiVersionEndpoint interface.
- Is decorated with at least one WebApiVersionEndpointVersion attribute, indicating the version(s) it applies to.
Note: There can only be one class that configures a specific API version. This is enforced by a Roslyn analyzer. Analyzers work per project, so if you have the sameWebApiVersionEndpoint for a version in more than 1 project, only the first will be registered. The order is dictated by the order of theAddWebApiEndpointsFor calls. Seehere
Note: The configuration set in this class is applied after the version route is created, but before the specific WebApiEndpoint route is created.
Here's an example of how to implement this:
[WebApiVersionEndpointVersion(WebApiEndpointVersions.V3_0.Number)][WebApiVersionEndpointVersion(WebApiEndpointVersions.V1_20_Beta.Text)]publicclassWebApiVersionEndpoint3_0a:IWebApiVersionEndpoint{publicRouteGroupBuilderConfigure(IEndpointRouteBuilderbuilder,WebApiEndpointConfigurationconfiguration){// Set the route group to "test-api" and require admin authorization for all endpointsreturnbuilder.MapGroup("test-api").RequireAuthorization(Authorization.Permission.Admin);}}
SeeWebApiVersionEndpoint3_0a in sample project
Remember, the configuration of specific API versions is a crucial aspect of maintaining and managing your APIs, especially when dealing with different versions of the same API. It allows you to control the behavior of each version independently, providing flexibility and control over your API's functionality.
Seehere
Seehere.
Seehere.
The Sandbox Runner is a feature that provides a set of comprehensive extension methods to execute your code in a controlled environment or "sandbox".
These extension methods are designed to handle code that returns an IResult. The behavior of these methods is as follows:
- If your code executes without throwing an unhandled exception, the original return value remains unchanged.
- If your code throws an unhandled exception, a BadRequest<ProblemDetails> is returned, containing relevant details about the exception.
The returned Results<...> type is always expanded to include BadRequest<ProblemDetails>.
TIResult1 ->Results<TIResult1,BadRequest<ProblemDetails>>Results<TIResult1,TIResult2> ->Results<TIResult1,TIResult2,BadRequest<ProblemDetails>>Results<TIResult1,TIResult2,TIResult3> ->Results<TIResult1,TIResult2,TIResult3,BadRequest<ProblemDetails>>Results<TIResult1,TIResult2,TIResult3,TIResult4> ->Results<TIResult1,TIResult2,TIResult3,TIResult4,BadRequest<ProblemDetails>>Results<TIResult1,TIResult2,TIResult3,TIResult4,TIResult5> ->Results<TIResult1,TIResult2,TIResult3,TIResult4,TIResult5,BadRequest<ProblemDetails>>
The Results type can accommodate a maximum of 6 types. Therefore, up to 5 types are allowed, reserving one space for BadRequest<ProblemDetails>.
privatestaticResults<NotFound,FileStreamHttpResult,BadRequest<ProblemDetails>>DownloadHandler(HttpContextcontext){returnRun(Execute,context,"Failed to read file");Results<NotFound,FileStreamHttpResult>Execute(){varpath="./Data/hello-world.txt";if(!File.Exists(path)){returnTypedResults.NotFound();}varfileStream=File.OpenRead(path);returnTypedResults.File(fileStream,MediaTypeNames.Application.Octet,"hello-world.txt");}}
In this example theExecute method is wrapped by the runner. It returns:
- aNotFound if the file does not exist
- aFileStreamHttpResult if the file exists
Results<NotFound,FileStreamHttpResult>
TheRun /RunAsync extension method modifies this to includeBadRequest<ProblemDetails>.
Results<NotFound,FileStreamHttpResult,BadRequest<ProblemDetails>>
Note: It is recommended to add the following to yourGlobalUsings.cs file.
globalusingstaticFuturum.WebApiEndpoint.Micro.WebApiEndpointRunner;
This allows you to use the helper functions without having to specify the namespace, as demonstrated in the examples.
These extension methods are designed to handle code that returnsvoid orT. The behavior of these methods is as follows:
- If your code executes without throwing an unhandled exception, the original return value remains unchanged.
- If your code throws an unhandled exception, a BadRequest<ProblemDetails> is returned, containing relevant details about the exception.
The returned Results<...> type is always expanded to include BadRequest<ProblemDetails>.
void ->Results<Ok,BadRequest<ProblemDetails>>T ->Results<Ok<T>,BadRequest<ProblemDetails>>
privatestaticResults<Ok<IAsyncEnumerable<Todo>>,BadRequest<ProblemDetails>>GetAllHandler(HttpContextcontext,SqliteConnectiondb){returnRunToOk(Execute,context,"Failed to get todos");IAsyncEnumerable<Todo>Execute()=>db.QueryAsync<Todo>("SELECT * FROM Todos");}
In this example theExecute method returnsIAsyncEnumerable<Todo>
IAsyncEnumerable<Todo>
TheRunToOk /RunToOkAsync extension method will
- change theT toOk<T>
- addBadRequest<ProblemDetails>.
Results<Ok<IAsyncEnumerable<Todo>>,BadRequest<ProblemDetails>>
Note: It is recommended to add the following to yourGlobalUsings.cs file.
globalusingstaticFuturum.WebApiEndpoint.Micro.WebApiEndpointRunner;
This allows you to use the helper functions without having to specify the namespace, as demonstrated in the examples.
Implement and register in DI you're ownIWebApiEndpointRunnerExceptionHandlerService.
This section guides you on how to upload files with additional JSON payload using Futurum.WebApiEndpoint.Micro.
Use theFormFileWithPayload type to upload a single file and a JSON payload
privatestaticTask<Results<Ok<FileDetailsWithPayloadDto>,BadRequest<ProblemDetails>>>UploadWithPayloadHandler(HttpContextcontext,FormFileWithPayload<PayloadDto>fileWithPayload){returnRunAsync(Execute,context,ToOk,"Failed to read file");asyncTask<FileDetailsWithPayloadDto>Execute(){vartempFile=Path.GetTempFileName();awaitusingvarstream=File.OpenWrite(tempFile);awaitfileWithPayload.File.CopyToAsync(stream);returnnewFileDetailsWithPayloadDto(fileWithPayload.File.FileName,fileWithPayload.Payload.Name);}}
Use theFormFilesWithPayload type to upload multiple files and a JSON payload
privatestaticTask<Results<Ok<IEnumerable<FileDetailsWithPayloadDto>>,BadRequest<ProblemDetails>>>UploadsWithPayloadHandler(HttpContextcontext,FormFilesWithPayload<PayloadDto>filesWithPayload){returnRunAsync(Execute,context,ToOk,"Failed to read file");asyncTask<IEnumerable<FileDetailsWithPayloadDto>>Execute(){varfileDetails=newList<FileDetailsWithPayloadDto>();foreach(varfileinfilesWithPayload.Files){vartempFile=Path.GetTempFileName();awaitusingvarstream=File.OpenWrite(tempFile);awaitfile.CopyToAsync(stream);fileDetails.Add(newFileDetailsWithPayloadDto(file.FileName,filesWithPayload.Payload.Name));}returnfileDetails;}}
The final section provides a detailed overview of the additional helper functions that aid in program development.
Converts aT to anOk<T>.
ToOk
Converts a() to aCreated.
ToCreated<string>
By default it will take the location from theHttpContext.Request.Path.
or
Converts aT to aCreated<T>.
This can be overridden by passing in astring.
ToCreated<T>("/api/articles")
Converts a() to aAccepted.
ToAccepted<string>
By default it will take the location from theHttpContext.Request.Path.
or
Converts aT to aAccepted<T>.
By default it will take the location from theHttpContext.Request.Path.
This can be overridden by passing in astring.
ToAccepted<T>("/api/articles")
There are examples showing the following:
- ✅ A basic blog CRUD implementation -link
- ✅ TheToDo sample from Damian Edwardshere -link
- ✅ AsyncEnumerable -link
- ✅ Bytes file download -link
- ✅ EndpointFilter on a specific WebApiEndpoint -link
- ✅ Exception handling -link
- ✅ File(s) upload -link
- ✅ File(s) upload with Payload -link
- ✅ File download -link
- ✅ OpenApi versioning -link v0,link v1, v1.20-beta, v3, v4-alpha,link v2
- ✅ Output Caching -link
- ✅ Rate Limiting -link
- ✅Security with a basic JWT example on a specific WebApiEndpoint -login link,protected link
- ✅ Weather Forecast -link
- ✅ Addition project containing WebApiEndpoints -link
- ✅ Configuring setting for entire API -link
- ✅ Configuring setting for specific API version -link v3, v1.20-beta
How to use in Swagger UI:
- Run the Sample project
- In the Swagger UI, go to the 'Security' 'Login' endpoint
- Set the following
- Username = user1
- Password = password1
- SetPermissions = true
- SetClaim = true
- SetRole = true
- Copy the value returned without double quotes.
- Go to the 'Security' 'Protected' endpoint
- Click on the padlock
- In the value textbox, enter "Bearer " (don't forget the space at the end) + the value returned from the 'Login' endpoint that you copied in step 4.
- Click "Authorize"
- Run the 'Protected' endpoint
Although the default conventions are good enough for most cases, you can customise them.
This is used to get theOpenApiInfo for eachWebApiEndpointVersion.
serviceCollection.AddWebApiEndpointOpenApiVersionConfigurationService<WebApiOpenApiVersionConfigurationService>();
This is used to configure theOpenApi JSON endpoint for eachWebApiEndpointVersion.
serviceCollection.AddWebApiEndpointOpenApiVersionUIConfigurationService<WebApiOpenApiVersionUIConfigurationService>();
This is used to configureApiVersioning andApiExplorer.
There is an overload ofAddWebApiEndpoints that takes a generic type ofIWebApiVersionConfigurationService.
builder.Services.AddWebApiEndpoints<CustomWebApiVersionConfigurationService>();
Use this instead
builder.Services.AddWebApiEndpoints();
Built in support for handling unhandled exceptions, returning aProblemDetails response.
You can extend theGlobalExceptionHandler by adding your own custom exception handling and overriding the default exception handler.
NOTE: ExceptionToProblemDetailsMapperService is not thread-safe for either:
- adding custom exception to ProblemDetails mapping
- overriding default exception to ProblemDetails mapping
It is recommended to do this in theprogram.cs file.
varbuilder=WebApplication.CreateBuilder(args);builder.Services.AddExceptionHandler<GlobalExceptionHandler>();...var app=builder.Build();app.UseExceptionHandler();
Seeprogram.cs in sample project
Inprogram.cs add the following:
ExceptionToProblemDetailsMapperService.Add<CustomException>((exception,httpContext,errorMessage)=>new(){Detail="An custom error occurred.",Instance=httpContext.Request.Path,Status=StatusCodes.Status500InternalServerError,Title=ReasonPhrases.GetReasonPhrase(StatusCodes.Status500InternalServerError)});
Inprogram.cs add the following:
ExceptionToProblemDetailsMapperService.OverrideDefault((exception,httpContext,errorMessage)=>new(){Detail="An error occurred.",Instance=httpContext.Request.Path,Status=StatusCodes.Status500InternalServerError,Title=ReasonPhrases.GetReasonPhrase(StatusCodes.Status500InternalServerError)});
You want to avoid duplicating the Api Versions in multiple places. So it's recommended to create a class that contains all the Api Versions.
publicstaticclassWebApiEndpointVersions{publicstaticclassV1_0{publicconstdoubleNumber=1.0d;publicstaticreadonlyWebApiEndpointVersionVersion=WebApiEndpointVersion.Create(Number);}publicstaticclassV4_0_Alpha{publicconstdoubleNumber=4.0d;publicconststringStatus="alpha";publicstaticreadonlyWebApiEndpointVersionVersion=WebApiEndpointVersion.Create(Number,Status);}publicstaticclassV1_20_Beta{publicconststringText="1.20-beta";publicstaticreadonlyWebApiEndpointVersionVersion=WebApiEndpointVersion.Create(Text);}}
SeeWebApiEndpointVersions in sample project
You can use this in yourprogram.cs file like this.
builder.Services.AddWebApiEndpoints(newWebApiEndpointConfiguration(WebApiEndpointVersions.V1_0.Version)
Seeprogram.cs in sample project
You can use this in your WebApiEndpoint like this.
[WebApiEndpoint("openapi")][WebApiEndpointVersion(WebApiEndpointVersions.V1_0.Number)]publicpartialclassOpenApiVersionV1WebApiEndpoint{ ...}
SeeOpenApiVersionV1WebApiEndpoint.cs in sample project
- ✅ when you want the OpenApi specification to correctly reflect error handling viaBadRequest<ProblemDetails>
- ✅ when you want to set a customerror message
Our recommendation is to useWebApiEndpointRunner for all your WebApiEndpoints.
If you see this error in the SwaggerUI -No operations defined in spec! - then it means you haven't added any WebApiEndpoint projects. You need to do this for each project, including the project that contains theprogram.cs file. Seethis section for more details.
If there are Rest Api's that are not being picked up, then it means you haven't added a WebApiEndpoint projects. You need to do this for each project, including the project that contains theprogram.cs file. Seethis section for more details.
We recommend that WebApiEndpoint's have an empty constructor and to take any injectable dependencies as parameters via the minimal API method itself.
Constructor dependencies will have a lifetime outside of the minimal API lifetime and could have unintended consequences.
Minimal API methods returning a 'BadRequest', should ensure that the 'BadRequest' is created with a 'ProblemDetails' instance.
Checks to ensure that there is only one instance of GlobalWebApiEndpoint.
Analyzers work per project, so if you haveGlobalWebApiEndpoint in more than 1 project, only the first will be registered. The order is dictated by the order of theAddWebApiEndpointsFor calls.
Checks to ensure that there is only one instance of WebApiVersionEndpoint.
Analyzers work per project, so if you have the sameWebApiVersionEndpoint for a version in more than 1 project, only the first will be registered. The order is dictated by the order of theAddWebApiEndpointsFor calls.
About
A dotnet library that allows you to build WebApiEndpoints using a vertical slice architecture approach. Built on dotnet 8 and minimal apis.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.
