How to preserve HttpContext in Web API async task - nhibernate

I have an action in a Web API Controller that reads bytes async:
public HttpResponseMessage Post() {
var response = Request.CreateResponse(HttpStatusCode.Created);
var task = Request.Content.ReadAsByteArrayAsync().ContinueWith(t => {
DoSomething(t.Result);
});
task.Wait();
return response;
}
In my DoSomething method, I need access to HttpContext, e.g, using NHibernate's WebSessionContext. Unfortunately, HttpContext.Current is null.
I've learned I can use a closure to solve my problem:
var state = HttpContext.Current;
var task = Request.Content.ReadAsByteArrayAsync().ContinueWith(t => {
HttpContext.Current = state;
DoSomething(t.Result);
});
I wonder if there is a better way... shouldn't Web API have some extensions for this?

Try making your action asynchronous:
public async Task<HttpResponseMessage> Post()
{
byte[] t = await Request.Content.ReadAsByteArrayAsync();
DoSomething(t);
// You could safely use HttpContext.Current here
// even if this is a terribly bad practice to do.
// In a properly designed application you never need to access
// HttpContext.Current directly but rather work with the abstractions
// that the underlying framework is offering to you to access whatever
// information you are trying to access.
// Bear in mind that from reusability and unit restability point of view,
// code that relies on HttpContext.Current directly is garbage.
return Request.CreateResponse(HttpStatusCode.Created);
}

Related

Blazor WebAssembly Authentication and code meaning

I am following one article about Blazor WebAssembly Authentication.
https://code-maze.com/blazor-webassembly-authentication-aspnetcore-identity/
This is AuthenticationService.cs.
public async Task<AuthResponseDto> Login(UserForAuthenticationDto userForAuthentication)
{
var content = JsonSerializer.Serialize(userForAuthentication);
var bodyContent = new StringContent(content, Encoding.UTF8, "application/json");
var authResult = await _client.PostAsync("accounts/login", bodyContent);
var authContent = await authResult.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<AuthResponseDto>(authContent, _options);
if (!authResult.IsSuccessStatusCode)
return result;
await _localStorage.SetItemAsync("authToken", result.Token);
((AuthStateProvider)_authStateProvider).NotifyUserAuthentication(userForAuthentication.Email);
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", result.Token);
return new AuthResponseDto { IsAuthSuccessful = true };
}
public async Task Logout()
{
await _localStorage.RemoveItemAsync("authToken");
((AuthStateProvider)_authStateProvider).NotifyUserLogout();
_client.DefaultRequestHeaders.Authorization = null;
}
I lost my way in this part.
((AuthStateProvider)_authStateProvider).NotifyUserAuthentication(userForAuthentication.Email);
I can't get this code. Type casting? Type converting?
This code calls a method, NotifyUserAuthentication. But what is the front part's meaning?
Generally, I know ( ) in front of the variable is for casting.
But I don't get this what is this for, and what is this code meaning?
And why double used the same class AuthenticationStateProvider.
AuthStateProvider is inherited from AuthenticationStateProvider.
_authStateProvider is an instance of AuthenticationStateProvider.
Any help could be helpful for me.
Your AuthStateProvider DI service is registered as a AuthenticationStateProvider object, so in AuthenticationService that's what gets injected
private readonly AuthenticationStateProvider _authStateProvider;
public AuthenticationService(HttpClient client, AuthenticationStateProvider authStateProvider, ILocalStorageService localStorage)
NotifyUserLogout is an AuthStateProvider method, it's not implemented in it's parent AuthenticationStateProvider so you need to cast the object instance (which is actually a AuthStateProvider instance) to AuthStateProvider to be able to call the method.
Put in a break point and see what you have.

Custom Result in Net 6 Minimal API

