RestSharp vs HttpClient

Hi,

I need to consume the web api designed in our app (iOS/Android) and I inherited a wrapper of RestSharp.
Everything was working fine until we realized dates sent form the server in UTC format (ie: "2013-10-18T22:13:55Z") it actually converts the UTC to Local Time when deserializing the response.

I'm considering moving to HttpClient, which is supported in Xamarin, but I wanted to ask for some feedback before I do this.

Thanks,
R.

Posts

  • DerekBeattie.6675DerekBeattie.6675 ✭✭ US ✭✭

    You might find this handy if you switch. https://github.com/advancedrei/PortableRest

  • SKallSKall ✭✭✭✭ USMember ✭✭✭✭

    Several options I can think of:

    1) AddHandler function
    2) fork RestSharp to allow changing RestClient deserializer
    3) Deserialize into object from the request.Content string (use PostAsync instead of Post)

    3 is probably your best option. Use Json.NET, ServiceStack.Text or other de-serializer libraries to get the correct behaviour out of the DateTime fields.

  • SKallSKall ✭✭✭✭ USMember ✭✭✭✭

    Sorry for the formatting, forgot < needs double <<.

  • rrodriguezrrodriguez USMember

    Thanks to both, I ended up using HttpClient and JSON.NET

  • GuillermoGutierrezGuillermoGutierrez ✭✭✭ ESMember ✭✭✭

    I implemented this simple REST Json client based on HttpClient and JSON.NET, piece of cake:

    public class JsonRestService : ISimpleRestService
    {
        private readonly IMvxJsonConverter _converter;
    
        public JsonRestService(IMvxJsonConverter converter)
        {
            _converter = converter;
        }
    
        public Task<T> GetAsync<T>(string url) where T : new()
        {
            return GetAsync<T>(url, CancellationToken.None);
        }
    
        public async Task<T> GetAsync<T>(string url, CancellationToken cancellationToken) where T : new()
        {
            Mvx.Trace(MvxTraceLevel.Diagnostic, "Get request with URL: {0}", url);
            var client = new HttpClient();
            var response = await client.GetAsync(url, cancellationToken);
            var responseString = await response.Content.ReadAsStringAsync();
            Mvx.Trace(MvxTraceLevel.Diagnostic, "Received response for URL {0}... - {1}", url.Substring(0, Math.Min(url.Length, 80)), responseString);
    
            var result = await Task<T>.Factory.StartNew(() => _converter.DeserializeObject<T>(responseString));
            Mvx.Trace(MvxTraceLevel.Diagnostic, "Received object response for URL {0}... - {1}", url.Substring(0, Math.Min(url.Length, 80)), result);
    
            return result;
        }
    
        public Task<T> GetAsync<T>(string baseUrl, Dictionary<string, object> parameters) where T : new()
        {
            return GetAsync<T>(AddUrlParams(baseUrl, parameters));
        }
    
        public Task<T> GetAsync<T>(string baseUrl, Dictionary<string, object> parameters, CancellationToken cancellationToken) where T : new()
        {
            return GetAsync<T>(AddUrlParams(baseUrl, parameters), cancellationToken);
        }
    
        public Task<T> PostAsync<T>(string url, object postContent) where T : new()
        {
            return PostAsync<T>(url, postContent, CancellationToken.None);
        }
    
        public async Task<T> PostAsync<T>(string url, object postContent, CancellationToken cancellationToken) where T : new()
        {
            Mvx.Trace(MvxTraceLevel.Diagnostic, "Post request with URL: {0}", url);
            var client = new HttpClient();
            var serializedContent = _converter.SerializeObject(postContent);
            HttpContent httpContent = new StringContent(serializedContent, Encoding.UTF8, "application/json");
    
            var response = await client.PostAsync(url, httpContent, cancellationToken);
            var responseString = await response.Content.ReadAsStringAsync();
    
            Mvx.Trace(MvxTraceLevel.Diagnostic, "Received response for URL {0}... - {1}", url.Substring(0, Math.Min(url.Length, 80)), responseString);
    
            var result = await Task<T>.Factory.StartNew(() => _converter.DeserializeObject<T>(responseString));
            Mvx.Trace(MvxTraceLevel.Diagnostic, "Received object response for URL {0}... - {1}", url.Substring(0, Math.Min(url.Length, 80)), result);
    
            return result;
        }
    
        public Task<T> PostAsync<T>(string baseUrl, Dictionary<string, object> parameters, object postContent) where T : new()
        {
            return PostAsync<T>(AddUrlParams(baseUrl, parameters), postContent);
        }
    
        public Task<T> PostAsync<T>(string baseUrl, Dictionary<string, object> parameters, object postContent, CancellationToken cancellationToken) where T : new()
        {
            return PostAsync<T>(AddUrlParams(baseUrl, parameters), postContent, cancellationToken);
        }
    
        private static string AddUrlParams(string baseUrl, Dictionary<string, object> parameters)
        {
            var stringBuilder = new StringBuilder(baseUrl);
            var hasFirstParam = baseUrl.Contains("?");
    
            foreach (var parameter in parameters)
            {
                var format = hasFirstParam ? "&{0}={1}" : "?{0}={1}";
                stringBuilder.AppendFormat(format, Uri.EscapeDataString(parameter.Key),
                    Uri.EscapeDataString(parameter.Value.ToString()));
                hasFirstParam = true;
            }
    
            return stringBuilder.ToString();
        }
    }
    

    The one that I'm using is a little more complex to handle HTTP response codes, but you get the idea.

  • SKallSKall ✭✭✭✭ USMember ✭✭✭✭

    Just FYI, in case you need to handle large JSON strings, ServiceStack.Text is quite a lot faster than Json.NET.

  • softlionsoftlion ✭✭✭ FRBeta ✭✭✭

    ServiceStack.Text is not portable, and never will (it uses inline IL), and does not have all features that Json.net has like anonymous object deserialization: JSonConvert.DeserializeAnonymous(jsonObj, new { id="", content= new { url="" } }).

    Json.Net portable works perfectly.

  • softlionsoftlion ✭✭✭ FRBeta ✭✭✭
    edited October 2013

    For Rest, PortableRest does not have any of the plugins RestSharp has like OAuth plugins. It is a good idea, but as this is a key feature it won't help.

    So your choice is the best one.

  • SKallSKall ✭✭✭✭ USMember ✭✭✭✭

    Ben, by portable you mean PCL? Anyways, the comment was FYI as the speed improvement is huge especially on Android.

  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭

    @GuillermoGutierrez said:
    I implemented this simple REST Json client based on HttpClient and JSON.NET, piece of cake

    HttpClient implements IDisposable, you should wrap them in using blocks, or at least wrap the risky calls in a try-catch finally and dispose them in the finally part.

  • GuillermoGutierrezGuillermoGutierrez ✭✭✭ ESMember ✭✭✭

    @ShimmyWeitzhandler said:
    HttpClient implements IDisposable, you should wrap them in using blocks, or at least wrap the risky calls in a try-catch finally and dispose them in the finally part.

  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭

    sorry then...

  • tmeniertmenier ✭✭ USMember ✭✭

    @ShimmyWeitzhandler said:
    HttpClient implements IDisposable, you should wrap them in using blocks, or at least wrap the risky calls in a try-catch finally and dispose them in the finally part.

    No, actually, you shouldn't. https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

  • softlionsoftlion ✭✭✭ FRBeta ✭✭✭
    Mono Android / iOS don't have the same implementation than windows one.
    It even happened to be non reentrant one day.
    And apps are not micro services.

    Just dispose it.
  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭
    edited August 2017

    @tmenier, that article you linked is assuming it's wrong to dispose it cuz he based his example as a for loop generating a gazillion HttpClient instances instead of reusing a single one.
    HttpClient is a long lived object and SHOULD be reused, but it should likewise be disposed.

    Personally I like getting an initiated IoC-controlled HttpClient along with the proper inner-handler, registering it as a singleton app-life-cycle long object, and the container automatically disposes any IDisposable registered types upon container disposal.

    IMHO - regarding that article:
    1. I don't like few things in that post, firstly the HttpClient variable shouldn't be static, making it static is a gate for trouble and unexpected results, I haven't read the whole article or made my research, but I'm not even sure HttpClient is thread safe, especially allowing concurrent requests. Trying to minimize the time it takes to initialize a HttpClient per console-application instance by reusing the once of the other instances, is - in my PoV - a proper example of premature optimization; the variable should indeed be there, but not static.
    2. Before exiting the program, he should have called client.Dispose indeed, to get rid of open connections/sockets or whatever thing still going on under the hood.

  • softlionsoftlion ✭✭✭ FRBeta ✭✭✭
    Btw you resurrected a 3 year old thread.
  • tmeniertmenier ✭✭ USMember ✭✭

    @ShimmyWeitzhandler said:
    HttpClient is a long lived object and SHOULD be reused, but it should likewise be disposed.

    Yep, I'll concede to that. :smile:

    Personally I like getting an initiated IoC-controlled HttpClient along with the proper inner-handler, registering it as a singleton app-life-cycle long object, and the container automatically disposes any IDisposable registered types upon container disposal.

    That is a perfect way to use it. You had originally suggested wrapping calls in a using blocks, which doesn't necessarily mean short-lived/use-once but, while, it sort of implies it.

    I haven't read the whole article or made my research, but I'm not even sure HttpClient is thread safe, especially allowing concurrent requests.

    It is, and you should. https://stackoverflow.com/questions/11178220/is-httpclient-safe-to-use-concurrently

  • tmeniertmenier ✭✭ USMember ✭✭

    @softlion said:
    Btw you resurrected a 3 year old thread.

    And? Does that make the information presented here any less relevant or google-indexed?

  • softlionsoftlion ✭✭✭ FRBeta ✭✭✭

    Yes

  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭

    I haven't read the whole article or made my research, but I'm not even sure HttpClient is thread safe, especially allowing concurrent requests.

    It is, and you should. https://stackoverflow.com/questions/11178220/is-httpclient-safe-to-use-concurrently

    I just discovered I already upvoted that SO answer in the past...
    Anyway to sum up, it's always a good habit to dispose IDisposables, that being said, it doesn't imply that their life has to be short.

  • ShimmyWeitzhandlerShimmyWeitzhandler ✭✭✭ USMember ✭✭✭

    @softlion said:
    Btw you resurrected a 3 year old thread.

    WOOTWOOT! I've always been told I'm superman :tongue:

Sign In or Register to comment.