7.8 KiB
Net.Sdk.Web.Extensions
.NET library extending functionality for web applications
Features
- 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
MVC Source Generators
Net.Sdk.Web.Extensions provides a set of source generators to replicate the MVC functionality of Asp while being compatible with NativeAOT compilation.
Usage
- Add the
Net.Sdk.Web.Extensions.SourceGeneratorspackage 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
WebApplicationBuilderusingWithRoutes()extension. This extension is generated by the source generator and is available under theNet.Sdk.Webnamespace inWebApplicationBuilderExtensionsclass.
builder.WithRoutes();
- Use the routes in your
WebApplicationclass usingUseRoutes()extension. This extension is generated by the source generator and is available under theNet.Sdk.Webnamespace inWebApplicationExtensionsclass.
app.UseRoutes();
Functionality
GenerateControllerattribute allows you to define a controller class and its route.GenerateGet,GeneratePost,GeneratePut,GenerateDeleteattributes allow you to define the HTTP methods and their routes.FromRoute,FromBody,FromQueryattributes allow you to define the parameters of the methods.RouteFilterattribute allows you to define the filters for the methods. Filters must be of typeMicrosoft.AspNetCore.Http.IEndpointFilter.HttpContextcan 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.
Better WebSocket support
Net.Sdk.Web.Extensions provides a streamlined implementation of WebSockets to be used in a web app, in a syntax similar to Asp Mvc
Integration into WebApplication
var builder = WebApplication.CreateSlimBuilder(args);
var app = builder.Build();
app.MapWebSocket<CustomRoute>("custom-route");
Message mapping
Define your request
[WebSocketConverter<JsonWebSocketMessageConverter<CustomRequest>, CustomRequest>]
public class CustomRequest{
}
Define your response
[WebSocketConverter<JsonWebSocketMessageConverter<CustomResponse>, CustomResponse>]
public class CustomResponse{
}
Implement your WebSocketRoute
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);
}
}
Support for IActionFilter
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>
Custom message converters
If you need specialized converters, implement your own WebSocketMessageConverter<T>
Request tracing using CorrelationVectors
Integration into WebApplication
var builder = WebApplication.CreateSlimBuilder(args);
builder.WithCorrelationVector();
var app = builder.Build();
app.UseCorrelationVector();
Accessing the correlation vector
Take a dependency on IHttpContextAccessor and get the CV using the HttpContextExtensions.GetCorrelationVector method
var cv = accessor.HttpContext.GetCorrelationVector();
Configuration
Configure an instance of CorrelationVectorOptions to adjust the CV header
Requests and responses
- On a request, the
CorrelationVectorMiddlewareretrieves the CV from the request header if present, otherwise it creates a new one - The CV is stored in
HttpContext.ItemsunderCorrelationVectorkey - Log the CV to follow traces of one application flow under multiple operations and across http requests
Integration with HttpClient
- Pass the
CorrelationVectorHandlerto theIHttpClientBuilderto manage CVs across requests - On each
HttpClientrequest, the CV is added to the configured header - After receiving the response, the
CorrelationVectorHandlerwill receive and parse any existing CV from the headers and reapply it to theHttpContext.Items
Client IP extraction
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
Integration into WebApplication
var builder = WebApplication.CreateSlimBuilder(args);
builder.WithIPExtraction();
var app = builder.Build();
app.UseIPExtraction();
Accessing the client IP
Take a dependency on IHttpContextAccessor and get the CV using the HttpContextExtensions.GetCorrelationVector method
var cv = accessor.HttpContext.GetClientIP();
Better configuration binding support
Integration into WebApplication
var builder = WebApplication.CreateSlimBuilder(args);
builder.ConfigureExtended<CustomOptions>();
public class CustomService
{
public CustomService (IOptions<CustomOptions> options)
{
}
}
Customizing configuration key
[OptionsName(Name = "CustomKey")]
public class CustomOptions
{
}
Mockable HttpClient
Integration into WebApplication
var builder = WebApplication.CreateSlimBuilder(args);
builder.RegisterHttpClient<CustomService>()
.WithTimeout(TimeSpan.FromSeconds(5))
.WithCorrelationVector()
.CreateBuilder();
public class CustomService
{
public CustomService (IHttpClient<CustomService> client)
{
}
}
Base64 to X509Certificate2 converter for options
Use the Base64ToCertificateConverter to retrieve your SSL certificate from configuration and bind it to options, to allow for dynamic loading of certificates
Use Base64ToCertificateConverter on your X509Certificate2 property
public class ServerOptions
{
[JsonConverter(typeof(Base64ToCertificateConverter))]
public X509Certificate2 Certificate { get; set; } = default!;
}