About ASP.NET Web API - Async and Await - c#

I have the following Asynchronous method inside my AsyncController:
public async Task<Dashboard> GetFeeds()
{
var movies = new HttpClient().GetStringAsync("http://netflix/api/MyMovies");
var tweets = new HttpClient().GetStringAsync("http://twitter/api/MyTweets");
await Task.WhenAll(movies, tweets);
Dashboard dash = new Dashboard();
dash.Movies = Deserialize<Movies >(movies.Result);
dash.Tweets = Deserialize<Tweets >(tweets.Result);
return dash;
}
In this method do different call APIs, one with different return time from each other. What I can not understand about Task<> is because I have to wait for the return of the two to update my client? Being that I'm creating new threads.
Imagining that I play the return of each API in a PartialView, the result I thought would get:
-First I would have my Movies list (it only takes 5s), -> Show for my user
-And Then would my list of Tweets -> Show for my user
But what I see is:
-While The Twitter request does not end I did not get to play the data I got from Netflix on-screen for my user.
The big question is: A Task<> serves only for the processing to be done faster?
I can not play the information on the screen according to the turnaround time of each API that I ordered?
This is the call to my method
public async Task<ActionResult> Index()
{
var feeds = await GetFeeds();
return View(feeds);
}
I confess, I'm very confused, or, maybe you did not understand the concept of Task<>.

The way ASP.NET MVC works is that a single controller action handles a single HTTP request, and produces a single HTTP response. This is true whether the action is synchronous or asynchronous.
In other words (as I explain on my blog), async doesn't change the HTTP protocol. To return an "initial result" to the client browser and update the page (or part of the page) with other data, you'll need to use a technology designed for that: AJAX, or SignalR.
For more information, see the "Asynchronous Code Is Not a Silver Bullet" section of my MSDN article on async ASP.NET.

Related

Possible to get response of each API calls inside ForEach without waiting to end of loop?

I need to return response of each API call inside ForEach loop without waiting for end of it.
First Api to get data from SharePoint and call another Api to update website database:
[HttpGet]
[Route("GetAllFaqs")]
public async Task<ApiResult<List<FaqModel>>> GetAllFaqs()
{
ApiSuccessResult<List<FaqModel>> faqs = await sharepointConnector.GetAllFaqsAsync();
foreach (var faq in faqs.Response)
{
// Here I want to return response of this Api call
// so I could show on client side one by one
await UpdateFaq(faq);
}
return faqs;
}
Another Api to update website database:
[HttpPost]
[Route("UpdateFaq")]
public async Task<ApiResult<FaqSyncResult>> UpdateFaq(FaqModel model)
{
ApiSuccessResult<FaqSyncResult> result = await websiteConnector.SyncFaq(model);
return result;
}
Is it possible to return response inside ForEach loop without stop it and also without waiting for the end?
This is usually not how it works.
There are 2 things which prevents you for doing this approach:
most javascript libraries used to fetch the data awaits the full response from the api
most javascript libraries used to process the data works with a full set of data
An alternative approach, to get this behaviour would be to push the data from the server to the client using HTTP2/websockets or SignalR.
I've implemented the SignalR variant quite a few times and it works really well. Here is an example on of chat application
The way it works, is basically sending data by a hub to the client (which is connected through the SignalR library) and call a piece of javascript with the appropriate data client side.
I must warn you however, this will break the "general-purpose" idea of the API because it relies heavily on a client accepting the data in this way.

MVC HttpClient multiple post requests, get mismatched responses

