【问题标题】:Posting form data to MVC Core API将表单数据发布到 MVC Core API
【发布时间】:2018-12-28 01:55:15
【问题描述】:

我想使用 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
    }
}

这是 Account 类:

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)。

【问题讨论】:

  • 您应该发布 JSON(带有适当的“application/json”Content-Type 标头)或表单数据(带有适当的“application/x-www-form-urlencoded”Content-Type,而不是两者。
  • @heretic 我不会同时发布它们。我正在制作两篇单独的帖子,一篇使用 JSON,另一篇使用表单数据。内容类型由 fiddler 自动设置并匹配。
  • 尝试使用 postman 或 fiddler 来测试 Api。使用它们时,您的 api 绑定模型好吗??

标签: c# rest asp.net-core asp.net-core-mvc


【解决方案1】:

在他 2016 年的帖子 Model binding JSON POSTs in ASP.NET Core 中,Andrew Lock 解释说,为了在 ASP.NET Core 中绑定 JSON POST,必须在参数上指定 [FromBody] 属性,如下所示:

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

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

Andrew 在他的帖子中还指出:

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

这里,当提到两种类型的数据时,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] 对默认值的影响,上面修改后的示例将其翻转。


请参阅我的回答 here,了解一种允许使用自定义 IActionConstraintFromFormFromBody 使用相同 URL 的潜在方法。

【讨论】:

    【解决方案2】:

    如果您想将 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)
        {
    
        }
    

    【讨论】:

      【解决方案3】:

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

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

      内容类型:application/json

      【讨论】:

        【解决方案4】:
        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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-08-09
          • 2017-05-21
          • 2020-04-16
          • 2019-06-20
          • 1970-01-01
          • 1970-01-01
          • 2022-06-14
          • 2014-01-27
          相关资源
          最近更新 更多