232

I am building a class library to interact with an API. I need to call the API and process the XML response. I can see the benefits of using HttpClient for Asynchronous connectivity, but what I am doing is purely synchronous, so I cannot see any significant benefit over using HttpWebRequest.

If anyone can shed any light I would greatly appreciate it. I am not one for using new technology for the sake of it.

Jakub Šturc
  • 35,201
  • 25
  • 90
  • 110
Ketchup
  • 3,071
  • 4
  • 20
  • 23
  • 6
    I hate to tell you, but a call over HTTP is never purely synchronous due to how windows networking internally works (a.k.a. completion ports). – TomTom Mar 27 '16 at 05:45
  • 3
    Related posts - [What's the “right way” to use HttpClient synchronously?](https://stackoverflow.com/q/53529061/465053), [Calling async method synchronously](https://stackoverflow.com/q/22628087/465053), [How would I run an async Task method synchronously?](https://stackoverflow.com/q/5095183/465053) & [When correctly use Task.Run and when just async-await](https://stackoverflow.com/q/18013523/465053) – RBT Aug 28 '19 at 07:35
  • 1
    Also, it is good to know - [Effectively use async/await with ASP.NET Web API](https://stackoverflow.com/q/31185072/465053) – RBT Aug 28 '19 at 07:36

7 Answers7

424

but what i am doing is purely synchronous

You could use HttpClient for synchronous requests just fine:

using (var client = new HttpClient())
{
    var response = client.GetAsync("http://google.com").Result;

    if (response.IsSuccessStatusCode)
    {
        var responseContent = response.Content; 

        // by calling .Result you are synchronously reading the result
        string responseString = responseContent.ReadAsStringAsync().Result;

        Console.WriteLine(responseString);
    }
}

As far as why you should use HttpClient over WebRequest is concerned, well, HttpClient is the new kid on the block and could contain improvements over the old client.

Amirhossein Mehrvarzi
  • 18,024
  • 7
  • 45
  • 70
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 34
    Wouldn't your synchronous use of the async methods potentially block your UI thread? You may want to consider something like `string responseString = Task.Run(() => responseContent.ReadAsStringAsync()).Result;` instead if you _must_ make this synchronous. – earthling Feb 26 '14 at 19:44
  • @earthling, yes, my solution will block the UI thread. But what I don't understand is how yours is different in this regard? You are invoking the `.Result` on a Task that you started on the main UI thread. Wouldn't that block this thread? – Darin Dimitrov Feb 26 '14 at 22:13
  • 1
    in your case, the task in running in a context with a limited number of available threads. If this is the UI thread, that number = 1. Using Task.Run invokes the "task" from the ThreadPool. At least this is how I understand it all works. I'm still learning of course, but I just implemented this in custom `IValueConverter` that needed to run synchronously and it appears to work. – earthling Feb 26 '14 at 22:29
  • 13
    @earthling, yes, `Task.Run` invokes the task from a ThreadPool, but you are calling `.Result` on it killing all the benefits from this and blocking the thread in which you called this `.Result` (which usually happens to be the main UI thread). – Darin Dimitrov Feb 27 '14 at 21:55
  • 42
    According to this post (http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx) calling `.Result` like this can exhaust the threadpool and cause deadlock. – Pete Garafano Dec 24 '14 at 14:36
  • is it possible to create two HttpClients and having them both requesting at the same time? I mean..(example) how can I have a download running and still be able to make requests to a server? – nhenrique May 19 '15 at 11:25
  • 19
    This code will always deadlock if executed within a task created on the main UI thread by a `new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()` – Wim Coenen Jun 02 '15 at 13:00
  • @nhenrique you asked a while ago but you don't even need multiple clients, you can use a single client. Instead of invoking `await` or `.GetAwaiter().GetResult()` immediately you `await Task.WaitAll(req1, req2)` – Chris Marisic Dec 08 '15 at 23:17
  • 4
    @DarinDimitrov shouldn't this be `var response = client.GetAsync("http://google.com").GetAwaiter().GetResult();` and `string responseString = responseContent.ReadAsStringAsync().GetAwaiter().GetResult();` – Chris Marisic Dec 08 '15 at 23:19
  • @DarinDimitrov: Is it possible to do same for `PutAsync` method? I am struggling to implement `PutAsync` synchronously – huMpty duMpty Dec 06 '16 at 11:33
  • 34
    So, how do I use HttpClient synchronously from UI thread? Let's say I **purposely** want to block UI thread (or I am writing a console app) until I get the HTTP response... So, if Wait(), Result(), etc can cause deadlocks, what's the definite solution for this **without** the risk of deadlock and **without** using other classes like WebClient? – Dexter Feb 06 '17 at 15:38
  • This is not Sync . Actually with httpclient it is assumed that webservice takes time and you wait for response and have async calls instead of blocking thread once you get response you can decide whether its good to proceed to next step or not .In order to do sync call you use webclient or Webrequest . http://codecaster.nl/blog/2015/11/webclient-httpwebrequest-httpclient-perform-web-requests-net/ It says async you have to mark your method with async. – Jin Thakur Mar 14 '17 at 16:43
  • 6
    This is not a good solution. You're not supposed to dispose of HttpClient (yes, even though it inherits from IDisposable). Also, this could cause deadlocks (so I've read from multiple sources). There's solutions like this (https://cpratt.co/async-tips-tricks/) but it won't be blocking. I would recommend using a client that allows synchronous calls even though that's exactly what you don't want to do. The only thing that is left is the solution above. Although, you should modify to not dispose and make a static client. – christo8989 Mar 28 '18 at 00:31
  • Good idea is to set **Timeout** property to the HttpClient object when you use it synchronously to avoid thread blocking for too long. – vinsa Oct 03 '18 at 23:25
  • 15
    @DarinDimitrov I'm sure you've written this answer in good faith. But now, 6 years later, we know this code is a collection of every anti-pattern possible with `HttpClient`. This code can get someone in a serious trouble. Could you please consider removing it or putting up a disclaimer or doing something else to warn people. – Myk Mar 07 '19 at 12:14
  • 1
    This can go into deadlock unless you do ConfigureAwait(false) on the task returned by 'ReadAsStringAsync()'. Hard to explain why here, better you read this article (not mine): https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – Shishir Gupta Nov 28 '19 at 14:06
  • One example of a synchronous use case is making pseudo-async requests in Unity Engine using a coroutine. – Zimano Aug 26 '20 at 07:12
  • 2
    This code contains deadlocks and antipatterns (disposing of HttpClient). it should be downvoted by all until fixed. – JJS Apr 21 '21 at 17:05
  • Thank you so much. I've been search for a simple way to do this for several days! – Tim Melton Dec 01 '21 at 21:47
32

I'd re-iterate Donny V. answer and Josh's

"The only reason I wouldn't use the async version is if I were trying to support an older version of .NET that does not already have built in async support."

(and upvote if I had the reputation.)

I can't remember the last time if ever, I was grateful of the fact HttpWebRequest threw exceptions for status codes >= 400. To get around these issues you need to catch the exceptions immediately, and map them to some non-exception response mechanisms in your code...boring, tedious and error prone in itself. Whether it be communicating with a database, or implementing a bespoke web proxy, its 'nearly' always desirable that the Http driver just tell your application code what was returned, and leave it up to you to decide how to behave.

Hence HttpClient is preferable.

trev
  • 421
  • 4
  • 2
  • 1
    I was surprised that `HttpClient` itself is a wrapper around `HttpWebRequest` (that indeed, internally catches those `WebException` objects and does the conversion to a `HttpResponseMessage` for you). I would have thought it would be easier to build a new client entirely from scratch. – Dai Aug 18 '17 at 00:43
  • 5
    There is plenty of good reasons like not wanting to rewrite your whole codebase just for a very low-level http call that isn't even performance critical (but would introduce async to a million places). – FrankyBoy Jan 08 '18 at 12:41
  • In .net core 2 if you want to dynamically evaluate an expression with DynamicExpressionParser it might not be possible to use async; property indexers can't use async; in my situation I need to dynamically evaluate a string like "GetDefaultWelcomeMessage[\"InitialMessage\"]" where this method makes an HttpCall and the index syntax is preferable to method syntax "Util.GetDefaultWelcomeMessage(\"InitialMessage\")" – eugen Dec 03 '19 at 15:39
28

For anyone coming across this now, .NET 5.0 has added a synchronous Send method to HttpClient. https://github.com/dotnet/runtime/pull/34948

The merits as to why where discussed at length here: https://github.com/dotnet/runtime/issues/32125

You can therefore use this instead of SendAsync. For example

public string GetValue()
{
    var client = new HttpClient();
            
    var webRequest = new HttpRequestMessage(HttpMethod.Post, "http://your-api.com")
    {
        Content = new StringContent("{ 'some': 'value' }", Encoding.UTF8, "application/json")
    };

    var response = client.Send(webRequest);

    using var reader = new StreamReader(response.Content.ReadAsStream());
            
    return reader.ReadToEnd();
}

This code is just a simplified example - it's not production ready.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
alexs
  • 1,507
  • 18
  • 17
11
public static class AsyncHelper  
{
    private static readonly TaskFactory _taskFactory = new
        TaskFactory(CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    public static void RunSync(Func<Task> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}

Then

AsyncHelper.RunSync(() => DoAsyncStuff());

if you use that class pass your async method as parameter you can call the async methods from sync methods in a safe way.

it's explained here : https://cpratt.co/async-tips-tricks/

Lean Bonaventura
  • 411
  • 5
  • 13
8

If you're building a class library, then perhaps the users of your library would like to use your library asynchronously. I think that's the biggest reason right there.

You also don't know how your library is going to be used. Perhaps the users will be processing lots and lots of requests, and doing so asynchronously will help it perform faster and more efficient.

If you can do so simply, try not to put the burden on the users of your library trying to make the flow asynchronous when you can take care of it for them.

The only reason I wouldn't use the async version is if I were trying to support an older version of .NET that does not already have built in async support.

Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
  • I see, so make the class library asynchronous, and allow the users of the system to decide whether to use it async, or to use await and use it synchronously? – Ketchup Jan 21 '13 at 09:28
  • erm, await helps to make certain calls asynchronous by returning control to the caller. – Josh Smeaton Jan 21 '13 at 09:31
6

In my case the accepted answer did not work. I was calling the API from an MVC application which had no async actions.

This is how I managed to make it work:

private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static T RunSync<T>(Func<Task<T>> func)
    {           
        CultureInfo cultureUi = CultureInfo.CurrentUICulture;
        CultureInfo culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew<Task<T>>(delegate
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap<T>().GetAwaiter().GetResult();
    }

Then I called it like this:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
Jonathan Alfaro
  • 4,013
  • 3
  • 29
  • 32
0

In the current era, the shortest answer to this question is quite straightforward: Literally all other prior .NET options other than HttpClient are now deprecated/obsolete.

NetXpert
  • 511
  • 5
  • 14