Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I would like to post data to my API using AJAX but I'm having issues. I'm using Fiddler to test my API and I'm able to post JSON correctly but when posting a name/value urlencoded string I get a 400 Bad Request with the response body being '{"":["The input was not valid."]}'.

My debug window displays: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor:Information: Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.SerializableError'.

The JSON being posted is:

{
    "Name": "Test"
}

The form data being posted is:

Name=Test

This is the Controller and Action:

[Route("api/[Controller]")]
[ApiController]
public class AccountsController : Controller
{
    [HttpPost]
    public IActionResult CreateAccount(Account account)
    {
        //code
    }
}

This is the Account class:

public class Account
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Type { get; set; }
    public string Website { get; set; }
}

It seems obvious that there is an issue during model binding but the form data seems valid (I've also generated form data using AJAX and get a 400 as well).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
397 views
Welcome To Ask or Share your Answers For Others

1 Answer

In his post Model binding JSON POSTs in ASP.NET Core from 2016, Andrew Lock explains that in order to bind a JSON POST in ASP.NET Core, the [FromBody] attribute must be specified on the argument, like so:

[HttpPost]
public IActionResult CreateAccount([FromBody] Account account)
{
    // ...
}

With the ASP.NET Core 2.1 introduction of [ApiController], this is no longer required. Of importance here is that this attribute effectively infers the presence of the [FromBody] attribute when the type being bound is "complex" (which it is in your example). In other words, it's as though you have written the code as I demonstrated above.

In his post, Andrew also states the following:

In some cases you may need to be able to bind both types of data to an action. In that case, you're a little bit stuck, as it won't be possible to have the same end point receive two different sets of data.

Here, when referring to both types of data, Andrew is referring to both a JSON post and a form-based POST. He continues on to explain how to actually achieve the required result. Modifying his example for your purposes, you'd need to do something like the following:

// Form.
[HttpPost("FromForm")]
public IActionResult CreateAccountFromForm([FromForm] Account account)) =>
    DoSomething(account);

// JSON.
[HttpPost("FromBody")]
public IActionResult CreateAccountFromBody(Account account) =>
    DoSomething(account);

private IActionResult DoSomething(Account account) {
    // ...
}

In Andrew's example, the [FromBody] is explicit and the [FromForm] is implicit, but given the affect that [ApiController] has on the defaults, the modified example above flips that around.


See my answer here for a potential approach that allows for the same URL to be used for both FromForm and FromBody using a custom IActionConstraint.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...