The scenario
I need to show N reports on a web page. The reports need to be requested to an external service. The time for the service to generate a report can vary from 2 seconds to 50 seconds, depending on the requested content.
To call the service I use HttpClient in an async action. To generate 1 report I call the service once. To generate 5 reports I call it 5 times and so on.
The Problem
Let's suppose we request 3 reports BigReport, MediumReport and SmallReport with a known relative generation time of 1 minute, 30 seconds and 2 seconds, and we call the service in the following order:
BigReport, MediumReport, SmallReport
The result of the HttpCalls will be as following:
HttpCall response for BigReport returns SmallReport (which is the quickest to be generated)
MediumReport will be correct
SmallReport response will contain the BigReport (which is the longest and the last)
Basicly, although the HttpCalls are different, for the fact they are made over a very short period of time, and they are still "active", the server will repond based on first arrived, first served, instead of serving each call with its exact response.
The Code
I have a Request controller with an async action like this:
public async Task<string> GenerateReport(string blockContent)
{
var formDataContent = new MultipartFormDataContent
{
AddStringContent(userid, "userid"),
AddStringContent(passcode, "passcode"),
AddStringContent(outputtype, "outputtype"),
AddStringContent(submit, "submit")
};
var blockStream = new StreamContent(new MemoryStream(Encoding.Default.GetBytes(blockContent)));
blockStream.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + filename + "\"");
formDataContent.Add(blockStream);
using (var client = new HttpClient())
{
using(var message = await client.PostAsync(Url, formDataContent))
{
var report = await message.Content.ReadAsStringAsync();
return report;
}
}
}
The action is being called from a view via Ajax, like this
//FOREACH BLOCK, CALL THE REPORT SERVICE
$('.block').each(function(index, block) {
var reportActionUrl = "Report/GenerateReport/"+block.Content;
//AJAX CALL GetReportAction
$(block).load(reportActionUrl);
});
Everything works fine if I covert the action from async to sync, by removing async Task and instead of "awaiting" for the response, I just get result as
var result = client.PostAsync(Url, formDataContent).Result.
This will make everything run synchronously and working fine, but the waiting time for the user, will be much longer. I would really like to avoid this by making parallel calls or similar.
Conclusions and questions
The problem itself make sense, after inspecting it also with Fiddler, as we have multiple opened HttpRequests pending almost simultaneously.
I suppose I need a sort of handler or something to identify and match request/response, but I don't know what's the name of the "domain" I need to look for. So far, my questions are:
What is the technical name of "making multiple http calls in parallel"?
If the problem is understandable, what is name of the problem? (concurrency, parallel requests queuing, etc..?)
And of course, is there any solution?
Many thanks.
With a "bit" of delay, I post the solution.
The problem was that the filename parameter was incorrectly called filename instead of blockname. This was causing the very weird behaviour, as a file could have had many blocks.
The lesson learned was that in case of very weird behaviour, in this case with a HttpClient call, analyse all the possible parameters and test it with different values, even if it doesn't make too much sense. At worst it can throw an error.

Web API allow only single async task

What is a proper scenario for handling only single async action? For example I need to import large file and while it being imported I need to disable that option to ensure that second import not triggered.
What comes in mind that:
[HttpPost]
public async Task<HttpResponseMessage> ImportConfigurationData()
{
if (HttpContext.Current.Application["ImportConfigurationDataInProcess"] as bool? ?? false)
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Task still running");
HttpContext.Current.Application["ImportConfigurationDataInProcess"] = true;
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
//actual import
HttpContext.Current.Application["ImportConfigurationDataInProcess"] = false;
Request.CreateResponse(HttpStatusCode.OK, true)
}
But it seems like very hard-coded solution. What is a proper way of handling that?
Another thing it is not properly works on client side at it still waits for a response. So is it possible for user just to send that file to server and not wait unlit it will finishes but reload page after file sent to server without waiting while await stuff will finish.
async does not change the HTTP protocol (as I explain on my blog). So you still just get one response per request.
The proper solution is to save a "token" (and import data) for the work in some reliable storage (e.g., Azure table/queue), and have a separate processing backend that does the actual import.
The ImportConfigurationData action would then check whether a token already exists for that data, and fault the request if found.

Is this fire and forget approach correct?