In ASP.NET Core 5 I had a custom Action Result as follows:
public class ErrorResult : ActionResult {
private readonly IList<Error> _errors;
public ErrorResult(IList<Error> errors) {
_errors = errors;
}
public override async Task ExecuteResultAsync(ActionContext context) {
// Code that creates Response
await result.ExecuteResultAsync(context);
}
}
Then on a Controller action I would have:
return new ErrorResult(errors);
How to do something similar in NET 6 Minimal APIs?
I have been looking at it and I think I should implement IResult.
But I am not sure if that is the solution or how to do it.
I have recently been playing around with minimal APIs and and working on global exception handling. Here is what I have come up with so far.
Create a class implementation of IResult
Create a constructor which will take an argument of the details you want going into your IResult response. APIErrorDetails is a custom implementation of mine similar to what you'd see in ProblemDetails in MVC. Method implementation is open to whatever your requirements are.
public class ExceptionAllResult : IResult
{
private readonly ApiErrorDetails _details;
public ExceptionAllResult(ApiErrorDetails details)
{
_details = details;
}
public async Task ExecuteAsync(HttpContext httpContext)
{
var jsonDetails = JsonSerializer.Serialize(_details);
httpContext.Response.ContentType = MediaTypeNames.Application.Json;
httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(jsonDetails);
httpContext.Response.StatusCode = _details.StatusCode;
await httpContext.Response.WriteAsync(jsonDetails);
}
}
Return result in your exception handling middleware in your Program.cs file.
app.UseExceptionHandler(
x =>
{
x.Run(
async context =>
{
// https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-6.0
var exceptionFeature = context.Features.Get<IExceptionHandlerPathFeature>();
// Whatever you want for null handling
if (exceptionFeature is null) throw new Exception();
// My result service for creating my API details from the HTTP context and exception. This returns the Result class seen in the code snippet above
var result = resultService.GetErrorResponse(exceptionFeature.Error, context);
await result.ExecuteAsync(context); // returns the custom result
});
}
);
If you still want to use MVC (Model-View-Controller), you still can use Custom ActionResult.
If you just want to use Minimal APIs to do the response, then you have to implement IResult, Task<IResult> or ValueTask<IResult>.
app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
The following example uses the built-in result types to customize the response:
app.MapGet("/api/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound())
.Produces<Todo>(StatusCodes.Status200OK)
.Produces(StatusCodes.Status404NotFound);
You can find more IResult implementation samples here: https://github.com/dotnet/aspnetcore/tree/main/src/Http/Http.Results/src
Link: Minimal APIs overview | Microsoft Docs

Injecting scoped services into DelegatingHandler throws InvalidOperationException

I'm refactoring my code to use Refit for my calls to the WebApi service. The interface is set up and I also created a delegating handler:
public class AuthHandler : DelegatingHandler
{
private readonly TokenProvider tokenProvider;
private readonly ISessionStorageService sessionStorage;
public AuthHandler (
TokenProvider tokenProvider,
ISessionStorageService sessionStorage)
{
this.tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));
this.sessionStorage = sessionStorage ?? throw new ArgumentNullException(nameof(sessionStorage));
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken ct)
{
var someToken = await sessionStorage.GetItemAsync<string>("sometoken");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokenProvider.AccessToken);
request.Headers.Add("someToken", someToken);
return await base.SendAsync(request, ct).ConfigureAwait(false);
}
}
And in Startup.cs:
services.AddBlazoredSessionStorage();
services.AddScoped<TokenProvider>();
services.AddScoped<AuthHandler>();
services.AddRefitClient<IApiService>().ConfigureHttpClient(options =>
{
options.BaseAddress = new Uri(Configuration["Server:Url"]);
options.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}).AddHttpMessageHandler<AuthHandler>();
I have a razor component and I want to use the service above so I injected the services and did:
#code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var list = await myService.GetListAsync();
}
}
}
Also, in _Host.cshtml:
<environment include="Staging,Production">
<component render-mode="ServerPrerendered" type="typeof(App)" param-InitialState="tokens" />
</environment>
<environment include="Development">
<component render-mode="Server" type="typeof(App)" param-InitialState="tokens" />
</environment>
However I get the following exception:
System.InvalidOperationException: JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendered. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.
So, just to make sure if I have values or not I added the following before the call to the api:
var someToken = await sessionStorage.GetItemAsync<string>("sometoken");
var accessToken = tokenProvider.AccessToken;
And I DO have values in both variables.
So why can't I access the session storage from the delegating handler? And why is token provider instance instantiated but the properties all null (also in the handler)?
EDIT
I only need one place to keep my tokens. It doesn't matter if it's the token provider or the session storage, as long as it works in blazor pages/componenents and other services.
UPDATE 1
One can skip DI and create the service like this:
var service = RestService.For<IMyService>(new HttpClient(new AuthHandler(tokenProvider, sessionStorage))
{
BaseAddress = new Uri(myUrl)
}
);
This will work as expected. However, it would be much more better to use DI. The problem might either be in Blazor or in Refit.
I think I have found a working solution to the problem.
Instead of relying on the HttpClientFactory used by Refit, I created my own DI logic.
Previous:
Uri apiBaseUri = dataAccessLayerConfiguration.API.BaseUrl;
foreach (Type repositoryType in repositoryTypes)
{
services
.AddRefitClient(repositoryType)
.ConfigureHttpClient((httpClient) => httpClient.BaseAddress = apiBaseUri)
.AddHttpMessageHandler<RequestHeaderHandler>();
}
Current:
foreach (Type repositoryType in repositoryTypes)
{
services.AddTransient(
repositoryType,
(provider) =>
{
return
RepositoryFactory.Create(
repositoryType,
configuration,
provider.GetRequiredService<ITokenCacheProvider>(),
() => provider.GetRequiredService<ICurrentUser>());
});
}
The RepositoryFactory I use creates the HttpClients for each API interface:
return
RestService.For<TRepository>(
new HttpClient(
new RequestHeaderHandler(tokenCacheProvider, fetchCurrentUserDelegate)
{
InnerHandler = new HttpClientHandler()
},
true)
{
BaseAddress = configuration.DataAccessLayer.API.BaseUrl
});
I currently get the current user in the code of my layout in the OnParametersSet() method. I'm not completely satisfied with it yet, but for now it's enough for me. However, it is important not to inject the user object directly when creating HttpClient, but only a delegate (or a Func<>) which then resolves the user object if needed.
This way I was able to work around the scope issue of Refit/HttpClientFactory, but still continue to work with Dependency Injection. It may not be a one hundred percent solution for everyone, but it could be enough to possibly find the right direction for your own project.

