首页 > 解决方案 > 从浏览器中的地址行执行时获取 415 不受支持的媒体类型,提供 JSON 作为 .NET Core 3 中路由的参数

问题描述

我正在执行 URL

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

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

“标题”:“不支持的媒体类型”,“状态”:415

被描述

...源服务器拒绝为请求提供服务,因为有效负载的格式不受目标资源上此方法的支持。

建议的故障排除是

...由于请求指示的 Content-Type 或 Content-Encoding,或由于检查数据...

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

控制器看起来像这样。

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

}

我考虑用[Consumes("application/json")]来装饰控制器,但发现有些东西让它感到沮丧。我尝试按照此处此处的建议添加 JSON 转换器,但无法真正确定要设置的选项,根据this摸索,不确定我是否从正确的树开始。

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

我的备份选项是使用查询字符串指定特定搜索所需的选项。但是,我现在更喜欢使用带参数的对象。

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

标签: c#rest.net-coreasp.net-core-3.0

解决方案


原因是可能有很多参数,我不想每次都重构控制器的签名

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

    [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. 以上两种方法都对我有用。但我个人建议您使用第一个,因为它是内置功能。


推荐阅读