ASP.NET Web API async controller method and deadlock - c#

Please help me to understand why this code cause a deadlock?
I have an asp.net web api application and I tried to make some controller method asynchronous.
[HttpPost]
[Authentication]
public async Task<SomeDTO> PostSomething([FromBody] SomeDTO someDTO)
{
return await _service.DoSomething(someDTO);
}
this is how looks the called service method:
public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
{
...
var someTask = Task.Run(() =>
{
var entity = new SomeEntity(someDTO);
return _repository.Create(entity);
});
...
var result = await someTask;
...
}
And there is some globalhandler, that prints a response to a console.
public class AppGlobalHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var resp = base.SendAsync(request, cancellationToken);
Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{resp?.ConfigureAwait(false).GetAwaiter().GetResult()?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult()}");
return resp;
}
}
Looks like ConfigureAwait(false).GetAwaiter().GetResult()
blocks the caller thread, but I supposed that ConfigureAwait(false) should avoid this, isn't it?

ConfigureAwait(false) would not help you here because it must be all the way down in the call stack (see more here) not at place where you wait synchronously, i.e. it depends rather on the implementation of base.SendAsync. If it acquired a lock on current thread it's too late to do something about it. It is also not recommended in ASP.net pipeline to continue responding on other thread after all (see discussion here and post here).
Finally it is always a highly risky idea to wait synchronously in async context.
If you need to read content, why not doing it like that:
public class AppGlobalHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var resp = await base.SendAsync(request, cancellationToken);
var content = resp?.Content != null
? (await resp.Content.ReadAsStringAsync())
: string.Empty;
Debug.WriteLine($"Response:{request.RequestUri}{Environment.NewLine}{content}");
return resp;
}
}

I think you overlook async keyword in Task.Run() method.
public async Task<SomeDTO> DoSomething(SomeDTO someDTO)
{
var someTask = Task.Run( async () => //simply add this for async run
{
var entity = new SomeEntity(someDTO);
return _repository.Create(entity);
});
var result = await someTask;
}

Related

Task.Wait always returns false although task finished

