0

I'm looking to send complex objects to my .NET Core API without FromBody tags.

What I am looking to do is simple with JQuery, but I can't figure out for the life of me how to duplicate the logic from a C# Web API Client.

For the sake of testing, I have a fairly simple object.

[Serializable]
public class FilterModel
{
    public int? PageSize { get; set; }
    public int Page { get; set; }
}

For the sake of testing, we also have an extremely simple controller method

[HttpPost("TEST")]
public virtual int GetTEST(Common.FilterModel filter, Common.FilterModel filter2)
{
    return ((filter.Page * filter.PageSize ?? 0) + (filter2.Page * filter2.PageSize ?? 0));
}

Obviously the use case for the code would me more complex, but I am just trying to get the values at this time.

The JQuery to call this method is fairly straight forward and works flawlessly:

var myData = {"filter":{"pageSize":3,"page":2},
            "filter2":{"pageSize":19,"page":1}};
$.ajax({
  type: 'POST',
  url: '/api/Authentication/TEST',
  data: myData
}).done(function (data, statusText, xhdr) {
  JsonResponse = data;
  console.log(data);
}).fail(function (xhdr, statusText, errorText) {
  //console.log(JSON.stringify(xhdr));
});

But trying to duplicate that logic from an HttpClient results in 0; the model is always empty when received on the API side. I've tried a few different methods, from JSON Serializing an ArrayList/Dictionary to what I'm adding below, but I'm hitting a dead end unsure where I went wrong.

        HttpClient client = new HttpClient
        {
            BaseAddress = new Uri("http://localhost:65447/")
        };
        ApiCommon.FilterModel filter = new ApiCommon.FilterModel()
        {
            PageSize = 10,
            Page = 1
        };
        ApiCommon.FilterModel filter2 = new ApiCommon.FilterModel()
        {
            PageSize = 9,
            Page = 41
        };


        MultipartFormDataContent mpContent = new MultipartFormDataContent();
        StringContent content = new StringContent(JsonConvert.SerializeObject(filter));
        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
        mpContent.Add(content, "filter");
        content = new StringContent(JsonConvert.SerializeObject(filter2));
        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
        mpContent.Add(content, "filter2");
        var retval = 
              JsonConvert.DeserializeObject<int>(
                  client.PostAsync("/api/Authentication/TEST", mpContent)
                  .Result.Content.ReadAsStringAsync().Result);
        Console.WriteLine(retval);

Is there any way to imitate the JQuery call to work through an HttpClient? Am I missing something critical here?

EDIT: After getting fiddler to pick it up, this is the raw data for each, I am going to try to match this with my changes.

JQuery fiddler

ePOST http://localhost:65447/api/Authentication/TEST HTTP/1.1
Host: localhost:65447
Connection: keep-alive
Content-Length: 86
Accept: */*
Origin: http://localhost:65447
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:65447/TestHarness
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
filter%5BpageSize%5D=3&filter%5Bpage%5D=2&filter2%5BpageSize%5D=19&filter2%5Bpage%5D=1

C# fiddler

POST http://local.dev:65447/api/Authentication/TEST HTTP/1.1
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Host: local.dev:65447

156
{"filter":{"<IncludeFullData>k__BackingField":false,"<SortBy>k__BackingField":"","<PageSize>k__BackingField":10,"<Page>k__BackingField":1,"<Operation>k__BackingField":""},"filter2":{"<IncludeFullData>k__BackingField":false,"<SortBy>k__BackingField":"","<PageSize>k__BackingField":9,"<Page>k__BackingField":41,"<Operation>k__BackingField":""}}
0
Kiradien
  • 104
  • 6
  • 2
    Have you tried capturing the request you are sending to your API in order to see what the issue is? You could use something like Fiddler to see what the request body looks like – benjrb Mar 29 '18 at 15:09
  • 1
    Yes, use Fiddler and compare the 2 requests for differences. If it's not obvious what to do from that, post the requests here and it'll make helping you much easier. – Jim W Mar 29 '18 at 15:44
  • Regretfully, Fiddler is not able to capture my applications requests; I'm generally not sure as to why. I can get the request from the browser with no issue. I assume it is bypassing the proxy, which I was looking into configuring. – Kiradien Mar 29 '18 at 15:47
  • 2
    I see that jquery uses url encoding to send the data, in c# you were using json which is deserialized into a single object which explains the result. If you want the c# equivalent you will have to also use `application/x-www-form-urlencoded` encoding. – Igor Mar 29 '18 at 16:56
  • 1
    Alternatively you can change your controller method signature to accept a single object or an array of objects if there are more than 1. Then you can use json but that would really be the only way to do that. – Igor Mar 29 '18 at 16:57
  • Yeah, I can definitely get it working through URLEncoding, regretfully the only way to get that to truly work in the full application this is for would be via reflection to get the property names and iterate through; really don't want to throw that kind of crazy complexity into what should be simple requests. I'll stick with multiple properties within a container in the end. Thanks guys. It's kinda crazy to think that JQuery is inherently more capable than C# on something like this though. – Kiradien Mar 29 '18 at 17:08
  • HttpClient in C# is quite capable of this when you use the correct encoding content types. When you use `[FromBody]` MVC is expecting JSON, otherwise it expects the default which is `application/x-www-form-urlencoded`. You would need to write a custom model binder to handle requests that are `application/json`. See https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding – Brad Mar 30 '18 at 04:19

0 Answers0