13

我想使用 AJAX 将数据发布到我的 API,但我遇到了问题。我正在使用 Fiddler 测试我的 API,并且能够正确发布 JSON,但是在发布名称/值 urlencoded 字符串时,我收到一个 400 Bad Request,响应正文为 '{"":["The input was not valid ."]}'。

我的调试窗口显示:Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor:Information: Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Mvc.SerializableError'.

正在发布的 JSON 是:

{
    "Name": "Test"
}

正在发布的表单数据是:

Name=Test

这是控制器和动作:

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

这是帐户类:

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

在模型绑定期间似乎很明显存在问题,但表单数据似乎有效(我还使用 AJAX 生成了表单数据并获得了 400)。

4

4 回答 4

20

在2016年的 ASP.NET Core 中的模型绑定 JSON POST 博文中,Andrew Lock 解释说,为了在 ASP.NET Core 中绑定 JSON POST,[FromBody]必须在参数上指定属性,如下所示:

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

随着 ASP.NET Core 2.1 的引入[ApiController],这不再是必需的。[FromBody]这里重要的是,当绑定的类型是“复杂的”(在您的示例中就是这样)时,此属性有效地推断出该属性的存在。换句话说,就好像您已经编写了我上面演示的代码。

在他的帖子中,安德鲁还指出:

在某些情况下,您可能需要能够将两种类型的数据绑定到一个操作。在这种情况下,您会有点卡住,因为不可能让同一个端点接收两组不同的数据。

在这里,当提到这两种类型的数据时,Andrew 指的是 JSON 帖子和基于表单的 POST。他继续解释如何实际达到所需的结果。为您的目的修改他的示例,您需要执行以下操作:

// 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) {
    // ...
}

在 Andrew 的示例中,[FromBody]是显式的[FromForm],而 是隐式的,但考虑到[ApiController]对默认值的影响,上面的修改示例将其翻转。


请在此处查看我的答案,以了解一种潜在的方法,该方法允许将相同的 URL 用于两者FromFormFromBody使用自定义IActionConstraint.

于 2018-07-19T21:12:09.647 回答
3

如果您想将 header Content-Type:application/x-www-form-urlencoded) 的表单数据获取到您的 api 控制器,那么您必须将 [FromForm] 属性放在操作方法中,例如

    // POST: api/Create
    [HttpPost]
    public IActionResult CreateAccount([FromForm] Account account)
    {

    }

如果您想将标头 Content-Type:application/json 的表单数据获取到您的 api 控制器,那么您必须将 [FromBody]/No 属性放在操作方法中,例如

    // POST: api/Create
    [HttpPost]
    public IActionResult CreateAccount([FromBody] Account account)
    {

    }

或者

    // POST: api/Create
    [HttpPost]
    public IActionResult CreateAccount(Account account)
    {

    }
于 2018-12-09T05:51:27.530 回答
2

确保您的请求类型设置为“application/json”。我复制了您的代码,并且在将请求类型设置为 application/json 之前没有使用 Postman 调用该方法。

编辑:当我在提琴手的标题中添加以下内容时,我也能够让提琴手调用我的方法:

内容类型:应用程序/json

于 2018-07-19T20:43:27.110 回答
0
Install-Package Toycloud.AspNetCore.Mvc.ModelBinding.BodyOrDefaultBinding

配置

services.AddMvc(options =>
     {
         options.ModelBinderProviders.InsertBodyOrDefaultBinding();
     })

启用一个参数

[HttpPost]
public int SaveData([FromBodyOrDefault]DataPoco m)
{
    ;//
}

https://github.com/shamork/Toycloud.AspNetCore.Mvc.ModelBinding.BodyAndFormBinding

于 2020-03-28T08:44:51.703 回答