I'm using HttpClient trying to execute a POST method in Web API controller. The controller method is synchronous. I'm doing so this way:
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
After that I'm calling Wait:
var result = response.Wait(15000);
When running this code, I see the http finish executing, yet the result value is always false. What can I be missing?
Edit:
I now tried an async approach yet it didn't help me as well
public IHttpActionResult Add(Item item)
{
var result = _db.AddItem(item);
return Ok(result);
}
Test project:
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await
_owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
Method2(item).Continue with(task => {// Never reach here }
}
What am I doing wrong?
When debugging I see that the controller method returns a good response, yet it never reaches back to my test
You are mixing async and blocking calls (ie .Result, .Wait()) which are leading to a deadlock.
This looks more like it is a blocking issue on the test client side.
You need to make the test async all the way through in this case if you want to await on results from the server.
Convert test method to async
[TestMethod]
public async Task test1() {
//Arrange
Item item = new Item(...);
//Act
var preview = await Method2(item);
//Assert
Assert.IsNotNull(preview);
}
And update the methods to not mix async and blocking calls.
Method1 does not need asyn/await if it is not using the task after the call so it can be removed and just have the method return the Task that can be awaited
TestServer _owinTestServer;
public Task<HttpResponse> Method1(string url, object body) {
return _owinTestServer.HttpClient.PostAsJsonAsync(url, body);
}
Method2 needs to await the response from Method1 and then get its content.
public async Task<ItemPreview> Method2(object body) {
var response = await Method1("..", body );
return await response.Content.ReadAsAsync<ItemPreview>();
}
It doesn't matter if the controller method is synchronous or not. That is purely a concern on the server code. Since the PostAsJsonAsync method is asynchronous, you need to await it:
var response = await owin.HttpClient.PostAsJsonAsync(uri, body);
Which will allow your code to wait for the response from the server.
I assume your initial code looked something like this...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var result = response.Wait(15000);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
...
And yes, result will be false after waiting the 15 seconds. This is because you have setup the request, A.K.A response, but you haven't actually made the called and any response.Wait(n) will return false.
You just need to start the ReadAsAsync...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
However, I think you will find you can skip the response.Wait(n) all together, as it will return true because the ReadAsAsync() will wait to return or fail. If you're looking to configure the request timeout, you can do that in the HttpClient and your ReadAsAsync will throw an AggregateException with an InnerException of TaskCanceledException.
On a side note, you don't need to use owin.HttpClient you can just instantiate a new HttpClient. I believe the owin object you are referring to is for self hosting your WebApi, I don't know if that matters. But let's say you are calling Add(Item item) on your WebApi and that db.AddItem(item) will return and ItemPreview object, your code could look like this:
[TestMethod]
public void test1()
{
Item item = new(...);
var uri = "..";
var client = new HttpClient();
var response = client.PostAsJsonAsync(uri, item);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
/* The things to happen once you have item preview */
}
The result may always be false because the _db.AddItem is returning false all the time. If not, I've made a change in your code ideally which should work for you
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await _owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
await Method2(item).ContinueWith(task => {// Never reach here }
}
Since Method2 returns an Async Task, the ContinueWith will not wait for it to complete and hence it may required await on the method invocation.

Custom AuthorizeAttribute OnAuthorizationAsync Function

I'm trying to make my own custom System.Web.Http.AuthorizeAttribute. Everything was working until I needed to run some async function inside of the overridden OnAuthorizationfunction.
I found the OnAuthorizationAsync method and I use that, however still having issues running an async method. I need to ensure that a header attached to the HttpActionContext is in the db.
public override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues("Authentication", out values))
{
var token = new Token() { TokenString = values.First() };
if (await _tg.ValidateToken(token))
{
return base.OnAuthorizationAsync(actionContext, cancellationToken);
}
}
base.HandleUnauthorizedRequest(actionContext);
}
_tg.ValidateToken(token) is an async method that returns a bool. If I try to await it and make OnAuthorizationAsync an async method I then apparently cant return a Task:
Since OnAuthorizeAsync is an async method that returns 'Task', a return keyword must not be followed by an object expression.
Is there a way you can see out of this mess?
Because you use await, you need to add the async modifier to the method's declaration. Then change the return you have there to await. The compiler will do the rest - i.e. returning the Task for you.
Based on #sellotape's answer:
public async override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
IEnumerable<string> values;
if (actionContext.Request.Headers.TryGetValues("Authentication", out values))
{
var token = new Token() { TokenString = values.First() };
if (await _tg.ValidateToken(token))
{
return base.OnAuthorizationAsync(actionContext, cancellationToken);
}
}
await base.HandleUnauthorizedRequest(actionContext);
}

Result of a async task is blocking

I have an issue with a task blocking when I try to retrieve it's result.
I have the following piece of code I want executed synchronously (which is why I'm looking for the result)
I would ignore the reason each call has to be made (legacy software that requires multiple calls through different layers)
the call seems to break down after it starts the task for the final call to be made in the PostCreateProfile, I can see this request never makes it any further than this.
if (CreateProfile(demographics).Result) // Task blocks here
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}
The request will reach its destination if I was to change CreateProfile to an async void like so:
private async void CreateProfile(AppointmentController controller)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(controller);
await profileService.Create(createProfileBindingModel);
}
But I can't return the bool I want to use from this.
Can anyone point out what I am doing wrong?
You should never call .Result on a async/await chain.
Whatever code that calls CreateProfile(demographics) needs to be async too so it can do
if (await CreateProfile(demographics))
{
//dothing
}
Also, if you can you really should put .ConfigureAwait(false) wherever it is logically possible.
if (await CreateProfile(demographics).ConfigureAwait(false)) // depending on what dothing is you may not want it here.
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel).ConfigureAwait(false);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}

How do I ensure DelegatingHandlers throw exceptions on timeouts?

