3

I have an n-tier application, whereas the core web service is built with Web API. many of the web service's methods are set as HTTPGET and accept a DTO object as parameter. my client app, built with MVC 5 is using HttpClient to call this API.

so it seems that by using client.PostAsJsonAsync() I can pass an object, whereas client.GetAsync() doesn't allow me to do that. this forces me to explicitly specify the properties of DTO in the URL, which works, but seem a bit redundant.

Can somebody explain why this is not possible through a GET call and suggest a better practice?

kob490
  • 3,177
  • 4
  • 25
  • 40
  • Can you give more details about your API - what are the objects passed in on GET request used for in the first place? Usually query string is used for filtering on GET requests to retrieve data matching your specific query. – Joanna Derks Mar 29 '14 at 17:57

1 Answers1

5

Why does passing data in the URI seem redundant? The HTTP spec says that GET methods are not to use content sent in the body. This is primarily to facilitate caches being able to cache responses based only on the URI, method and headers. Requiring caches to parse the body of a message to identify a resource would be very inefficient.

Here is an basic extension method that will do the grunt work for you,

 public static class UriExtensions
    {
        public static Uri AddToQuery<T>(this Uri requestUri,T dto)
        {
            Type t = typeof (T);
            var properties = t.GetProperties();
            var dictionary = properties.ToDictionary(info => info.Name, 
                                                     info => info.GetValue(dto, null).ToString());
            var formContent = new FormUrlEncodedContent(dictionary);

            var uriBuilder = new UriBuilder(requestUri) {Query = formContent.ReadAsStringAsync().Result};

            return uriBuilder.Uri;
        }
    }

and assuming you have a DTO like this,

 public class Foo
    {
        public string Bar { get; set; }
        public int Baz { get; set; }
    }

you can use it like this.

    [Fact]
    public void Foo()
    {
        var foo = new Foo()
        {
            Bar = "hello world",
            Baz = 10
        };

        var uri = new Uri("http://example.org/blah");
        var uri2 = uri.AddToQuery(foo);

        Assert.Equal("http://example.org/blah?Bar=hello+world&Baz=10", uri2.AbsoluteUri);
    }
Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • my problem is not with how HTTP works, but more with the HttpClient class. would it make sense to add a wrapper method to client.GetAsync() that will accept an object and handle its conversion to the URL for you? – kob490 Mar 28 '14 at 20:23
  • If you use a library like RestSharp, then that has helper methods which will allow you to take a parameter object and serialise that into your querystring (IRestRequest.AddObject(object)) – David Keaveny Jun 17 '14 at 01:54
  • @DavidKeaveny Does the RestSharp method do anything more than the extensions method I suggested? – Darrel Miller Jun 17 '14 at 11:07
  • Not really; but as they say, the best piece of code you can write is the piece of code that you don't have to write - in other words, if the library you are using already provides the functionality, why reinvent a possibly buggier implementation? – David Keaveny Aug 11 '14 at 11:58