从浏览器中的地址行执行时获取 415 不支持的媒体类型,将 JSON 作为 .NET Core 3 中的路由参数

Getting 415 Unsupported media type when executing from address line in browser provifing JSON as parameter for route in .NET Core 3

我正在执行 URL

https://localhost:44310/api/Licensee/{"name":"stan"}

在我浏览器的地址字段中,出现错误

"title": "Unsupported Media Type", "status": 415

其中is described

... the origin server is refusing to service the request because the payload is in a format not supported by this method on the target resource.

建议的问题是

... due to the request's indicated Content-Type or Content-Encoding, or as a result of inspecting the data ...

我无法真正控制 header 浏览器提供的内容。由于预期用途,我不能依赖 Postman 或 Web 应用程序。它需要从 URL 行中执行。该参数的结构会有所不同,具体取决于应用的搜索条件。

控制器看起来像这样。

[HttpGet("{parameters}")]
public async Task<ActionResult> GetLicensee(LicenseeParameters parameters)
{
  return Ok(await Licensee.GetLicenseeByParameters(parameters));

}

我考虑用 [Consumes("application/json")] 装饰控制器,但发现 something dicouraging it. I tried to add JSON converter as suggested and but couldn't really work out what option to set, fumbling according to ,不确定我是否在咆哮正确的树开始。

services.AddControllers()
  .AddJsonOptions(_ =>
  {
    _.JsonSerializerOptions.AllowTrailingCommas = true;
    _.JsonSerializerOptions.PropertyNamingPolicy = null;
    _.JsonSerializerOptions.DictionaryKeyPolicy = null;
    _.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
  });

我的备用选项是使用查询字符串指定特定搜索所需的选项。但是,我现在更愿意使用带参数的 object。

我该如何解决这个问题(或至少进一步排除故障)?

The reason is that there might be a loooot of parameters and I don't want to refactor the controller's signature each time

  1. 实际上,您不必每次都更改控制器的签名。 ASP.NET 核心模型绑定器能够自动绑定来自查询字符串的对象。例如,假设您有一个简单的控制器:

    [HttpGet("/api/licensee")]
    public IActionResult GetLicensee([FromQuery]LicenseeParameters parameters)
    {
        return Json(parameters);
    }
    

    第一次DTO是:

    public class LicenseeParameters
    {
        public string Name {get;set;}
        public string Note {get;set;}
    }
    

    您需要发送如下 HTTP 请求:

    GET /api/licensee?name=stan&note=it+works
    

    后来您决定更改 LicenseeParameters:

    public class LicenseeParameters
    {
        public string Name {get;set;}
        public string Note {get;set;}
    
        public List<SubNode> Children{get;set;} // a complex array
    }
    

    您不必更改控制器签名。只需以这种方式发送有效载荷:

    GET /api/licensee?name=stan&note=it+works&children[0].nodeName=it&children[1].nodeName=minus
    

    转换为:.表示属性,[]表示集合或字典。

  2. 如果您确实想在 URL 内发送 json 字符串,您需要创建一个自定义模型活页夹。

    internal class LicenseeParametersModelBinder : IModelBinder
    {
        private readonly JsonSerializerOptions _jsonOpts;
    
        public LicenseeParametersModelBinder(IOptions<JsonSerializerOptions> jsonOpts)
        {
            this._jsonOpts = jsonOpts.Value;
        }
    
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            var name= bindingContext.FieldName;
            var type = bindingContext.ModelType;
            try{
                var json= bindingContext.ValueProvider.GetValue(name).FirstValue;
                var obj = JsonSerializer.Deserialize(json,type, _jsonOpts);
                bindingContext.Result = ModelBindingResult.Success(obj);
            }
            catch (JsonException ex){
                bindingContext.ModelState.AddModelError(name,$"{ex.Message}");
            }
            return Task.CompletedTask;
        }
    }
    

    并按如下方式注册模型绑定器:

    [HttpGet("/api/licensee/{parameters}")]
    public IActionResult GetLicensee2([ModelBinder(typeof(LicenseeParametersModelBinder))]LicenseeParameters parameters)
    {
        return Json(parameters);
    }
    

    最后,你可以在URL内发送一个json(假设属性名称不区分大小写):

    GET /api/licensee/{"name":"stan","note":"it works","children":[{"nodeName":"it"},{"nodeName":"minus"}]}
    
  3. 以上两种方法都适合我。但我个人建议您使用第一个,因为它是内置功能。