Using Attribute and ActionFilters for logging request and response of controller and actions

I am trying to find an elegant way of logging every request and response in my Web API using Filters in Asp.net Core 3.1 rather than have them in each action and each controller.
Haven't found a nice solution that seems performable well to deploy in production.
I've been trying to do something like this (below) but no much success.
Any other suggestion would be appreciated.
public class LogFilter : IAsyncActionFilter
{
private readonly ILogger _logger;
public LogFilter(ILogger logger)
{
_logger = logger;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var requestBodyData = context.ActionArguments["request"];
var responseBodyData = "";//how to get the response result
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Request Body: {requestBodyData}");
await next();
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseBodyData}");
}
}
I think logging the response should be done in debugging mode only and really can be done at your service API (by using DI interception). That way you don't need to use IActionFilter which actually can provide you only a wrapper IActionResult which wraps the raw value from the action method (which is usually the result returned from your service API). Note that at the phase of action execution (starting & ending can be intercepted by using IActionFilter or IAsyncActionFilter), the HttpContext.Response may have not been fully written (because there are next phases that may write more data to it). So you cannot read the full response there. But here I suppose you mean reading the action result (later I'll show you how to read the actual full response body in a correct phase). When it comes to IActionResult, you have various kinds of IActionResult including custom ones. So it's hard to have a general solution to read the raw wrapped data (which may not even be exposed in some custom implementations). That means you need to target some specific well-known action results to handle it correctly. Here I introduce code to read JsonResult as an example:
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var requestBodyData = context.ActionArguments["request"];
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Request Body: {requestBodyData}");
var actionExecutedContext = await next();
var responseBodyData = "not supported result";
//sample for JsonResult
if(actionExecutedContext.Result is JsonResult jsonResult){
responseBodyData = JsonSerializer.Serialize(jsonResult.Value);
}
//check for other kinds of IActionResult if any ...
//...
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseBodyData}");
}
IActionResult has a method called ExecuteResultAsync which can trigger the next processing phase (result execution). That's when the action result is fully written to the HttpContext.Response. So you can try creating a dummy pipeline (starting with a dummy ActionContext) on which to execute the action result and get the final data written to the response body. However that's what I can imagine in theory. It would be very complicated to go that way. Instead you can just use a custom IResultFilter or IAsyncResultFilter to try getting the response body there. Now there is one issue, the default HttpContext.Response.Body is an HttpResponseStream which does not support reading & seeking at all (CanRead & CanSeek are false), we can only write to that kind of stream. So there is a hacky way to help us mock in a readable stream (such as MemoryStream) before running the code that executes the result. After that we swap out the readable stream and swap back the original HttpResponseStream in after copying data from the readable stream to that stream. Here is an extension method to help achieve that:
public static class ResponseBodyCloningHttpContextExtensions
{
public static async Task<Stream> CloneBodyAsync(this HttpContext context, Func<Task> writeBody)
{
var readableStream = new MemoryStream();
var originalBody = context.Response.Body;
context.Response.Body = readableStream;
try
{
await writeBody();
readableStream.Position = 0;
await readableStream.CopyToAsync(originalBody);
readableStream.Position = 0;
}
finally
{
context.Response.Body = originalBody;
}
return readableStream;
}
}
Now we can use that extension method in an IAsyncResultFilter like this:
//this logs the result only, to write the log entry for starting/beginning the action
//you can rely on the IAsyncActionFilter as how you use it.
public class LoggingAsyncResultFilterAttribute : Attribute, IAsyncResultFilter
{
//missing code to inject _logger here ...
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
var readableStream = await context.HttpContext.CloneBodyAsync(() => next());
//suppose the response body contains text-based content
using (var sr = new StreamReader(readableStream))
{
var responseText = await sr.ReadToEndAsync();
_logger.LogInformation($"{AppDomain.CurrentDomain.FriendlyName} Endpoint: {nameof(context.ActionDescriptor.DisplayName)} - Response Body: {responseText}");
}
}
}
You can also use an IAsyncResourceFilter instead, which can capture result written by IExceptionFilter. Or maybe the best, use an IAsyncAlwaysRunResultFilter which can capture the result in all cases.
I assume that you know how to register IAsyncActionFilter so you should know how to register IAsyncResultFilter as well as other kinds of filter. It's just the same.
starting with dotnet 6 asp has HTTP logging built in. Microsoft has taken into account redacting information and other important concepts that need to be considered when logging requests.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
/* enabled HttpLogging with this line */
app.UseHttpLogging();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
app.Run();
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-6.0#enabling-http-logging

