簡體   English   中英

在Asp.Net Core上為MongoDB ObjectId創建ModelBinder

[英]Creating a ModelBinder for MongoDB ObjectId on Asp.Net Core

我正在嘗試為我的模型中的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;
    }
}

這是我嘗試將body參數綁定到的類:

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

為了使此代碼正常工作,我的意思是要運行模型綁定程序,我從Controller繼承了控制器,並從Player參數中刪除了[FromBody]屬性。

運行此命令時,可以進入模型綁定程序的BindModelAsync方法,但是似乎無法從發布數據中獲取Id參數值。 我可以看到bindingContext.FieldName是正確的; 它設置為Id,result.FirstValue為null。

我離開Asp.Net MVC已有一段時間了,看來很多事情已經改變並且變得更加混亂:-)

編輯基於評論,我認為我應該提供更多的背景信息。

如果我把[FromBody]玩家操作參數之前, 播放器設置為null。 如果刪除[FromBody],則播放器將設置為默認值,而不是我發布的值。 帖子正文如下所示,它只是一個簡單的JSON:

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

如果刪除[FromBody],則播放器將設置為默認值,而不是我發布的值。

從主體讀取數據是可選的 (除非您使用[ApiController] )。 當從Player參數中刪除[FromBody] ,默認情況下,模型綁定過程將使用路由,查詢字符串和表單值來填充Player屬性。 在您的示例中,這些位置沒有此類屬性,因此沒有設置Player的屬性。

如果我把[FromBody]玩家操作參數之前, 播放器設置為null。

[FromBody]屬性存在的情況下,模型綁定過程會根據請求所提供的Content-Type嘗試從主體讀取Content-Type 如果這是application/json ,則正文將解析為JSON並映射到Player的屬性。 在您的示例中,JSON解析過程失敗,因為它不知道如何從string轉換為ObjectId 發生這種情況時,控制器內的ModelState.IsValid將返回falsePlayer參數將為null

為了使此代碼正常工作,我的意思是要運行模型綁定程序,我從Controller繼承了控制器,並從Player參數中刪除了[FromBody]屬性。

刪除[FromBody] ,將尊重在Id屬性上設置的[ModelBinder(...)]屬性,因此代碼將運行。 但是,如果存在[FromBody ],則實際上將忽略此屬性。 幕后有很多事情要做,但本質上可以歸結為以下事實:您已經選擇將主體的模型綁定作為JSON,並且在這種情況下,模型綁定就停止了。


上面我提到過,由於不了解如何處理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; }

或者,您可以在Startup.ConfigureServices使用類似的方法全局注冊ObjectIdJsonConverter ,以便將其應用於所有ObjectId屬性:

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

您被誤認為是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;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM