ASP.NET Core表单POST导致HTTP 415 Unsupported Media Type响应

Bar*_*jen 132 c# asp.net-core-mvc asp.net-core

将表单POST HTTP request(Content-Type: application/x-www-form-urlencoded)发送到以下控制器会导致HTTP 415 Unsupported Media Type响应.

public class MyController : Controller
{
    [HttpPost]
    public async Task<IActionResult> Submit([FromBody] MyModel model)
    {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

表单发布HTTP标头:

POST /submit HTTP/1.1
Host: example.com:1337
Connection: keep-alive
Content-Length: 219
Pragma: no-cache
Cache-Control: no-cache
Origin: https://example.com:1337
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Referer: https://example.com:1337/submit
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8,nl;q=0.6
Run Code Online (Sandbox Code Playgroud)

这曾经用于.NET 4.6上的ASP.NET MVC 5.

Bar*_*jen 228

对于表单,请使用[FromForm]属性而不是[FromBody]属性.

以下控制器适用于ASP.NET Core 1.1:

public class MyController : Controller
{
    [HttpPost]
    public async Task<IActionResult> Submit([FromForm] MyModel model)
    {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

  • POST 请求是否需要“[FromForm]”?我以为它已经默认了。 (3认同)

Bjo*_*eul 79

您可以使用[FromBody],但需要将请求的Content-Type标头设置为application/json

喜欢

Content-Type: application/json
Run Code Online (Sandbox Code Playgroud)

  • 这比接受的答案IMO更准确答案. (3认同)
  • 这对我很有帮助,因为我提交的是对象,而不是表单。接受的答案对于 OP 来说是最正确的,他已经为 [FromForm] 使用了正确的内容类型。不过,我很高兴这个人也在这里。:) (3认同)
  • 这根本没有回答问题。问题是如何使服务器支持表单主体,而不是如何告诉所有客户端停止发送它们! (3认同)
  • 仍然有39个人发现它足够有用以支持它。所以他们从中学到了一些东西 (3认同)
  • 等等,这是否意味着无法从请求正文中提取与“application/json”不同的内容,例如“application/text”?@BartVerkoeijen 有什么想法吗? (3认同)

Vit*_*kiv 11

new FormData()就我而言,自从我使用并发送axios.post(...)但未设置415 Unsupported Media Types 以来,收到了 415 Unsupported Media Types headers: {content-type: 'multipart/form-data'}。我也必须在服务器端做同样的事情:

[Consumes("multipart/form-data")]
public async Task<IActionResult> FileUpload([FromForm] IFormFile formFile) { ... }
Run Code Online (Sandbox Code Playgroud)


Sto*_*ely 11

ASP.NET Core:[FromBody] 与 [FromForm]

当您尝试通过 POST 将表单字段数据发送到 ASP.NET 但使用不接受名称/值对的专用 WebAPI 端点处理程序之一时,这种不受支持的媒体类型错误很常见。[FromBody]是问题所在,并且不接受传统的 HTML5 表单字段名称/值对,但需要通过 JSON 进行 JavaScript POST。请继续阅读...

用于[FromForm]传统的 HTML 表单后期捕获。它可用于从名称-值对(从存储在 Http 请求正文中的 POST HTML 数据)中捕获单个表单字段值,也可用于捕获一批中的所有字段值。但您必须修改每个的 WebAPI C# 方法。

在传统的 HTML 表单字段 POST 到服务器中,提交时,浏览器使用发送到服务器的请求的 Http 正文发送表单的所有名称/值对。因此,提交以下 HTML 并将其方法更改为 POST 将所有字段以纯文本形式发送到服务器:

    <form id="form1" name="form1" method="post" action="">
        <input type="text" id="field1" name="field1" size="20" value="" /><br />
        <input type="text" id="field2" name="field2" size="20" value="" /><br />
        <input type="text" id="field3" name="field3" size="20" value="" /><br />
        <!-- SEND POST as traditional Name-Value pair. -->
        <button id="formbutton" name="formbutton" form="form1" value="submit">Form Submit (POST)</button>
    </form>
Run Code Online (Sandbox Code Playgroud)

要仅捕获 ASP.NET WebAPI 中的表单字段值之一,您可以使用以下命令执行以下操作[FromForm]

[HttpPost]
public string EditByPost([FromForm] string myfield1)
{
  return "Success: " + myfield1;
}
Run Code Online (Sandbox Code Playgroud)

...或者您可以使用 C# 匹配对象将所有表单字段捕获为一个集合...

[HttpPost]
public ActionResult EditByPost([FromForm] Form1 form)
{
   // Here I am just returning the full set of form fields
   // back as a JSON object.
   return new JsonResult(form);
}

// Here is the C# Class I created that matches the HTML
// form fields being captured by [FormForm]:
public class Form1
{
  public string field1 { get; set; }
  public string field2 { get; set; }
  public string field3 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

名为【FromBody】的奇怪生物

[FromBody]是不同的。[FromBody] 打破了 20 多年来一直以相同方式工作的传统 HTML 表单字段 POST,并使用了不同的东西!

它的工作原理很像[FromForm],但它是一种新设计,需要JavaScript自定义 WebAPI 调用或脚本来发布 JSON 格式的表单数据,并在请求中使用 JSON mime-type 或“Content-Type”Http 标头。它通过请求正文或有效负载部分内的 Http POST 发送,但格式为带有“application/json”内容类型的 JSON,告诉您的 ASP.NET WebAPI Core 端点方法解析 JSON 对象,而不是传统的 HTML 形式名称-值对。

由于常规 HTML4/HTML5 表单无法 POST JSON 或其内容类型,因此您必须使用JavaScript 技巧并在 C# 中装饰您的属性来[FromBody]执行此操作。这就是使用常规 HTML 的人感到困惑的地方。

下面的示例现在正在侦听 JSON 表单数据,而不是名称-值对。它正在这样做并不明显,因为它看起来与[FromForm]版本相同......

[HttpPost]
public ActionResult EditByPost([FromBody] Form1 form)
{
    return new JsonResult(form);
}
Run Code Online (Sandbox Code Playgroud)

要将带有 JSON 的表单字段数据发布到[FromBody]上面的 WebAPI,您必须使用JavaScript并手动从 HTML 中提取表单字段,然后将它们与 JSON 数据和 JSON 内容类型一起发送到服务器。大多数现代JavaScript API现在只是绕过 HTML 并存储原始 JSON 数据并将其发送到侦听 WebAPI POST。

下面,我使用新的 ECMAScript/JavaScript 技巧来提取与上面使用的相同的 HTML 表单字段值。然后,我使用 async-await 调用Promise 对象(由fetch包装),手动为侦听服务器添加正确的 JSON 标头内容类型,然后将这个新表单数据包直接 POST 到服务器,绕过 HTML。这可以添加到上面的 HTML 提交按钮作为关闭 HTML 提交过程并使用 JavaScript 的函数:

async function JavaScriptPOST() {
  var form = document.getElementById('form1');
  var myfield1 = form.querySelector('#field1').value;
  var myfield2 = form.querySelector('#field2').value;
  var myfield3 = form.querySelector('#field3').value;
  const response = await fetch('https://example.com/editbypost',
  {
    method: 'POST',
    body: JSON.stringify({
      field1: myfield1,
      field2: myfield2,
      field2: myfield3
    }),
    headers: {
      'Content-type':'application/json; charset=UTF-8',
    }
  }).then(...);
}
Run Code Online (Sandbox Code Playgroud)

意识到一件事...传统的 HTML 名称-值 POST 格式仍然是世界各地的主导技术。大多数 HTML 表单仍然使用传统的 POST 名称-值技术来发布数据。像这样的 JavaScript 技巧正在成为新的规范,但它们不会取代原始的 HTML 表单技术,也不会单独通过脚本编写。

但是,当这些技术供应商重写数十年来一直运行的技术堆栈时,了解旧技术的工作原理总是有帮助的。可悲的是,他们并没有让开发人员变得更容易、更快或直观,反而最终让像这张海报这样的人感到困惑。


has*_*san 9

作为补充的好答案,您不必使用[FromForm]来获取控制器中的表单数据。框架会根据需要自动将表单数据转换为模型。您可以实现如下。

[HttpPost]
public async Task<IActionResult> Submit(MyModel model)
{
    //...
}
Run Code Online (Sandbox Code Playgroud)

  • 不是我看到的。 (5认同)

Ogg*_*las 8

我有.NET 5一个 .NET API 控制器方法,如下所示:

[HttpPost("{rootEntity}/{id}")]
public ActionResult Post(RootEntity rootEntity, int id, [FromBody] string message)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

我有这样的要求:

POST /api/Comment/1/1 HTTP/1.1
Host: localhost:12345
Content-Type: text/plain
Content-Length: 4

test
Run Code Online (Sandbox Code Playgroud)

它导致以下状态代码响应:415 不支持的媒体类型

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
    "title": "Unsupported Media Type",
    "status": 415,
    "traceId": "00-e7ca54e9f313c24699c3ca4697b9363d-be4719bd10735245-00"
}
Run Code Online (Sandbox Code Playgroud)

然后我转而Content-Type: application/json喜欢@BjornBailleul 的答案,但收到了这个错误:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-0549e2c73842c249a93c8dc2f817e250-796e99fc0000224d-00",
    "errors": {
        "$": [
            "'test' is an invalid JSON literal. Expected the literal 'true'. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
        ]
    }
}
Run Code Online (Sandbox Code Playgroud)

通过将字符串封装在引号中来使其工作,如下所示:"test"

完整的工作要求:

POST /api/Comment/1/1 HTTP/1.1
Host: localhost:12345
Content-Type: application/json
Content-Length: 6

"test"
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

  • 如果您投反对票,请说出原因,否则很难改进答案。 (6认同)

Gab*_* P. 7

首先,您需要在标题中指定Content-Type,例如,可以为application/json

如果设置application/json内容类型,则需要发送一个json。

所以在body你的要求,你会送不form-data,不x-www-for-urlencoded,但一个rawJSON,例如{"Username": "user", "Password": "pass"}

您可以使示例适应各种内容类型,包括要发送的内容。

您可以使用Postman或curl等工具来玩这个游戏。


小智 5

This is my case: it's run Environment: AspNet Core 2.1 Controller:

public class MyController
{
    // ...

    [HttpPost]
    public ViewResult Search([FromForm]MySearchModel searchModel)
    {
        // ...
        return View("Index", viewmodel);
    }
}
Run Code Online (Sandbox Code Playgroud)

View:

<form method="post" asp-controller="MyController" asp-action="Search">
    <input name="MySearchModelProperty" id="MySearchModelProperty" />
    <input type="submit" value="Search" />
</form>
Run Code Online (Sandbox Code Playgroud)


1_b*_*bug 5

请按照以下步骤操作:

  1. 添加到发送请求头Content-Type字段:

     axios.post(`/Order/`, orderId,
     {
         headers: {'Content-Type': 'application/json'}
     })
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用 axios 发送的每个数据(简单或复杂类型)都不应放置任何额外的括号 ( axios.post('/Order/', orderId, ...))。

警告!type有一个例外string- 在 send() 之前将其字符串化axios.post('/Order/', JSON.stringify(address), ...)

  1. 向控制器添加方法:

    [HttpPost]
    public async Task<IActionResult> Post([FromBody]int orderId)
    {
        return Ok();
    }
    
    Run Code Online (Sandbox Code Playgroud)