0

I am attempting to pass a complex object in a GET request. The C# model object looks like this:

public class GetRequest
{
    public string ID { get; set; }
    public List<string> RequestedFields { get; set; }
    public Dictionary<string, List<string>> RequestedGrids { get; set; }
}

Looking at the request in Chrome I see the following under "Query String Parameters":

ID: 1
RequestedFields[]: someTxtField
RequestedFields[]: someOtherField
RequestedGrids[someGrid][]: keyColumn
RequestedGrids[someGrid][]: someDataColumn

I would expect such a request to be deserialized correctly as the parameter in the following action:

[HttpGet]
[ActionName("get")]
public Dictionary<string,object> GetStuff([FromUri] GetRequest get_req)

However, whenever a request enters this action, the RequestedGrids property of the parameter always has a count of 0, while the other properties are populated fine. Is there a way to make this work?

Addition

The object going into the JQuery $.get call looks like this:

 { ID: p_key, RequestedFields: p_page.dataIds, RequestedGrids: p_page.grids }

Where RequestedFields is a plain array of strings and RequestedGrids is a plain object where each object property value is an array of strings.

Ian
  • 4,169
  • 3
  • 37
  • 62
  • 1
    IMO if the request needs a complex object, you should make it a POST so you can send it in a message body. –  Aug 09 '18 at 20:07
  • Yes, you can deserialize complex data, use complex data in the get if its used for a specifiec reason e.g you creating a dynamic list api and want to be able to pass the urls so you data set remains filtered – johnny 5 Aug 09 '18 at 20:40
  • The default model binder support complex objects, but to avoid long query strings you should rename or alias your member names in your models, I've written a way to serialize to query string in [typescript](https://stackoverflow.com/a/45603696/1938988) Alternatively you can write a custom model binder to deserialize json from the querystring – johnny 5 Aug 09 '18 at 20:43

3 Answers3

0

When you are needing to send this much data, especially complex data, it is best to send the data as part of the body of a POST request. This way, you can make sure that the structure matches completely and can easily convert to/from JSON format.

Grungondola
  • 680
  • 6
  • 26
  • I would prefer to avoid using POST. I like to restrict POST messages to requests that actually modify data. This allows me to easily enforce a simple admin vs user role in the middle-ware based on HTTP verb – Ian Aug 09 '18 at 20:11
  • 1
    @Ian Unfortunately, to send complex data structures like this across HTTP calls, those calls would need to be able to include a body, which could contain this data. Whether that is a POST or a PUT (response has no body) or even a PATCH (also no response body) is up to you. If you're just requesting data, consider simplifying your data structure to something that would be a little easier for the serializer to handle. – Grungondola Aug 09 '18 at 20:21
0

What does the input look like on the GET? Dictionary types are sort of painful to deserialize in general - see Deserialize JSON string to Dictionary<string,object> for details...

Adam Hoffman
  • 723
  • 6
  • 13
  • See addition to question. – Ian Aug 09 '18 at 20:30
  • Still not sure what the actual input, URL-escaped, to the MVC side looks like, but try this: `public class GetRequest { public string ID { get; set; } public string[] RequestedFields { get; set; } public Dictionary RequestedGrids { get; set; } }` – Adam Hoffman Aug 09 '18 at 20:38
  • Friggin' mini-markdown... the point of it is to change your List definitions to simple string arrays. I've seen it make serializers happier in the past. – Adam Hoffman Aug 09 '18 at 20:41
  • This answer is not complete, if the OP is using a the Get you would need to write a custom model binder to deserialize JSON from the Querystring – johnny 5 Aug 09 '18 at 20:45
  • @AdamHoffman - I tried changing to an array of strings and it didn't work. I think johnny might be right. – Ian Aug 09 '18 at 20:50
  • He might - here's the default model binding behavior (https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1) and here's a pointer to writing a custom one (https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-2.1). – Adam Hoffman Aug 09 '18 at 20:52
0

The easiest method to accomplish this seems to be to just pass a JSON query string instead of using the auto-binding. So the action looks like this:

[HttpGet]
[ActionName("get")]
public Dictionary<string, object> GetStuff(string get_str)
{
    var get_req = JsonConvert.DeserializeObject<GetRequest>(get_str);
    //Do ur stuff
}

And JS looks like this:

$.get('<action path>', { get_str: JSON.stringify(get_obj) }, ...
Ian
  • 4,169
  • 3
  • 37
  • 62