I'm using HttpClient's PostAnsyc method to synchronously call a REST API from service code invoked by my MVC application, but I'm losing exceptions in a DelegatingHandler.
The usage is synchronous. I am aware of the async path and it does not fit my use case.
Here are some variant's I've tried that didn't throw exceptions on timeout:
//controller action
[HttpPost]
public JsonResult Foo(int id)
{
try
{
var result = _businessService.Foo(id);
return Json(result, JsonRequestBehavior.DenyGet);
}
catch(Exception exception)
{
return Json(exception, JsonRequestBehavior.DenyGet);
}
}
//infrastructure code deep in my application
public HttpResponseMessage Post(Uri uri, StringContent content)
{
return _httpClient.PostAsync(uri, content).Result;
}
//DelegatingHandler code
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var taskCompletionSource = new TaskCompletionSource<HttpResponseMessage>();
base.SendAsync(request, cancellationToken)
.ContinueWith( t =>
{
if (t.IsFaulted)
{
if(t.Exception != null)
{
taskCompletionSource.TrySetException(t.Exception);
}
}
else if (t.IsCanceled)
{
taskCompletionSource.TrySetCanceled();
}
else
{
try
{
LogResponse(t.Result);
taskCompletionSource.SetResult(t.Result);
}
catch (Exception ex)
{
taskCompletionSource.TrySetException(ex);
}
}
}, cancellationToken);
return taskCompletionSource.Task;
}
How do I ensure that my DelegatingHandlers do not swallow exceptions during a timeout?
public async Task<HttpResponseMessage> PostAsync(Uri uri, StringContent content)
{
var cancellation = new CancellationTokenSource();
var task = _httpClient.PostAsync(uri, content, cancellation.Token);
var timeout = Task.Delay(5000);
await Task.WhenAny(task, timeout);
if(timeout.IsCompleted)
{
cancellation.Cancel();
throw new TimeoutException();
}
else
return await task;
}
This example would provide a timeout of 5 seconds before try to cancel the POST operation and throw a timeout exception.
Unfortunately the HttpClient has no synchronous methods, so whatever you do another thread pool thread is taking care of the request and you have to wait for it.
An alternative is to use the WebRequest, but it is less fancy and you have to serialize your payload yourself (which is not big deal with the NewtonSoft Json library)
It's pretty trivial to create a timeout using a CancellationTokenSource
public async Task<HttpResponseMessage> PostAsync(Uri uri, StringContent content)
{
var cancellation = new CancellationTokenSource(5000); // Cancel after 5 seconds.
return await _httpClient.PostAsync(uri, content, cancellation.Token);
}

WebAPI ActionFilter Await ExecuteActionFilterAsync No Result

I try to create a filter to modify the content. For some reason the
var result = await actionContext.Request.Content.ReadAsStringAsync(); does not wait and returns me empty values. I'm sure that there is data. Checked directly inside controller and header. Is there maybe a workaround. Can be blocking, too (HttpContent seems just to have async methods).
public class AsyncAttribute : FilterAttribute, IActionFilter
{
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
await InternalActionExecuting(actionContext, cancellationToken);
if (actionContext.Response != null)
{
return actionContext.Response;
}
HttpActionExecutedContext executedContext;
try
{
var response = await continuation();
executedContext = new HttpActionExecutedContext(actionContext, null)
{
Response = response
};
}
catch (Exception exception)
{
executedContext = new HttpActionExecutedContext(actionContext, exception);
}
await InternalActionExecuted(executedContext, cancellationToken);
return executedContext.Response;
}
public virtual async Task InternalActionExecuting(HttpActionContext actionContext, CancellationToken cancellationToken)
{
//////////////////////////////////////////////////////////////////////////
var result = await actionContext.Request.Content.ReadAsStringAsync();// <------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
}
public virtual async Task InternalActionExecuted(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
}
}
You should not be accessing the body in an action filter, if you have any thing else accessing the body in parameter binding it might be already gone.
Why are you calling actionExecuted inside the action executing overload? The pattern is one gets called before the action is run and one after.
For some reason it also looks like you are trying to implement the filter pipeline itself inside this method.
If you want to bind data from the body your recommended alternative is to use a formatter. Implement a MediaTypeFormatter and read what you need into a string. See one example
My wild bet here is that something else already read the body stream (and technically you are done at the point) and you can try and rewind it first (it will only work if you are in buffered mode).

Categories

Resources