1

I know this question has been asked a hundred times on SO, but none of the solutions have helped me. I'm trying to do something that ought to be simple: pass a list of objects from my AngularJS (aka Angular 1) front-end to a .NET MVC controller endpoint.

Here's a simplified version of the relevant JavaScript:

var data = [{ A: 1, B: 2 }, { A: 3, B: 4 }];
$http.post(baseUrl + 'ap/underwriting', data);

And here's the .NET controller endpoint:

[AcceptVerbs(HttpVerbs.Post)]
public async Task<string> Underwriting(List<MyViewModel> list)
{
    return await DoSomethingWith(list);
}

When I try to POST to this endpoint, I can't reach a breakpoint, so it's failing before the code in the action runs. Oddly, I get a 200 back as the response status, though this may be an artifact of the environment I'm working in.

I can confirm that the JavaScript objects match MyViewModel (in fact, they are actually created by a GET call that returns a List). I can also confirm that the endpoint is wired up correctly, as I have tried hitting it with a model that doesn't include a List and it works, and binds the data, as expected.

I've tried making the List a property on a parent model, then passing the parent model, like so:

JS:

var data = { Name: 'Whatever', 'Steps': [{ A: 1, B: 2 }, { A: 3, B: 4 }] };
$http.post(baseUrl + 'ap/underwriting', data);

C# model:

public class MyParentModel
{
    public string Name { get; set; }
    public List<MyViewModel> Steps { get; set; }
}

Controller action:

[AcceptVerbs(HttpVerbs.Post)]
public async Task<string> Underwriting(MyParentModel model)
{
    return await DoSomethingWith(model.Steps);
}

This fails in the same manner. However, if I comment out the Steps property in the MyParentModel class, the endpoint works and the Name property comes through correctly. So I'm pretty confident that the issue lies with .NET attempting to bind the List.

On the JS side, I've tried using JSON.stringify to manually serialize the data. I've also tried explicitly making it a JSON object like so:

var data = { list: [{ A: 1, B: 2 }, { A: 3, B: 4 }] };
$http.post(baseUrl + 'ap/underwriting', data);

I've also made sure that the Content-Type of the POST is application/json (AngularJS does this automatically).

On the .NET side, I've tried making the parameter type an IList and a MyViewModel[].

I feel as though this ought to be simple, but I know it's something .NET has trouble with. What am I missing?

Thank you!

Nat Webb
  • 669
  • 1
  • 7
  • 18

3 Answers3

1

Well, I figured it out. I haven't seen this cause mentioned anywhere else in relation to this common problem so I'm self-answering to put it out there.

The problem I had was that I had written a constructor method for the MyViewModel class that had specific parameters. So the issue .NET was having had nothing to do with it being a List, but rather that it couldn't create members of the MyViewModel class because it had no way to call the constructor. The solution was to add a second constructor with no parameters.

Nat Webb
  • 669
  • 1
  • 7
  • 18
0

I'm not an Angular expert, but to use an MVC view as an example. If you were to do a standard post, the extension methods generates elements like:

<input type="text" name="Steps[0].A" />

to match your model, so a serialized FORM POST would use syntax of:

Steps[0].A=1&Steps[0].B=2

for the post operation. Your example of:

var data = { Name: 'Whatever', 'Steps': [{ A: 1, B: 2 }, { A: 3, B: 4 }] }

Is closest, but looking from some other examples via jQuery, it appears that simiarly you have to serialize the array (some examples using jQuery):

EDIT: Although this solution seemed to work for someone else:

Brian Mains
  • 50,520
  • 35
  • 148
  • 257
0

Add [FromBody] to your api method so it looks like so;

Then the model converter should convert your simple data to its matching viewmodel.

[AcceptVerbs(HttpVerbs.Post)]
public async Task<string> Underwriting([FromBody] List<MyViewModel> list)
{
    return await DoSomethingWith(list);
}

hth steve

wortho
  • 31
  • 2
  • Unfortunately, as far as I can tell the FromBody attribute isn't available in MVC controllers, only in Web API controllers. It's in the System.Web.Http namespace, which includes things that conflict with the System.Web.Mvc namespace. – Nat Webb Aug 10 '17 at 12:56