发送一个表单POST HTTP请求(Content-Type: application/x-www-form-urlencoded)到下面的控制器,结果是一个HTTP 415不支持的媒体类型响应。

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

表单post 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

这在过去是与ASP一起工作的。NET MVC 5在。NET 4.6。


当前回答

这个问题可能是因为MVC的MW。你必须在MVC选项中设置formatterType:

services.AddMvc(options =>
            {
                options.UseCustomStringModelBinder();
                options.AllowEmptyInputInBodyModelBinding = true;
                foreach (var formatter in options.InputFormatters)
                {
                    if (formatter.GetType() == typeof(SystemTextJsonInputFormatter))
                        ((SystemTextJsonInputFormatter)formatter).SupportedMediaTypes.Add(
                            Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("text/plain"));
                }
            }).AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
            });

其他回答

在我的情况下,收到了415不支持的媒体类型,因为我使用了新的FormData(),并将其与axios.post(…)一起发送,但没有设置报头:{content-type: 'multipart/form-data'}。我也必须在服务器端做同样的事情:

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

这是我的案子:它跑了 环境:AspNet Core 2.1 控制器:

public class MyController
{
    // ...

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

观点:

<form method="post" asp-controller="MyController" asp-action="Search">
    <input name="MySearchModelProperty" id="MySearchModelProperty" />
    <input type="submit" value="Search" />
</form>

你必须指定编码和内容类型,例如:

        var request = new HttpRequestMessage
        {
            Method = new HttpMethod("POST"),
            RequestUri = new Uri(CombineUrl(_baseUrl, _resource))
        };
        request.Content = new StringContent(contentBody, System.Text.Encoding.UTF8, "application/json");

ASP。NET核心:[FromBody] vs [FromForm]

当您试图将表单字段数据发送给ASP时,不支持的媒体类型错误很常见。NET通过POST,但使用它的专门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="https://localhost:44347/api/test/edit">
        <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>

在ASP. xml中仅捕获一个表单字段值。你可以使用[FromForm]…

[HttpPost]
public string EditByPost([FromForm] string myfield1)
{
  return "Success: " + myfield1;
}

...或者你可以捕获所有的表单字段作为一个集合使用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; }
}

奇怪的生物叫[FromBody]

[FromBody] is different. It breaks the traditional HTML form field POST that has worked the same for 20+ years and uses something different! It works much like [FromForm] does, but it is a new design, requiring JavaScript and its form data formatted in JSON with the JSON mime-type or "Content-Type" Http header in the Request that is sent to the server. It is also sent via the Http POST inside the body or payload section of the Request, but formatted as JSON with the 'application/json' Content-type, telling your ASP.NET WebAPI Core endpoint method to parse JSON objects, not traditional HTML form name-value pairs.

因为常规的HTML4/HTML5表单不能POST JSON或其内容类型,你必须使用JavaScript技巧,并在c#中使用[FromBody]装饰你的属性来做到这一点。这就是让使用普通HTML的人感到困惑的地方。

下面的示例现在监听JSON表单数据,而不是名称-值对。这并不是很明显,因为它看起来和[FromForm]版本一样…

[HttpPost]
public ActionResult EditByPost([FromBody] Form1 form)
{
    return new JsonResult(form);
}

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

下面,我将使用一个新的ECMAScript/JavaScript技巧来提取上面使用的相同的HTML表单字段值。然后,我使用async-await调用Promise对象(由fetch包装),手动为侦听服务器添加正确的JSON头内容类型,然后绕过HTML直接将这个新表单数据包POST到服务器。这可以添加到上面的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(...);
}

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

但是,当这些技术供应商根据几十年来的工作方式重写技术堆栈时,了解旧技术的工作方式总是有帮助的。可悲的是,它们并没有让开发人员更容易、更快或更直观,而是像这张海报一样让人困惑。

在。net 5中,我有一个。net API Controller方法,看起来像这样:

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

我有这样的要求:

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

test

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

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

然后我切换到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."
        ]
    }
}

通过将字符串封装在引号中,就像这样:"test"。

完整的工作要求:

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

"test"