【问题标题】:Custom model binder with IDictionary<string, object>带有 IDictionary<string, object> 的自定义模型绑定器
【发布时间】:2022-09-27 19:04:28
【问题描述】:

我有一个带有两个参数的方法的 .NET 6 REST API:

public async Task<object> CreateSingleEntity([FromRoute] string entity, [FromBody] IDictionary<string, object> model)
{
    //process data
}

当我执行此请求时,这很有效:

curl --location --request POST \'https://localhost:7299/api/data/cars\' \\
--header \'Accept: application/json\' \\
--header \'Content-Type: application/json\' \\
--data-raw \'{
    \"model\": 1,
    \"name\": \"Ford\",
    \"id\":\"a47d52de-fcd1-48e7-8656-7edb84dc78bd\",
    \"is_created\": true,
    \"date\":\"2022-09-23\",
    \"datetime\":\"2022-09-23 13:10\"
}\'

但是因为我正在使用 MediatR,所以我想改用模型。

public class CreateSingleRecord : ICommand<object>
{
    [FromRoute(Name =\"entity\")]
    public string Entity { get; init; }

    [FromBody]
    public IDictionary<string, object> Record { get; init; }
}

可悲的是,每次我尝试用以下方法替换以前的方法时:

public async Task<object> CreateSingleEntity([FromHybrid] CreateSingleRecord model)
{
    //process data
}

我收到错误:

{ \"type\": \"https://tools.ietf.org/html/rfc7231#section-6.5.1\", \"title\": \"出现一个或多个验证错误。\", “状态”:400, \"traceId\": \"00-0b9809f4e2a656dd8b0255940ce84db7-49b9b11c21ce132a-00\", \"错误\": { \“记录\”: [ \"记录字段是必需的。\" ] } }

我尝试过使用[FromHybrid] model binder,但遗憾的是它不适用于字典类型。

端点必须处理动态对象,因为整个系统是非常动态的,所以我不能绑定到预定义的模型。

我认为唯一的方法是创建一个模型绑定器,但我不知道如何将整个主体反序列化为字典并将其分配给我的模型属性。

  • 您发送到服务器的对象必须保持不变。您的代码需要通过键值在字典中查找对象,并且只发送值而不是键。
  • @jdweng 我认为我的问题不是很清楚,抱歉我不是以英语为母语的人。我想将整个主体绑定到模型的 Record 属性。现在我必须手动创建模型,如下所示:var m = new CreateSingleRecord() {Entity =entity, Record=model}。但这应该自动完成。
  • 您必须以服务器期望的格式创建请求。您的模型还必须满足 MediatR 要求。您可以使用 JSON 序列化程序,但它可能无法同时满足 MediatR 和服务器格式。在创建请求之前,您还必须通过键提取字典的值。服务器无法识别字典输入。
  • @jdweng MediatR 与此无关。当我手动创建模型时它工作正常,我创建这个问题的原因是我想要一个一致的 API 并且能够将整个主体绑定到模型的属性。
  • 模型必须满足MediatR格式,满足html中的json格式。没有绑定会自动将一种格式转换为另一种格式。你需要编写代码。

标签: c# asp.net asp.net-core .net-6.0


【解决方案1】:

还有另一种方法。如果您的问题真的只是您想获取字典的值,那么只需像这样发送您的请求:

curl --location --request POST 'https://localhost:7299/api/data/cars' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{ "Record": {
    "model": 1,
    "name": "Ford",
    "id":"a47d52de-fcd1-48e7-8656-7edb84dc78bd",
    "is_created": true,
    "date":"2022-09-23",
    "datetime":"2022-09-23 13:10"
}}'

如果您想摆脱Record 属性,那么您可能无法避免编写自定义活页夹。像这样的东西。

[ModelBinder(BinderType = typeof(CreateSingleRecordBnder))]
public class CreateSingleRecord : ICommand<object>
{
    public string Entity { get; init; }

    public IDictionary<string, object> Record { get; init; }
}

模型粘合剂

public class CreateSingleRecordBnder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var bodyStream = bindingContext.HttpContext.Request.Body;
        using var streamReader = new StreamReader(bodyStream);
        var body = await streamReader.ReadToEndAsync();

        var data = System.Text.Json.JsonSerializer.Deserialize<IDictionary<string, object>>(body);

        var model = new CreateSingleRecord
        {
            Entity = bindingContext.HttpContext.Request.RouteValues["entity"].ToString(),
            Record = data
        };

        bindingContext.Result = ModelBindingResult.Success(model);
    }
}

更新

删除混合绑定并尝试这个怎么样?是不是更适合你?

端点

[HttpPost]
[Route("/api/data/{entity}")]

public async Task<object> CreateSingleEntity([FromRoute] CreateSingleRecord model)
{
    await Task.CompletedTask;
    return Task.FromResult("Test");
}

和单曲

public class CreateSingleRecord : ICommand<object>
{
    [FromRoute(Name = "entity")]
    public string Entity { get; init; }

    [FromBody]
    public IDictionary<string, object> Record { get; init; }
}

【讨论】:

  • 我不介意编写自定义活页夹,我只需要一些指导。 MS 网站上的示例不是很有帮助:learn.microsoft.com/en-us/aspnet/core/mvc/advanced/…
  • 我更新了我的答案,也许这会帮助你完成下一步。
  • 我不得不将[ModelBinder(typeof(CreateSingleRecordBnder ))] 属性添加到我的方法参数中,因为模型上的属性不起作用。我仍然想知道是否可以以某种方式重用默认活页夹。我在 Hybrid Model Binder 中创建了一个问题:github.com/billbogaiv/hybrid-model-binding/issues/64,所以也许创建者会知道如何重用活页夹。我将有大约 20 个模型,body 只是其中一个字段,因此使用更通用的方法会更好,而不是创建 20 个活页夹
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-18
  • 2012-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-29
相关资源
最近更新 更多