Skip to content

Commit 88d6c84

Browse files
Copilotmoattarwork
andcommitted
Add minimal API support to LittleBlocks framework
Co-authored-by: moattarwork <[email protected]>
1 parent 5b46bf6 commit 88d6c84

File tree

8 files changed

+644
-1
lines changed

8 files changed

+644
-1
lines changed

README.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Boilerplate api provides the following features:
2121
* Swagger integration
2222
* Health endpoint exposed
2323
* Diagnostics endpoint
24+
* **Minimal API support** for modern .NET applications
2425

2526
For using the full benefit of the library, Create a simple asp.net core project and install *EasyApi.AspNetCore.Bootstrap* nuget package.
2627

@@ -34,11 +35,12 @@ Install-Package LittleBlocks.AspNetCore.Bootstrap
3435
3536
```
3637

38+
## Traditional Usage with Startup Class
39+
3740
In order to achieve all of this functionality you merely need a few lines of code. At this point your *Program.cs* should look like this:
3841

3942
```csharp
4043

41-
4244
public class Program
4345
{
4446
public static void Main(string[] args)
@@ -79,6 +81,45 @@ In order to achieve all of this functionality you merely need a few lines of cod
7981

8082
```
8183

84+
## Minimal API Usage
85+
86+
For minimal APIs, you can use the new simplified approach:
87+
88+
```csharp
89+
90+
using LittleBlocks.AspNetCore.Bootstrap;
91+
92+
var builder = WebApplication.CreateBuilder(args);
93+
94+
// Bootstrap LittleBlocks services
95+
builder.BootstrapLittleBlocks(app => app
96+
.AddConfigSection<AppSettings>()
97+
.HandleApplicationException<MyApplicationException>()
98+
.ConfigureCorrelation(m => m.AutoCorrelateRequests())
99+
.ConfigureHealthChecks(c =>
100+
{
101+
c.AddUrlGroup(new Uri("http://www.google.com"), HttpMethod.Get, "google");
102+
})
103+
.AddServices((container, config) =>
104+
{
105+
container.AddScoped<IMyService, MyService>();
106+
})
107+
);
108+
109+
var app = builder.Build();
110+
111+
// Configure the pipeline
112+
app.UseLittleBlocksPipeline();
113+
114+
// Define your minimal API endpoints
115+
app.MapGet("/api/hello", () => "Hello World!");
116+
117+
app.Run();
118+
119+
```
120+
121+
This provides the same rich feature set including global error handling, logging, health checks, authentication, CORS, and Swagger documentation, but with the simplified minimal API approach.
122+
82123
The project/solution is ready to be running in visual studio or using dotnet cli.
83124

84125
More detail information can be found in [wiki](https://github.com/LittleBlocks/LittleBlocks.API/wiki)
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// This software is part of the LittleBlocks framework
2+
// Copyright (C) 2024 LittleBlocks
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace LittleBlocks.AspNetCore.Bootstrap;
18+
19+
/// <summary>
20+
/// Bootstrapper for minimal APIs that provides the same configuration capabilities as the traditional AppBootstrapper
21+
/// but without requiring a Startup class.
22+
/// </summary>
23+
public sealed class MinimalApiBootstrapper :
24+
IBootstrapApplication,
25+
IConfigureContainer,
26+
IAddExtraConfigSection,
27+
IHandleAdditionalException,
28+
ISetDetailsLevel,
29+
IExtendPipeline,
30+
IConfigureRequestCorrelation,
31+
IConfigureAuthentication,
32+
IConfigureHealthChecks,
33+
IConfigureApplicationBootstrapper
34+
{
35+
private readonly IConfiguration _configuration;
36+
private readonly ConfigurationOptionBuilder _configurationOptionBuilder;
37+
private readonly GlobalErrorHandlerConfigurationBuilder _errorHandlerBuilder;
38+
private readonly List<Action<IServiceCollection, IConfiguration>> _pipelineExtenders =
39+
new List<Action<IServiceCollection, IConfiguration>>();
40+
41+
private readonly IServiceCollection _services;
42+
private readonly IHealthChecksBuilder _healthChecksBuilder;
43+
private readonly AppInfo _appInfo;
44+
45+
private readonly AuthOptions _authOptions;
46+
private Action<IServiceCollection, IConfiguration> _containerFactory;
47+
private Func<IExcludeRequests, IBuildOptions> _requestCorrelationExtender = cop => cop.EnforceCorrelation();
48+
49+
public MinimalApiBootstrapper(
50+
IServiceCollection services,
51+
IConfiguration configuration)
52+
{
53+
_services = services ?? throw new ArgumentNullException(nameof(services));
54+
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
55+
_configurationOptionBuilder = new ConfigurationOptionBuilder(services, _configuration);
56+
_errorHandlerBuilder = new GlobalErrorHandlerConfigurationBuilder(services);
57+
58+
_errorHandlerBuilder.UseStandardMessage();
59+
_appInfo = _configuration.GetApplicationInfo();
60+
_authOptions = _configuration.GetAuthOptions();
61+
62+
_healthChecksBuilder = _services.AddHealthChecks();
63+
}
64+
65+
public IAddExtraConfigSection AndSection<TSection>()
66+
where TSection : class, new()
67+
{
68+
_configurationOptionBuilder.And<TSection>();
69+
return this;
70+
}
71+
72+
public IAddExtraConfigSection AndSection<TSection>(string section)
73+
where TSection : class, new()
74+
{
75+
_configurationOptionBuilder.And<TSection>(section);
76+
return this;
77+
}
78+
79+
public IHandleAdditionalException HandleApplicationException<TApplicationBaseException>()
80+
where TApplicationBaseException : Exception
81+
{
82+
_errorHandlerBuilder.Handle<TApplicationBaseException>();
83+
return this;
84+
}
85+
86+
public void Bootstrap()
87+
{
88+
_configurationOptionBuilder.Build();
89+
90+
_services.TryAddSingleton<IDateTimeProvider, DateTimeProvider>();
91+
// Don't add IUrlHelper for minimal APIs as it requires MVC ActionContext
92+
_services.TryAddScoped<IArgumentsFormatter, ArgumentsFormatter>();
93+
_services.TryAddSingleton(_ => new ArgumentFormatterOptions());
94+
95+
_services.AddDatabaseDeveloperPageExceptionFilter();
96+
_services.AddHttpRequestContext();
97+
_services.AddGlobalExceptionHandler(_ => _errorHandlerBuilder.UseDefault());
98+
_services.AddRequestCorrelation(b => _requestCorrelationExtender(b.ExcludeDefaultUrls()));
99+
_services.AddFeatureFlagging(_configuration);
100+
101+
// For minimal APIs, we don't add the default MVC services automatically
102+
// Users can still add controllers if needed via AddControllers() on the builder
103+
_services.AddDefaultCorsPolicy();
104+
_services.AddAuthentication(_authOptions);
105+
_services.AddAuthorization(); // Required for minimal APIs
106+
107+
// Add minimal API specific services for OpenAPI/Swagger
108+
_services.AddEndpointsApiExplorer();
109+
_services.AddSwaggerGen();
110+
111+
_pipelineExtenders.ForEach(e => e(_services, _configuration));
112+
113+
_containerFactory(_services, _configuration);
114+
}
115+
116+
public IAddExtraConfigSection AddConfigSection<TSection>()
117+
where TSection : class, new()
118+
{
119+
_configurationOptionBuilder.AddSection<TSection>();
120+
return this;
121+
}
122+
123+
public IAddExtraConfigSection AddConfigSection<TSection>(string section)
124+
where TSection : class, new()
125+
{
126+
_configurationOptionBuilder.AddSection<TSection>(section);
127+
return this;
128+
}
129+
130+
public IBootstrapApplication UseContainer<TContainer>(ContainerFactory<TContainer> containerFactory)
131+
where TContainer : class
132+
{
133+
if (containerFactory == null) throw new ArgumentNullException(nameof(containerFactory));
134+
135+
_containerFactory = containerFactory.Create;
136+
return this;
137+
}
138+
139+
public IConfigureRequestCorrelation UseStandardMessage()
140+
{
141+
_errorHandlerBuilder.UseStandardMessage();
142+
return this;
143+
}
144+
145+
public IConfigureRequestCorrelation UseUserErrors()
146+
{
147+
_errorHandlerBuilder.UseUserErrors();
148+
return this;
149+
}
150+
151+
public IConfigureRequestCorrelation UseDetailedErrors()
152+
{
153+
_errorHandlerBuilder.UseDetailedErrors();
154+
return this;
155+
}
156+
157+
public IConfigureAuthentication ConfigureCorrelation(
158+
Func<IExcludeRequests, IBuildOptions> optionsProvider)
159+
{
160+
_requestCorrelationExtender = optionsProvider ??
161+
throw new ArgumentNullException(nameof(optionsProvider));
162+
return this;
163+
}
164+
165+
public IConfigureAuthentication ConfigureCorrelation(Func<IExcludeRequests, ICorrelateRequests> optionsProvider)
166+
{
167+
if (optionsProvider == null) throw new ArgumentNullException(nameof(optionsProvider));
168+
_requestCorrelationExtender = r => optionsProvider(r).EnforceCorrelation();
169+
return this;
170+
}
171+
172+
public IExtendPipeline Extend(Action<IServiceCollection, IConfiguration> pipelineExtender)
173+
{
174+
if (pipelineExtender == null) throw new ArgumentNullException(nameof(pipelineExtender));
175+
_pipelineExtenders.Add(pipelineExtender);
176+
return this;
177+
}
178+
179+
public IConfigureHealthChecks ConfigureAuthentication(Action<ISetAuthenticationMode> configure)
180+
{
181+
if (configure == null) throw new ArgumentNullException(nameof(configure));
182+
configure(_authOptions);
183+
return this;
184+
}
185+
186+
public IExtendPipeline ConfigureHealthChecks(Action<IHealthChecksBuilder> configure)
187+
{
188+
if (configure == null) throw new ArgumentNullException(nameof(configure));
189+
190+
configure(_healthChecksBuilder);
191+
192+
return this;
193+
}
194+
195+
public IHandleAdditionalException AndHandle<TThirdPartyBaseException>()
196+
where TThirdPartyBaseException : Exception
197+
{
198+
_errorHandlerBuilder.AndHandle<TThirdPartyBaseException>();
199+
return this;
200+
}
201+
202+
public IHandleAdditionalException AndHandle<TThirdPartyBaseException>(
203+
Func<TThirdPartyBaseException, bool> predicate) where TThirdPartyBaseException : Exception
204+
{
205+
_errorHandlerBuilder.AndHandle(predicate);
206+
return this;
207+
}
208+
209+
public IHandleAdditionalException AndHandle<TThirdPartyBaseException>(
210+
Func<ISetErrorBuilder<TThirdPartyBaseException>, IProvideErrorBuilder<TThirdPartyBaseException>>
211+
errorBuilderProvider) where TThirdPartyBaseException : Exception
212+
{
213+
_errorHandlerBuilder.AndHandle(errorBuilderProvider);
214+
return this;
215+
}
216+
217+
public IHandleAdditionalException AndHandle<TThirdPartyBaseException>(
218+
Func<ISetErrorBuilder<TThirdPartyBaseException>, IProvideErrorBuilder<TThirdPartyBaseException>>
219+
errorBuilderProvider, Func<TThirdPartyBaseException, bool> predicate)
220+
where TThirdPartyBaseException : Exception
221+
{
222+
_errorHandlerBuilder.AndHandle(errorBuilderProvider, predicate);
223+
return this;
224+
}
225+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// This software is part of the LittleBlocks framework
2+
// Copyright (C) 2024 LittleBlocks
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace LittleBlocks.AspNetCore.Bootstrap;
18+
19+
/// <summary>
20+
/// Extension methods for WebApplicationBuilder to support LittleBlocks minimal API configuration.
21+
/// </summary>
22+
public static class WebApplicationBuilderExtensions
23+
{
24+
/// <summary>
25+
/// Bootstraps the application services using LittleBlocks configuration for minimal APIs.
26+
/// </summary>
27+
/// <param name="builder">The WebApplicationBuilder instance</param>
28+
/// <param name="appBootstrapperProvider">Configuration function for the application bootstrapper</param>
29+
/// <returns>The configured WebApplicationBuilder</returns>
30+
public static WebApplicationBuilder BootstrapLittleBlocks(
31+
this WebApplicationBuilder builder,
32+
Func<IConfigureApplicationBootstrapper, IBootstrapApplication> appBootstrapperProvider)
33+
{
34+
ArgumentNullException.ThrowIfNull(builder);
35+
ArgumentNullException.ThrowIfNull(appBootstrapperProvider);
36+
37+
// Create a type placeholder for the bootstrapper since minimal APIs don't have a startup class
38+
var bootstrapper = appBootstrapperProvider(new MinimalApiBootstrapper(builder.Services, builder.Configuration));
39+
bootstrapper.Bootstrap();
40+
41+
return builder;
42+
}
43+
}

0 commit comments

Comments
 (0)