I've implemented instagram api realtime updates. Basically they fire a POST request to a url I provide when there are new images added based on my subscription.
They said:
" you should acknowledge the POST within a 2 second timeout--if you need to do more processing of the received information, you can do so in an asynchronous task."
so I built something like:
[HttpPost]
[ActionName("realtime")]
public async Task<ActionResult> IndexPost()
{
var form = Request.Form;
Request.InputStream.Position = 0;
System.IO.StreamReader str = new System.IO.StreamReader(Request.InputStream);
string sBuf = str.ReadToEnd();
// deserialize this from json
var serializer = new JavaScriptSerializer();
var updates = serializer.Deserialize<IEnumerable<RealtimeUpdate>>(sBuf).ToList();
ProcessNewTaggedImages(updates);
return new ContentResult { Content = "Ok" };
}
where ProcessNewTaggedImages is running async.
public async void ProcessNewTaggedImages(List<RealtimeUpdate> updates)
{
Task.Run(() =>
{
// query Instagram api for new images
}
}
so basically when Instagram POSTs to www.mysite.com/realtime it does not wait for ProcessNewTaggedImages.
I just wanted to make sure this approach is correct for fire and forget approach because under Task.Run I receive a warning saying:
Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the await operator to the result of the call.
but I don't want await here because
the result of my processing does not matter for instagram call
timeout for POST is 2 seconds so I don't want to wait for this processing.
Can you confirm I am on the right track?
Ps: POST is working fine and all works good just wanted to confirm I've not done any mystake because I am mostly beginner to this async approach in C#.
If you need fire and forget functionality you don't need to add async keywords to your methods as your are not doing any awaits. So remove the async keywords from your code and the compiler will not complain about your code.
I think you might need to read though this first.
And I quote: "If an async method doesn’t use an await operator to mark a suspension point, the method executes as a synchronous method does, despite the async modifier."

Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending

I'm writing an application that proxies some HTTP requests using the ASP.NET Web API and I am struggling to identify the source of an intermittent error.
It seems like a race condition... but I'm not entirely sure.
Before I go into detail here is the general communication flow of the application:
Client makes a HTTP request to Proxy 1.
Proxy 1 relays the contents of the HTTP request to Proxy 2
Proxy 2 relays the contents of the HTTP request to the Target Web Application
Target Web App responds to the HTTP request and the response is streamed (chunked transfer) to Proxy 2
Proxy 2 returns the response to Proxy 1 which in turn responds to the original calling Client.
The Proxy applications are written in ASP.NET Web API RTM using .NET 4.5.
The code to perform the relay looks like so:
//Controller entry point.
public HttpResponseMessage Post()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
private static HttpRequestMessage BuildRelayHttpRequest(HttpRequestMessage incomingRequest)
{
var requestUri = BuildRequestUri();
var relayRequest = new HttpRequestMessage(incomingRequest.Method, requestUri);
if (incomingRequest.Method != HttpMethod.Get && incomingRequest.Content != null)
{
relayRequest.Content = incomingRequest.Content;
}
//Copies all safe HTTP headers (mainly content) to the relay request
CopyHeaders(relayRequest, incomingRequest);
return relayRequest;
}
private static HttpRequestMessage BuildResponse(HttpResponseMessage responseMessage)
{
var returnMessage = Request.CreateResponse(responseMessage.StatusCode);
returnMessage.ReasonPhrase = responseMessage.ReasonPhrase;
returnMessage.Content = CopyContentStream(responseMessage);
//Copies all safe HTTP headers (mainly content) to the response
CopyHeaders(returnMessage, responseMessage);
}
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
var content = new PushStreamContent(async (stream, context, transport) =>
await sourceContent.Content.ReadAsStreamAsync()
.ContinueWith(t1 => t1.Result.CopyToAsync(stream)
.ContinueWith(t2 => stream.Dispose())));
return content;
}
The error that occurs intermittently is:
An asynchronous module or handler completed while an asynchronous operation was still pending.
This error usually occurs on the first few requests to the proxy applications after which the error is not seen again.
Visual Studio never catches the Exception when thrown.
But the error can be caught in the Global.asax Application_Error event.
Unfortunately the Exception has no Stack Trace.
The proxy applications are hosted in Azure Web Roles.
Any help identifying the culprit would be appreciated.
Your problem is a subtle one: the async lambda you're passing to PushStreamContent is being interpreted as an async void (because the PushStreamContent constructor only takes Actions as parameters). So there's a race condition between your module/handler completing and the completion of that async void lambda.
PostStreamContent detects the stream closing and treats that as the end of its Task (completing the module/handler), so you just need to be sure there's no async void methods that could still run after the stream is closed. async Task methods are OK, so this should fix it:
private static PushStreamContent CopyContentStream(HttpResponseMessage sourceContent)
{
Func<Stream, Task> copyStreamAsync = async stream =>
{
using (stream)
using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
{
await sourceStream.CopyToAsync(stream);
}
};
var content = new PushStreamContent(stream => { var _ = copyStreamAsync(stream); });
return content;
}
If you want your proxies to scale a bit better, I also recommend getting rid of all the Result calls:
//Controller entry point.
public async Task<HttpResponseMessage> PostAsync()
{
using (var client = new HttpClient())
{
var request = BuildRelayHttpRequest(this.Request);
//HttpCompletionOption.ResponseHeadersRead - so that I can start streaming the response as soon
//As it begins to filter in.
var relayResult = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
var returnMessage = BuildResponse(relayResult);
return returnMessage;
}
}
Your former code would block one thread for each request (until the headers are received); by using async all the way up to your controller level, you won't block a thread during that time.
I would like to add some wisdom for anyone else who landed here with the same error, but all of your code seems fine. Look for any lambda expressions passed into functions across the call-tree from where this occurs.
I was getting this error on a JavaScript JSON call to an MVC 5.x controller action. Everything I was doing up and down the stack was defined async Task and called using await.
However, using Visual Studio's "Set next statement" feature I systematically skipped over lines to determine which one caused it. I kept drilling down into local methods until I got to a call into an external NuGet package. The called method took an Action as a parameter and the lambda expression passed in for this Action was preceded by the async keyword. As Stephen Cleary points out above in his answer, this is treated as an async void, which MVC does not like. Luckily said package had *Async versions of the same methods. Switching to using those, along with some downstream calls to the same package fixed the problem.
I realize this is not a novel solution to the problem, but I passed over this thread a few times in my searches trying to resolve the issue because I thought I didn't have any async void or async <Action> calls, and I wanted to help someone else avoid that.
A slightly simpler model is that you can actually just use the HttpContents directly and pass them around inside the relay. I just uploaded a sample illustrating how you can rely both requests and responses asynchronously and without buffering the content in a relatively simple manner:
http://aspnet.codeplex.com/SourceControl/changeset/view/7ce67a547fd0#Samples/WebApi/RelaySample/ReadMe.txt
It is also beneficial to reuse the same HttpClient instance as this allows you to reuse connections where appropriate.

Categories