.NET library extending functionality for web applications
- MVC Source Generators
- Improved Websocket Support
- Improved request tracing
- Improved client IP handling
- Improved configuration binding
- Mockable IHttpClient builder
- Support for base64 encoded certificates in configuration
Net.Sdk.Web.Extensions provides a set of source generators to replicate the MVC functionality of Asp while being compatible with NativeAOT compilation.
- Add the
Net.Sdk.Web.Extensions.SourceGenerators
package to your project - Create a controller class
[GenerateController("api/v1/accounts")]
public sealed class AccountsController(
IAccountService accountService)
{
private readonly IAccountService accountService = accountService;
[GenerateGet("{accountId}")]
[RouteFilter<AuthenticatedFilter>]
public async Task<IResult> GetAccount(HttpContext context, [FromRoute] string accountId, CancellationToken cancellationToken)
{
var account = await this.accountService.GetAccountById(accountId, cancellationToken);
if (account is null)
{
return Results.NotFound();
}
return Results.Ok(account);
}
[GeneratePost]
public async Task<IResult> PostAccount([FromBody] PostAccountRequest request, CancellationToken cancellationToken)
{
if (await this.accountService.CreateAccount(request.Email, request.Email, request.Password, cancellationToken))
{
return Results.Created();
}
return Results.BadRequest();
}
}
- Register the routes in the
WebApplicationBuilder
usingWithRoutes()
extension. This extension is generated by the source generator and is available under theNet.Sdk.Web
namespace inWebApplicationBuilderExtensions
class.
builder.WithRoutes();
- Use the routes in your
WebApplication
class usingUseRoutes()
extension. This extension is generated by the source generator and is available under theNet.Sdk.Web
namespace inWebApplicationExtensions
class.
app.UseRoutes();
GenerateController
attribute allows you to define a controller class and its route.GenerateGet
,GeneratePost
,GeneratePut
,GenerateDelete
attributes allow you to define the HTTP methods and their routes.FromRoute
,FromBody
,FromQuery
attributes allow you to define the parameters of the methods.RouteFilter
attribute allows you to define the filters for the methods. Filters must be of typeMicrosoft.AspNetCore.Http.IEndpointFilter
.HttpContext
can be accessed by requesting it as a parameter of your route method. The generator will automatically fetch it from the context and pass it to your method.- Controllers are registered as services in the DI container, so you can inject any service into them.
- There is no restriction on the modifiers of your controllers. You can freely use inheritance for shared functionality between controllers.
Net.Sdk.Web.Extensions provides a streamlined implementation of WebSockets to be used in a web app, in a syntax similar to Asp Mvc
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
app.MapWebSocket<CustomRoute>("custom-route");
[WebSocketConverter<JsonWebSocketMessageConverter<CustomRequest>, CustomRequest>]
public class CustomRequest{
}
[WebSocketConverter<JsonWebSocketMessageConverter<CustomResponse>, CustomResponse>]
public class CustomResponse{
}
public class CustomRoute : WebSocketRouteBase<CustomRequest, CustomResponse>
{
public override Task SocketAccepted(CancellationToken cancellationToken)
{
return base.SocketAccepted(cancellationToken);
}
public override Task SocketClosed()
{
return base.SocketClosed();
}
public override async Task ExecuteAsync(CustomRequest? request, CancellationToken cancellationToken)
{
await this.SendMessage(new BotResponse(), cancellationToken);
}
}
Decorate your CustomRoute
with filters that get executed the same way as in a AspNetCore web app
Both IActionFilter
and IAsyncActionFilter
are supported
[ServiceFilter<CustomFilter>]
public class CustomRoute : WebSocketRouteBase<CustomRequest, CustomResponse>
If you need specialized converters, implement your own WebSocketMessageConverter<T>
var builder = WebApplication.CreateSlimBuilder(args);
builder.WithCorrelationVector();
var app = builder.Build();
app.UseCorrelationVector();
Take a dependency on IHttpContextAccessor
and get the CV using the HttpContextExtensions.GetCorrelationVector
method
var cv = accessor.HttpContext.GetCorrelationVector();
Configure an instance of CorrelationVectorOptions
to adjust the CV header
- On a request, the
CorrelationVectorMiddleware
retrieves the CV from the request header if present, otherwise it creates a new one - The CV is stored in
HttpContext.Items
underCorrelationVector
key - Log the CV to follow traces of one application flow under multiple operations and across http requests
- Pass the
CorrelationVectorHandler
to theIHttpClientBuilder
to manage CVs across requests - On each
HttpClient
request, the CV is added to the configured header - After receiving the response, the
CorrelationVectorHandler
will receive and parse any existing CV from the headers and reapply it to theHttpContext.Items
IPExtractingMiddleware
figures out the IP of the client, being able to handle reverse proxying through X-Forwarded-For
header as well as CloudFlare specific CF-Connecting-IP
header
var builder = WebApplication.CreateSlimBuilder(args);
builder.WithIPExtraction();
var app = builder.Build();
app.UseIPExtraction();
Take a dependency on IHttpContextAccessor
and get the CV using the HttpContextExtensions.GetCorrelationVector
method
var cv = accessor.HttpContext.GetClientIP();
var builder = WebApplication.CreateSlimBuilder(args);
builder.ConfigureExtended<CustomOptions>();
public class CustomService
{
public CustomService (IOptions<CustomOptions> options)
{
}
}
[OptionsName(Name = "CustomKey")]
public class CustomOptions
{
}
var builder = WebApplication.CreateSlimBuilder(args);
builder.RegisterHttpClient<CustomService>()
.WithTimeout(TimeSpan.FromSeconds(5))
.WithCorrelationVector()
.CreateBuilder();
public class CustomService
{
public CustomService (IHttpClient<CustomService> client)
{
}
}
Use the Base64ToCertificateConverter
to retrieve your SSL certificate from configuration and bind it to options, to allow for dynamic loading of certificates
public class ServerOptions
{
[JsonConverter(typeof(Base64ToCertificateConverter))]
public X509Certificate2 Certificate { get; set; } = default!;
}