Wrong Thread.CurrentPrincipal in async WCF end-method

I have a WCF service which has its Thread.CurrentPrincipal set in the ServiceConfiguration.ClaimsAuthorizationManager.
When I implement the service asynchronously like this:
public IAsyncResult BeginMethod1(AsyncCallback callback, object state)
{
// Audit log call (uses Thread.CurrentPrincipal)
var task = Task<int>.Factory.StartNew(this.WorkerFunction, state);
return task.ContinueWith(res => callback(task));
}
public string EndMethod1(IAsyncResult ar)
{
// Audit log result (uses Thread.CurrentPrincipal)
return ar.AsyncState as string;
}
private int WorkerFunction(object state)
{
// perform work
}
I find that the Thread.CurrentPrincipal is set to the correct ClaimsPrincipal in the Begin-method and also in the WorkerFunction, but in the End-method it's set to a GenericPrincipal.
I know I can enable ASP.NET compatibility for the service and use HttpContext.Current.User which has the correct principal in all methods, but I'd rather not do this.
Is there a way to force the Thread.CurrentPrincipal to the correct ClaimsPrincipal without turning on ASP.NET compatibility?
Starting with a summary of WCF extension points, you'll see the one that is expressly designed to solve your problem. It is called a CallContextInitializer. Take a look at this article which gives CallContextInitializer sample code.
If you make an ICallContextInitializer extension, you will be given control over both the BeginXXX thread context AND the EndXXX thread context. You are saying that the ClaimsAuthorizationManager has correctly established the user principal in your BeginXXX(...) method. In that case, you then make for yourself a custom ICallContextInitializer which either assigns or records the CurrentPrincipal, depending on whether it is handling your BeginXXX() or your EndXXX(). Something like:
public object BeforeInvoke(System.ServiceModel.InstanceContext instanceContext, System.ServiceModel.IClientChannel channel, System.ServiceModel.Channels.Message request){
object principal = null;
if (request.Properties.TryGetValue("userPrincipal", out principal))
{
//If we got here, it means we're about to call the EndXXX(...) method.
Thread.CurrentPrincipal = (IPrincipal)principal;
}
else
{
//If we got here, it means we're about to call the BeginXXX(...) method.
request.Properties["userPrincipal"] = Thread.CurrentPrincipal;
}
...
}
To clarify further, consider two cases. Suppose you implemented both an ICallContextInitializer and an IParameterInspector. Suppose that these hooks are expected to execute with a synchronous WCF service and with an async WCF service (which is your special case).
Below are the sequence of events and the explanation of what is happening:
Synchronous Case
ICallContextInitializer.BeforeInvoke();
IParemeterInspector.BeforeCall();
//...service executes...
IParameterInspector.AfterCall();
ICallContextInitializer.AfterInvoke();
Nothing surprising in the above code. But now look below at what happens with asynchronous service operations...
Asynchronous Case
ICallContextInitializer.BeforeInvoke(); //TryGetValue() fails, so this records the UserPrincipal.
IParameterInspector.BeforeCall();
//...Your BeginXXX() routine now executes...
ICallContextInitializer.AfterInvoke();
//...Now your Task async code executes (or finishes executing)...
ICallContextInitializercut.BeforeInvoke(); //TryGetValue succeeds, so this assigns the UserPrincipal.
//...Your EndXXX() routine now executes...
IParameterInspector.AfterCall();
ICallContextInitializer.AfterInvoke();
As you can see, the CallContextInitializer ensures you have opportunity to initialize values such as your CurrentPrincipal just before the EndXXX() routine runs. It therefore doesn't matter that the EndXXX() routine assuredly is executing on a different thread than did the BeginXXX() routine. And yes, the System.ServiceModel.Channels.Message object which is storing your user principal between Begin/End methods, is preserved and properly transmitted by WCF even though the thread changed.
Overall, this approach allows your EndXXX(IAsyncresult) to execute with the correct IPrincipal, without having to explicitly re-establish the CurrentPrincipal in the EndXXX() routine. And as with any WCF behavior, you can decide if this applies to individual operations, all operations on a contract, or all operations on an endpoint.
Not really the answer to my question, but an alternate approach of implementing the WCF service (in .NET 4.5) that does not exhibit the same issues with Thread.CurrentPrincipal.
public async Task<string> Method1()
{
// Audit log call (uses Thread.CurrentPrincipal)
try
{
return await Task.Factory.StartNew(() => this.WorkerFunction());
}
finally
{
// Audit log result (uses Thread.CurrentPrincipal)
}
}
private string WorkerFunction()
{
// perform work
return string.Empty;
}
The valid approach to this is to create an extension:
public class SLOperationContext : IExtension<OperationContext>
{
private readonly IDictionary<string, object> items;
private static ReaderWriterLockSlim _instanceLock = new ReaderWriterLockSlim();
private SLOperationContext()
{
items = new Dictionary<string, object>();
}
public IDictionary<string, object> Items
{
get { return items; }
}
public static SLOperationContext Current
{
get
{
SLOperationContext context = OperationContext.Current.Extensions.Find<SLOperationContext>();
if (context == null)
{
_instanceLock.EnterWriteLock();
context = new SLOperationContext();
OperationContext.Current.Extensions.Add(context);
_instanceLock.ExitWriteLock();
}
return context;
}
}
public void Attach(OperationContext owner) { }
public void Detach(OperationContext owner) { }
}
Now this extension is used as a container for objects that you want to persist between thread switching as OperationContext.Current will remain the same.
Now you can use this in BeginMethod1 to save current user:
SLOperationContext.Current.Items["Principal"] = OperationContext.Current.ClaimsPrincipal;
And then in EndMethod1 you can get the user by typing:
ClaimsPrincipal principal = SLOperationContext.Current.Items["Principal"];
EDIT (Another approach):
public IAsyncResult BeginMethod1(AsyncCallback callback, object state)
{
var task = Task.Factory.StartNew(this.WorkerFunction, state);
var ec = ExecutionContext.Capture();
return task.ContinueWith(res =>
ExecutionContext.Run(ec, (_) => callback(task), null));
}