ASP.NET 核心中内容类型 "application/csp-report" 的“415 不支持的媒体类型”

"415 Unsupported Media Type" for Content-Type "application/csp-report" in ASP.NET Core

我的内容安全策略导致 Chrome 到 post 报告,但收到报告的操作 returns “415 不支持的媒体类型”。我理解这是因为 post 的内容类型为 "application/csp-report"。我如何将其添加为 Core 3.1 中允许的内容类型(它基本上只是 json)。

动作

// https://anthonychu.ca/post/aspnet-core-csp/
[HttpPost][Consumes("application/csp-report")]
public IActionResult Report([FromBody] CspReportRequest request)
{
    return Ok();
}

模型缩减版本

public class CspReportRequest
{
    [JsonProperty(PropertyName = "csp-report")]
    public CspReport CspReport { get; set; }
}

public class CspReport
{
    [JsonProperty(PropertyName = "document-uri")]
    public string DocumentUri { get; set; }
}

以下示例显示了如何添加对 SystemTextJsonInputFormatter 的支持以处理其他媒体类型:

services.AddControllers(options =>
{
    var jsonInputFormatter = options.InputFormatters
        .OfType<SystemTextJsonInputFormatter>()
        .Single();

    jsonInputFormatter.SupportedMediaTypes.Add("application/csp-report");
});

这是一个两步过程:

  1. 查询配置的输入格式化程序列表以找到 SystemTextJsonInputFormatter
  2. application/csp-report 添加到其现有的支持媒体类型列表(application/jsontext/jsonapplication/*+json)。

如果您使用 Json.NET 而不是 System.Text.Json,方法是 similar:

services.AddControllers(options =>
{
    var jsonInputFormatter = options.InputFormatters
        .OfType<NewtonsoftJsonInputFormatter>()
        .First();

    jsonInputFormatter.SupportedMediaTypes.Add("application/csp-report");
})

有两个小区别:

  1. 类型是 NewtonsoftJsonInputFormatter 而不是 SystemTextJsonInputFormatter
  2. 集合中有 两个 个这种类型的实例,因此我们将第一个作为目标(具体请参见 )。

请参阅 ASP.NET 核心文档中的 Input Formatters 了解更多信息。

上周我遇到了同样的问题,并使用我自己的 custom formatter:

找到了替代解决方案
using CspReportLogger.Models;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Net.Http.Headers;
using System;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace CspReportLogger.Formatters
{
  public class CSPReportInputFormatter : TextInputFormatter
  {
    public CSPReportInputFormatter()
    {
      // Specify the custom media type.
      SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/csp-report"));
      SupportedEncodings.Add(Encoding.UTF8);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding effectiveEncoding)
    {
      // Let ASP interrupt deserialization
      var cancellationToken = context.HttpContext.RequestAborted;

      // Avoid InvalidCastException, pull type from controller
      var modelType = context.ModelType;

      // Deserialize the body using our models and the JsonSerializer.
      var report = await JsonSerializer.DeserializeAsync(context.HttpContext.Request.Body, modelType, null, cancellationToken);
      return await InputFormatterResult.SuccessAsync(report);
    }
  }
}

当然要在Startup.cs注册:

    public void ConfigureServices(IServiceCollection services)
    {
      services.AddControllers(options =>
      {
        options.InputFormatters.Insert(0, new CSPReportInputFormatter());
      });
    }

我希望我早点看到,因为它显然更简洁。

如果您想接受无效的正文类型,我想自定义格式化程序解决方案会有所帮助 json。

我想补充一点,接受的解决方案对我不起作用。 (.NET Core 3.1) 关于 CSP 报告,我有完全相同的用例。当尝试使用 NewtonSoft 并修改 InputFormatter NewtonsoftJsonInputFormatter 以接受媒体 header 类型 application/csp-report 时,我总是会收到一个异常,说找不到 inputformatter(有或没有 .AddNewtonsoftJson();)

我通过执行以下操作设法解决了问题:

services.AddControllers().AddNewtonsoftJson();
services.AddOptions<MvcOptions>()
      .PostConfigure<IOptions<JsonOptions>, IOptions<MvcNewtonsoftJsonOptions>, ArrayPool<char>, ObjectPoolProvider, ILoggerFactory>(
          (mvcOptions, jsonOpts, newtonJsonOpts, charPool, objectPoolProvider, loggerFactory) =>
          {
              var formatter = mvcOptions.InputFormatters.OfType<NewtonsoftJsonInputFormatter>().First(i => i.SupportedMediaTypes.Contains("application/json"));
              formatter.SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/csp-report"));
              mvcOptions.InputFormatters.RemoveType<NewtonsoftJsonInputFormatter>();
              mvcOptions.InputFormatters.Add(formatter);
          });

我的模型和控制器动作与问题中发布的相同。

(我的解决方案来自 How to configure two JSON serializers and select the correct one based on the route

这要感谢 rm-code。我确实必须进行一些更改,因为我在以下位置获得了一个空值:

var report = await JsonSerializer.DeserializeAsync(context.HttpContext.Request.Body, modelType, null, cancellationToken);

这就是最终对我有用的方法。

using Namespace.WebUI.Models;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using System.IO;
using System.Text;
using System.Threading.Tasks;

public class CSPReportInputFormatter : TextInputFormatter
{
    public CSPReportInputFormatter()
    {
        // Specify the custom media type.
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/csp-report"));
        SupportedEncodings.Add(Encoding.UTF8);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding effectiveEncoding)
    {
        using var reader = new StreamReader(context.HttpContext.Request.Body);
        string responseString = await reader.ReadToEndAsync();

        var data = JsonConvert.DeserializeObject<CspReportRequest>(responseString);

        return await InputFormatterResult.SuccessAsync(data);
    }
}