创建于Asp.Net核心MongoDB的一个的ObjectId ModelBinder的

问题描述 投票:4回答:2

我试图创建的ObjectId类型在我的模型非常简单的模型绑定,但似乎无法使它工作至今。

这里的模型绑定:

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;
    }
}

下面是我想要的身体参数绑定类:

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; }
}

这是操作方法:

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

对于这个代码的工作,我的意思是模型绑定跑,我继承了控制器的控制和去除从播放器参数的[FromBody]属性。

当我运行这个,我可以进入模型绑定的BindModelAsync方法,但是我似乎无法摆脱后的数据Id参数值。我可以看到bindingContext.FieldName是正确的;它被设置为Id,然而result.FirstValue为空。

我已经从Asp.Net MVC走了一会儿,似乎很多事情都已经变了,变得更加混乱:-)

编辑根据意见,我想我应该提供更多的上下文。

如果我的球员动作参数之前把[FromBody],播放器设置为null。如果我删除[FromBody],播放器设置为默认值,而不是我博文的值。后机身如下图所示,它只是一个简单的JSON:

{
    "Id": "507f1f77bcf86cd799439011"
    "PlatformId": "9c8aae0f-6aad-45df-a5cf-4ca8f729b70f"
}
c# mongodb asp.net-core asp.net-core-mvc model-binding
2个回答
5
投票

如果我删除[FromBody],播放器设置为默认值,而不是我博文的值。

从身体中读数据请选择(除非你使用[ApiController])。当您从[FromBody]参数中删除Player,该模型结合流程将填充使用途径,查询字符串和表单值Player的性能,在默认情况下。在你的榜样,也有在这些位置没有这样的特性,所以没有Player的属性被置。

如果我的球员动作参数之前把[FromBody],播放器设置为null。

[FromBody]属性的存在,该模型结合过程尝试根据设置有该请求的Content-Type从主体读取。如果这是application/json,身体会被解析为JSON,并映射到你的Player的属性。在您的例子,JSON的解析过程失败,因为它不知道如何从一个string转换为ObjectId。发生这种情况时,你的控制器内ModelState.IsValid将返回false和你Player参数会null

对于这个码工作,我的意思是用于模型粘合剂来运行,我继承来自控制器控制器并除去从所述播放器参数中的[FromBody]属性。

当您删除[FromBody],该[ModelBinder(...)]属性你设置你的Id财产受到尊重,并让你的代码运行。然而,随着[FromBody]的存在,这个属性有效地将被忽略。有很多事情背后的幕后这里,但基本上它归结为一个事实,即你已经选择加入模型绑定从身体JSON而这也正是模型结合停止在这种情况下。


我上面说它的JSON的解析过程,真实未能这里由于不理解如何处理ObjectId提及。作为该JSON-解析由Newtonsoft.Json(又名JSON.NET)处理,一种可能的解决方案是创建一个定制JsonConverter。这是很好覆盖这里对堆栈溢出,所以我不会去到它是如何工作的细节。下面是一个完整的例子(错误处理略去了和懒惰):

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());
        );

0
投票

你错了,在ModelBinder的。正确的代码:

public class ObjectIdModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var result = bindingContext.ValueProvider.GetValue(bindingContext.FieldName);

        bindingContext.Result = ModelBindingResult.Success(new ObjectId(result.FirstValue));

        return Task.CompletedTask;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.