c# - 在 Asp.Net Core 上为 MongoDB ObjectId 创建一个 ModelBinder

标签 c# mongodb asp.net-core asp.net-core-mvc model-binding

我正在尝试为我的模型中的 ObjectId 类型创建一个非常简单的模型绑定(bind)器,但到目前为止似乎无法让它工作。

这是模型 Binder :

public class ObjectIdModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var result = bindingContext.ValueProvider.GetValue(bindingContext.FieldName);
        return Task.FromResult(new ObjectId(result.FirstValue));
    }
}

这是我编写的 ModelBinderProvider:

public class ObjectIdModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));

        if (context.Metadata.ModelType == typeof(ObjectId))
        {
            return new BinderTypeModelBinder(typeof(ObjectIdModelBinder));
        }

        return null;
    }
}

这是我试图将 body 参数绑定(bind)到的类:

public class Player
{
    [BsonId]
    [ModelBinder(BinderType = typeof(ObjectIdModelBinder))]
    public ObjectId Id { get; set; }
    public Guid PlatformId { get; set; }
    public string Name { get; set; }
    public int Score { get; set; }
    public int Level { get; set; }
}

这是 Action 方法:

[HttpPost("join")]
public async Task<SomeThing> Join(Player player)
{
    return await _someService.DoSomethingOnthePlayer(player);
}

为了让这段代码工作,我的意思是为了让模型绑定(bind)器运行,我从 Controller 继承了 Controller 并从 Player 参数中删除了 [FromBody] 属性。

当我运行它时,我可以进入模型联编程序的 BindModelAsync 方法,但是我似乎无法从发布数据中获取 Id 参数值。我可以看到 bindingContext.FieldName 是正确的;它设置为 Idresult.FirstValue 为空。

我已经离开 Asp.Net MVC 一段时间了,似乎很多东西都发生了变化,变得更加困惑:-)

编辑 根据评论,我认为我应该提供更多背景信息。

如果我将 [FromBody] 放在 Player 操作参数之前,player 将设置为 null。如果我删除 [FromBody],player 将设置为默认值,而不是我发布的值。帖子正文如下所示,它只是一个简单的 JSON:

{
    "Id": "507f1f77bcf86cd799439011"
    "PlatformId": "9c8aae0f-6aad-45df-a5cf-4ca8f729b70f"
}

最佳答案

If I remove [FromBody], player is set to a default value, not to the values I post.

从正文中读取数据是选择加入(除非您使用 [ApiController] )。当您从 Player 参数中删除 [FromBody] 时,模型绑定(bind)过程将使用路由查询填充 Player 的属性,查询-默认情况下,字符串和表单值。在您的示例中,这些位置没有此类属性,因此未设置 Player 的任何属性。

If I put [FromBody] before the Player action parameter, player is set to null.

由于存在 [FromBody] 属性,模型绑定(bind)进程会尝试根据随请求提供的 Content-Type 从正文中读取内容。如果这是 application/json,正文将被解析为 JSON 并映射到您的 Player 的属性。在您的示例中,JSON 解析过程失败,因为它不知道如何将 string 转换为 ObjectId。发生这种情况时, Controller 中的 ModelState.IsValid 将返回 false 并且您的 Player 参数将为 null

For this code to work, I mean for the model binder to run, I inherited the controller from Controller and removed the [FromBody] attribute from the Player parameter.

当您删除 [FromBody] 时,您在 Id 属性上设置的 [ModelBinder(...)] 属性是受到尊重,因此您的代码可以运行。然而,由于 [FromBody] 的存在,这个属性实际上被忽略了。这里的幕后发生了很多事情,但本质上归结为这样一个事实,即您已经选择以 JSON 形式从正文中进行模型绑定(bind),这就是模型绑定(bind)在这种情况下停止的地方。


我在上面提到,由于不了解如何处理 ObjectId,这里失败的是 JSON 解析过程。由于此 JSON 解析由 Newtonsoft.Json(又名 JSON.NET)处理,一个可能的解决方案是创建自定义 JsonConverter .这在 Stack Overflow 上有很好的介绍,所以我不会详细介绍它的工作原理。这是一个完整的示例(为简洁和懒惰而省略了错误处理):

public class ObjectIdJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) =>
        objectType == typeof(ObjectId);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
        ObjectId.Parse(reader.Value as string);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) =>
        writer.WriteValue(((ObjectId)value).ToString());
}

要使用它,只需将现有的 [ModelBinder(...)] 属性替换为 [JsonConverter(...)] 属性,如下所示:

[BsonId]
[JsonConverter(typeof(ObjectIdJsonConverter))]    
public ObjectId Id { get; set; }

或者,您可以全局注册 ObjectIdJsonConverter,以便它适用于所有 ObjectId 属性,在 Startup.ConfigureServices 中使用如下内容:

services.AddMvc()
        .AddJsonOptions(options =>
            options.SerializerSettings.Converters.Add(new ObjectIdJsonConverter());
        );

关于c# - 在 Asp.Net Core 上为 MongoDB ObjectId 创建一个 ModelBinder,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53246755/

相关文章:

mongodb - 在 AWS EC2 实例上找不到 MongoDump 命令

node.js - MERN 教程问题 : Cannot read properties of undefined (reading 'collection' )

c# - SupportedUICultures 不显示所有文化

c# - 如何在 ASP.NET Core 2.1 中使用角色?

c# - 如果从隐藏窗口调用 ShowDialog() 会提前结束

c# - 使用 C sharp 中的 RSA 对 128 字节的字节数组进行签名

c# - 如何更改项目中的启动窗体?

java - 计算文档中字段的数量 mongo Java

c# - 在 MediatR 管道上使用多个 FluentValidator

c# - 如何将一个 mvc3 c# 项目添加到其他可执行项目中?