How to asynchronously make a web request without a callback - c#

I have a service that talks to a 3rd party on the internet, if i want this to scale i don't want my threads to be blocked waiting for the response when they could be handling new requests. One way around this is to use the httpRequest.BeginGetResponse method and pass in a callback. The issue with this is this method returns immediately and i have nothing to give back to the caller of my service. Once in the callback i cant do anything useful.
What i would like is something like this
public string CallServiceNonBlocking(string url) {
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
HttpResponse res = httpRequest.FetchNonBlocking(); //this does the work but my thread should be returned to the asp.net thread pool to do other work rather than blocking here
return res.GetValue();
}
Note i don't have .Net 4.5 available to me only .NET 4. But for reference can i see a solution for both versions?

Coding this with .NET 4.5 and C# 5.0 async/await is very easy.
You can make it work asynchronously with .NET 4.0 / C# 4.0 too, if you have to. You'd need to derive your controller from AsyncController and use AsyncManager. The basic steps are described in "Using an Asynchronous Controller in ASP.NET MVC".
In this light, your code may look like this (untested):
static public Task<WebResponse> GetResponseTapAsync(this WebRequest request)
{
return Task.Factory.FromAsync(
(asyncCallback, state) =>
request.BeginGetResponse(asyncCallback, state),
(asyncResult) =>
request.EndGetResponse(asyncResult), null);
}
// ...
public class YourController : AsyncController
{
public void YourMethodAsyc(string url)
{
AsyncManager.OutstandingOperations.Increment();
var request = (HttpWebRequest)WebRequest.Create(url);
request.GetResponseTapAsync().ContinueWith(responseTask =>
{
try
{
var stream = responseTask.Result.GetResponseStream();
using (var streamReader = new StreamReader(stream))
{
// still blocking here, see notes below
var data = streamReader.ReadToEnd();
AsyncManager.Parameters["data"] = data;
}
}
finally
{
AsyncManager.OutstandingOperations.Decrement();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
public ActionResult YourMethodCompleted(string data)
{
return View("Data", new ViewModel
{
Data = data
});
}
}
You could take it further and implement ReadToEndAsync (as it is absent in .NET 4.0), but you won't be able to use using. Check this for some more details.
Ideally, if you need to target ASP.NET MVC with .NET 4.0, but develop with VS2012+ in C# 5.0, you still can use async/await, Microsoft provides Microsoft.Bcl.Async library for that. Then your code might look like this:
public class YourController : AsyncController
{
async Task<string> YourMethodAsyncImpl(string url)
{
var request = (HttpWebRequest)WebRequest.Create(url);
using (var response = await request.GetResponseAsync()
using (var streamReader = new StreamReader(response.GetResponseStream())
return await streamReader.ReadToEndAsync();
}
public void YourMethodAsyc(string url)
{
AsyncManager.OutstandingOperations.Increment();
YourMethodAsyncImpl(url).ContinueWith(resultTask =>
{
try
{
AsyncManager.Parameters["data"] = resultTask.Result;
}
finally
{
AsyncManager.OutstandingOperations.Decrement();
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
public ActionResult YourMethodCompleted(string data)
{
return View("Data", new ViewModel
{
Data = data
});
}
}

I'd use Microsoft's Reactive Framework for this. (NuGet "Rx-Main")
It's then super easy.
Define your function like this:
public IObservable<string> CallServiceNonBlocking(string url)
{
return Observable.Start(() =>
{
var httpRequest = (HttpWebRequest)WebRequest.Create(url);
HttpResponse res = httpRequest.FetchBlocking();
return res.GetValue();
});
}
Then call it like this:
CallServiceNonBlocking(url)
.Subscribe(response =>
{
/* handle response here */
});
It's easy to use an ObserveOn to make the subscription run on the UI thread, or current context, etc.

Related

Polly waits the thread for an indefinite amount of time on .NET Framework 4.6.1 when called from a Web API project

.NET Version: .NET Framework 4.6.1
Polly Version: 7.2.2
On .NET Framework 4.6.1 when using a Web API project Polly will wait the thread the request is running in for an indefinite amount of time, causing there to never be a response back to the client that called it. Calling the same method from a console app will work just fine.
This was tested using a freshly created solution in Visual Studio 'ASP.NET Web Application (.NET Framework)'.
I also tried this same code in .NET 5 and this issue is not present, it only happens on .NET Framework 4.6.1.
Code to reproduce the problem:
PolicyContainer.cs:
public class PolicyContainer
{
public IAsyncPolicy<HttpResponseMessage> CircutBreakerPolicy { get; set; }
public PolicyContainer()
{
SetCircutBreakerPolicy();
}
private void SetCircutBreakerPolicy()
{
//////////////////////////////////////////////////////////////////////////////////////
// Normally these values would be set by a config file, hardcoded for this example. //
//////////////////////////////////////////////////////////////////////////////////////
// 0.5 means 50% of requests must fail before the circut breaks
double failureThreshold = 0.5;
// 60 means only the most recent 60 seconds are considered for breaking the circut
double samplingDuration = 60;
// 10 means at least this many calls must pass through the circut within the samplingDuration before breaking the circut
int minimumThroughput = 10;
// 60 means the circut will be broken for 60 seconds after the threshold is met
double durationOfBreak = 60;
CircutBreakerPolicy = Policy.HandleResult<HttpResponseMessage>(result => !result.IsSuccessStatusCode)
.AdvancedCircuitBreakerAsync(failureThreshold,
TimeSpan.FromSeconds(samplingDuration),
minimumThroughput,
TimeSpan.FromSeconds(durationOfBreak),
OnBreak,
OnReset,
OnHalfOpen);
}
private void OnBreak(DelegateResult<HttpResponseMessage> response, TimeSpan timespan, Context context)
{
Console.WriteLine("Circut Broken");
}
private void OnReset(Context context)
{
Console.WriteLine("Circut Reset");
}
private void OnHalfOpen()
{
Console.WriteLine("Circut Half-Open");
}
}
PollyTestRequest.cs:
public class PollyTestRequest
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// If set to true the Web API will never return a response, though any other type of project works fine. //
///////////////////////////////////////////////////////////////////////////////////////////////////////////
private const bool USE_POLLY = true;
public static async Task<HttpResponseMessage> Send()
{
HttpClient httpClient = new HttpClient();
PolicyContainer policyContainer = new PolicyContainer();
HttpResponseMessage response;
if (USE_POLLY)
{
// Does not work in a Web API application.
// I stepped through the decompiled code this calls and it will arrive at a "public static bool Wait(object obj, int millisecondsTimeout, bool exitContext)" method.
// Inside this method there is a call to "ObjWait(exitContext, millisecondsTimeout, obj)", however the debugger will not decompile this method so the debugging session will stop if you try to step into it.
// The 'millisecondsTimeout' variable passed here will be "-1" and the 'exitContext' will be "null". I believe that this is what is hanging the thread indefinitely.
// Its very strange though, calling this from a Console app, it will work fine, but from a Web API application it will hang indefinitely.
response = await policyContainer.CircutBreakerPolicy.ExecuteAsync(
async token => await httpClient.PostAsync(new Uri("http://example.com"), new StringContent(""), token),
CancellationToken.None
);
}
else
{
// Works perfectly fine in both Web API and Console Apps
response = await httpClient.PostAsync(new Uri("http://example.com"), new StringContent("")).ConfigureAwait(false);
}
return response;
}
}
TestController.cs:
[Route("[controller]")]
public class TestController : ApiController
{
[HttpGet]
[Route("testRoute")]
public IHttpActionResult TestGetRoute()
{
var response = PollyTestRequest.Send().Result;
if (response.IsSuccessStatusCode)
{
return Ok();
}
else
{
return new StatusCodeResult(HttpStatusCode.InternalServerError, this);
}
}
}
Here's your bug:
var response = PollyTestRequest.Send().Result;
Don't block on async code; in situations like ASP.NET (pre-Core), it can cause a deadlock.
The proper fix is to use async all the way:
public async Task<IHttpActionResult> TestGetRoute()
{
var response = await PollyTestRequest.Send();
...
}

Why can't I use HttpClient for Syncrhonous calls from ASP.Net?

I'm using a client library for accessing a 3rd party API. The library was generated by NSwagStudio from Swagger documentation.
The app I'm working on is entirely synchronous in all its calls and updating it to be async is out of scope of what I'm working on.
When I test the client library from a unit test, it works fine. When I try to call it from within an ASP.Net app, I get the following error:
The CancellationTokenSource has been disposed.
I've distilled the client library down to the essentials for demonstrating the problem, I selected an option to provide sync methods as well as async:
public class ClientApi
{
private readonly HttpClient _httpClient;
public ClientApi(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string BaseUrl { get; set; }
public object Get()
{
return Task.Run(async () => await GetAsync(CancellationToken.None)).GetAwaiter().GetResult();
}
/// <returns>OK</returns>
/// <param name="cancellationToken">
/// A cancellation token that can be used by other objects or threads to receive notice of
/// cancellation.
/// </param>
public async Task<string> GetAsync(CancellationToken cancellationToken)
{
var client_ = _httpClient;
try
{
using (var request_ = new HttpRequestMessage())
{
request_.Method = new HttpMethod("GET");
request_.RequestUri = new System.Uri(BaseUrl, System.UriKind.RelativeOrAbsolute);
var response_ = await client_.SendAsync(
request_,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
).ConfigureAwait(false);
try
{
// Exception occurs on following line
var responseData_ = response_.Content == null
? null
: await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
return responseData_;
}
finally
{
response_?.Dispose();
}
}
}
finally { }
}
}
Here's the code that calls it:
protected void OnClick(object sender, EventArgs e)
{
var httpClient = new HttpClient();
var client = new ClientApi(httpClient)
{
BaseUrl = "https://www.google.com"
};
var html = client.Get();
}
The code calling this is just an asp.net page with a button, and the button events runs the same code as the unit test that passes.
When I compare the runs in a debugger: from a unit test, the response_.Content object does not have a cancellation token, however when run from asp.net it does. In fact they almost seem to be different objects, despite the fact GetType() reports them both as being System.Net.Http.StreamContent. From decompiling the class, this doesn't have a _cancellationtoken property, so where is the debugger getting it from?
I'm guessing that the http request to my asp.net web app has it's own token and source, that is somehow getting used by the HttpClient. However, the client is awaiting all the async calls to get the result synchronously, so I don't understand how the underlying CTS could be disposed as we haven't returned from the call the client library yet.
Can anyone understand what's happening and is there a resolution?
First of, you should really rethink of rewriting your client app so you can implement async all the way.
“Async all the way” means that you shouldn’t mix synchronous and
asynchronous code without carefully considering the consequences. In
particular, it’s usually a bad idea to block on async code by calling
Task.Wait or Task.Result.
Taken from this great guide.
Basicaly, by running async code sync you will allways do things wrong.
But if you really need one solution, start by wrapping your disposable objects in using statements instead of manually disposing them.
Here's a simplified solutions of your ClientApi class which does what you need(But it can deadlock). The code is basically the same as in this answer.
public class ClientApi
{
public object Get(string url)
{
using (var client = new HttpClient())
{
var response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
return responseContent.ReadAsStringAsync().Result;
}
}
}
}
Read more about deadlock here

Hung returning Follows data from Relationships endpoint

I'm trying to return a list of followed users from the Instagram API. I'm on a sandbox account using the InstaSharp wrapper for .NET.
The action method is being called after user is authenticated.
public ActionResult Following()
{
var oAuthResponse = Session["InstaSharp.AuthInfo"] as OAuthResponse;
if (oAuthResponse == null)
{
return RedirectToAction("Login");
}
var info = new InstaSharp.Endpoints.Relationships(config_, oAuthResponse);
var following = info.Follows("10").Result;
return View(following.Data);
}
Try making the method async all the way through instead of making the blocking call .Result which runs the risk of causing a deadlock
public async Task<ActionResult> Following() {
var oAuthResponse = Session["InstaSharp.AuthInfo"] as OAuthResponse;
if (oAuthResponse == null) {
return RedirectToAction("Login");
}
var info = new InstaSharp.Endpoints.Relationships(config_, oAuthResponse);
var following = await info.Follows("10");
return View(following.Data);
}
depending on how info.Follows was implemented.
Looking at the Github repo, the API internally makes a call to a method defined like this
public static async Task<T> ExecuteAsync<T>(this HttpClient client, HttpRequestMessage request)
Which looks like your smoking gun as calling .Result higher up the call stack on this task would result in your experienced deadlock.
Reference Async/Await - Best Practices in Asynchronous Programming

Invoke Async Funcs

I've made a nice controller helper in my MVC project that allows me to return similarly structured result payloads when I make any ajax request. It looks like this:
public static JsonNetResult Responsify<T>(Func<T> func)
{
AjaxResponse response;
var result = new JsonNetResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
try
{
var resp = func();
response = AjaxResponse.SuccessResponse(resp);
result.Data = response;
}
catch (Exception e)
{
response = AjaxResponse.ErrorResponse(e.Message);
result.Data = response;
}
return result;
}
and it gets used like this:
[HttpPost]
public JsonNetResult Link(LinkInstruction instruction)
{
return ControllerHelper.Responsify(() => _linkHandler.Link(instruction));
}
where _linkHandler.Link is not async.
My issue is I have some async handlers that I would like to invoke using this pattern and I can't figure out how to await the function. I've tried Task.Factory.StartNew as well as wrapping the lambda in async/await and I can't quite figure it out.
Create a version of Responsify - call it ResponsifyAsync - that specifies that the passed Func must return a Task<T> (therefore meaning you can await it):
public async static JsonNetResult ResponsifyAsync<T>(Func<Task<T>> func)
{
...
}
This would do much what your current function does, but would await func() instead of just executing it.
You must then pass a Func delegate that returns a Task to this method. When calling an async library in the delegate, since the async method you're calling already returns a Task, this will simply take the form:
var x = await ResponsifyAsync(() => _someClass.MyMethodAsync(someParameter));
Or, if the method is parameterless...
var x = await ResponsifyAsync(_someclass.MyMethodAsync);
I'll let you flesh out the method but this format should set you off on the right lines.

progressivelly updating model in asp.net mvc

I have a long db networking call and I want to populate my model in chunks. We are talking about asp.net MVC.
I have a vague idea that each time a new chunk is available I should trigger the model.Bind()
but I don't know how to do the plumbing between
a) the service which is providing the data in chunks- it's implemented using the event pattern- each time a new chunk is available an event is triggered, but which event ? It should hold a reference to the model?
b) the data which will be bound to the model ( i suppose it should not be an bind(), but an addition to some collection)
c) if everything is ok in steps a and b, then the changes will be propagated to the view without further a do?
You could use long polling with a hidden iframe and chunked transfer encoding from the server which will spit <script> tags as data becomes available. In this script tag you could invoke a custom callback javascript function that will take care to format the results.
UPDATE:
As requested in the comments section here's a sample implementation of a long polling technique using a hidden iframe.
Let's suppose that you have some model:
public class MyViewModel
{
public string Foo { get; set; }
}
and that you have a service that returns this model in chunks and notifies the caller that a chunk is available using events:
public class MyService
{
public void GetModels(Action<MyViewModel, object> onModelAvailable, object state, Action onComplete)
{
Task.Factory.StartNew(x =>
{
try
{
for (int i = 0; i < 10; i++)
{
onModelAvailable(new MyViewModel
{
Foo = "foo " + i
}, x);
Thread.Sleep(1000);
}
}
finally
{
onComplete();
}
}, state);
}
}
Now, we could have the following controller:
public class HomeController : AsyncController
{
public ActionResult Index()
{
return View();
}
public ActionResult LongPoll()
{
var service = new MyService();
return new MyActionResult(service);
}
}
and the following view:
<script type="text/javascript">
// we define a callback function which will be invoked
// when a chunk is available from the server
var callback = function (model) {
// the model variable passed here will represent the chunk
$($('<div/>', {
html: model.Foo
})).appendTo('#result');
};
</script>
<iframe style="display:none;" src="#Url.Action("longpoll")"></iframe>
<div id="result"></div>
Now the last part of course is the implementation of the custom action result which will do the chunked transfer:
public class MyActionResult : ActionResult
{
private readonly MyService _service;
public MyActionResult(MyService service)
{
_service = service;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.BufferOutput = true;
response.ContentType = "text/html";
var wait = new ManualResetEvent(false);
_service.GetModels((model, state) =>
{
var httpResponse = (HttpResponseBase)state;
httpResponse.BufferOutput = true;
httpResponse.ContentType = "text/html";
var serializer = new JavaScriptSerializer();
var script = string.Format(
"<script type=\"text/javascript\">window.parent.callback({0});</script>",
serializer.Serialize(model)
);
httpResponse.Write(script);
httpResponse.Flush();
},
response,
() =>
{
wait.Set();
});
wait.WaitOne();
}
}
The simplest solution is to use polling, just some ajax call every n-seconds to check if new data is available. Downsides to this approach: latency, server load. Advantages: rather simple to implement.
A better but much more involved solution is to use something like long-polling, web-sockets, etc.. If this feature is worth the trouble then take a look at Signal-R, which is an async signaling library for ASP.NET to help you build real-time, multi-user interactive web applications. Adding it to an ASP.NET MVC 3 web application is very straightforward. This is a good intro to the library: Asynchronous Scalable Web Applications With Realtime Persistent Long running Connections With SignalR

Categories