[英]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
將返回false
而Player
參數將為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.