mirror of
https://github.com/AlexMacocian/Net.Sdk.Web.Extensions.git
synced 2026-07-15 13:39:29 +00:00
228 lines
7.9 KiB
Markdown
228 lines
7.9 KiB
Markdown
# Net.Sdk.Web.Extensions
|
|
|
|
.NET library extending functionality for web applications
|
|
|
|
# Features
|
|
- [MVC Source Generators](#mvc-source-generators)
|
|
- [Improved Websocket Support](#better-websocket-support)
|
|
- [Improved request tracing](#request-tracing-using-correlationvectors)
|
|
- [Improved client IP handling](#client-ip-extraction)
|
|
- [Improved configuration binding](#better-configuration-binding-support)
|
|
- [Mockable IHttpClient builder](#mockable-httpclient)
|
|
- [Support for base64 encoded certificates in configuration](#base64-to-x509certificate2-converter-for-options)
|
|
|
|
## 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
|
|
1) Add the `Net.Sdk.Web.Extensions.SourceGenerators` package to your project
|
|
2) Create a controller class
|
|
```csharp
|
|
[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();
|
|
}
|
|
}
|
|
```
|
|
3) Register the routes in the `WebApplicationBuilder` using `WithRoutes()` extension. This extension is generated by the source generator and is available under the `Net.Sdk.Web` namespace in `WebApplicationBuilderExtensions` class.
|
|
```csharp
|
|
builder.WithRoutes();
|
|
```
|
|
|
|
4) Use the routes in your `WebApplication` class using `UseRoutes()` extension. This extension is generated by the source generator and is available under the `Net.Sdk.Web` namespace in `WebApplicationExtensions` class.
|
|
```csharp
|
|
app.UseRoutes();
|
|
```
|
|
|
|
### Functionality
|
|
- `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 type `Microsoft.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.
|
|
|
|
## 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`
|
|
```C#
|
|
var builder = WebApplication.CreateSlimBuilder(args);
|
|
builder.WithWebSocketRoute<TWebSocketRoute>();
|
|
var app = builder.Build();
|
|
app.UseWebSocketRoute<TWebSocketRoute>("custom-route");
|
|
```
|
|
|
|
### Message mapping
|
|
#### Define your request
|
|
```C#
|
|
[WebSocketConverter<JsonWebSocketMessageConverter<CustomRequest>, CustomRequest>]
|
|
public class CustomRequest{
|
|
}
|
|
```
|
|
|
|
#### Define your response
|
|
```C#
|
|
[WebSocketConverter<JsonWebSocketMessageConverter<CustomResponse>, CustomResponse>]
|
|
public class CustomResponse{
|
|
}
|
|
```
|
|
|
|
#### Implement your WebSocketRoute
|
|
```C#
|
|
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
|
|
```C#
|
|
[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`
|
|
```C#
|
|
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
|
|
```C#
|
|
var cv = accessor.HttpContext.GetCorrelationVector();
|
|
```
|
|
|
|
### Configuration
|
|
Configure an instance of `CorrelationVectorOptions` to adjust the CV header
|
|
|
|
### Requests and responses
|
|
- 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` under `CorrelationVector` key
|
|
- Log the CV to follow traces of one application flow under multiple operations and across http requests
|
|
|
|
### Integration with `HttpClient`
|
|
- Pass the `CorrelationVectorHandler` to the `IHttpClientBuilder` 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 the `HttpContext.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`
|
|
```C#
|
|
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
|
|
```C#
|
|
var cv = accessor.HttpContext.GetClientIP();
|
|
```
|
|
|
|
## Better configuration binding support
|
|
|
|
### Integration into `WebApplication`
|
|
```C#
|
|
var builder = WebApplication.CreateSlimBuilder(args);
|
|
builder.ConfigureExtended<CustomOptions>();
|
|
|
|
public class CustomService
|
|
{
|
|
public CustomService (IOptions<CustomOptions> options)
|
|
{
|
|
}
|
|
}
|
|
```
|
|
|
|
### Customizing configuration key
|
|
```C#
|
|
[OptionsName(Name = "CustomKey")]
|
|
public class CustomOptions
|
|
{
|
|
|
|
}
|
|
```
|
|
|
|
## Mockable HttpClient
|
|
### Integration into `WebApplication`
|
|
```C#
|
|
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
|
|
```C#
|
|
public class ServerOptions
|
|
{
|
|
[JsonConverter(typeof(Base64ToCertificateConverter))]
|
|
public X509Certificate2 Certificate { get; set; } = default!;
|
|
}
|
|
``` |