带有 IDictionary<string, object> 的自定义模型绑定器

Mis*_*siu 5 c# asp.net asp.net-core .net-6.0

我有一个 .NET 6 REST API,其方法有两个参数:

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

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

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"
}'
Run Code Online (Sandbox Code Playgroud)

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

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

    [FromBody]
    public IDictionary<string, object> Record { get; init; }
}
Run Code Online (Sandbox Code Playgroud)

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

public async Task<object> CreateSingleEntity([FromHybrid] CreateSingleRecord model)
{
    //process data
}
Run Code Online (Sandbox Code Playgroud)

我收到错误:

{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "发生一个或多个验证错误。", "status": 400, "traceId" : "00-0b9809f4e2a656dd8b0255940ce84db7-49b9b11c21ce132a-00", "errors": { "Record": [ "记录字段为必填项。" ] } }

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

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

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

T. *_*nik 8

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

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"
}}'
Run Code Online (Sandbox Code Playgroud)

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

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

    public IDictionary<string, object> Record { get; init; }
}
Run Code Online (Sandbox Code Playgroud)

模型活页夹

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

更新

删除混合绑定并尝试一下怎么样?它更适合你吗?

端点

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

public async Task<object> CreateSingleEntity([FromRoute] CreateSingleRecord model)
{
    ///
}
Run Code Online (Sandbox Code Playgroud)

以及单条记录

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

    [FromBody]
    public IDictionary<string, object> Record { get; init; }
}
Run Code Online (Sandbox Code Playgroud)