Asp Net Core RC2. Связывание абстрактной модели класса
В RC1 я использую следующий код для привязки абстрактных классов или интерфейсов:
public class MessageModelBinder : IModelBinder {
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext) {
if(bindingContext.ModelType == typeof(ICommand)) {
var msgTypeResult = bindingContext.ValueProvider.GetValue("messageType");
if(msgTypeResult == ValueProviderResult.None) {
return ModelBindingResult.FailedAsync(bindingContext.ModelName);
}
var type = Assembly.GetAssembly(typeof(MessageModelBinder )).GetTypes().SingleOrDefault(t => t.FullName == msgTypeResult.FirstValue);
if(type == null) {
return ModelBindingResult.FailedAsync(bindingContext.ModelName);
}
var metadataProvider = (IModelMetadataProvider)bindingContext.OperationBindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
bindingContext.ModelMetadata = metadataProvider.GetMetadataForType(type);
}
return ModelBindingResult.NoResultAsync;
}
}
Это связующее читает только тип модели (messageType
параметр) из строки запроса и переопределяет тип метаданных. А остальная часть работы выполняется стандартными связующими, такими как BodyModelBinder
,
В Startup.cs я просто добавляю первый компоновщик:
services.AddMvc().Services.Configure<MvcOptions>(options => {
options.ModelBinders.Insert(0, new MessageModelBinder());
});
контроллер:
[Route("api/[controller]")]
public class MessageController : Controller {
[HttpPost("{messageType}")]
public ActionResult Post(string messageType, [FromBody]ICommand message) {
}
}
Как я могу выполнить это в RC2?
Насколько я понимаю, теперь я должен использовать IModelBinderProvider
, Хорошо, я попробовал Startup.cs:
services.AddMvc().Services.Configure<MvcOptions>(options => {
options.ModelBinderProviders.Insert(0, new MessageModelBinderProvider());
});
ModelBinderProvider:
public class MessageModelBinderProvider : IModelBinderProvider {
public IModelBinder GetBinder(ModelBinderProviderContext context) {
if(context == null) {
throw new ArgumentNullException(nameof(context));
}
return context.Metadata.ModelType == typeof(ICommand) ? new MessageModelBinder() : null;
}
}
ModelBinder:
public class MessageModelBinder : IModelBinder {
public Task BindModelAsync(ModelBindingContext bindingContext) {
if(bindingContext.ModelType == typeof(ICommand)) {
var msgTypeResult = bindingContext.ValueProvider.GetValue("messageType");
if(msgTypeResult == ValueProviderResult.None) {
bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
return Task.FromResult(0);
}
var type = typeof(MessageModelBinder).GetTypeInfo().Assembly.GetTypes().SingleOrDefault(t => t.FullName == msgTypeResult.FirstValue);
if(type == null) {
bindingContext.Result = ModelBindingResult.Failed(bindingContext.ModelName);
return Task.FromResult(0);
}
var metadataProvider = (IModelMetadataProvider)bindingContext.OperationBindingContext.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
bindingContext.ModelMetadata = metadataProvider.GetMetadataForType(type);
bindingContext.Result = ModelBindingResult.Success(bindingContext.ModelName, Activator.CreateInstance(type));
}
return Task.FromResult(0);
}
}
Но я не могу указать NoResult
, Если я не укажу bindingContext.Result
Я получаю нулевую модель в контроллере. Если я укажу bindingContext.Result
, Я получаю пустую модель без установки полей модели.
1 ответ
У меня было похожее требование с пользовательскими моделями привязки и абстрактными классами, и предложения, опубликованные dougbu на github AspNet / Mvc / questions / 4703, работали для меня. Я обновил RC1 до ASP.NET Core 1.0, и мне нужно было модифицировать привязку моей пользовательской модели с его рекомендациями. Я скопировал и вставил его код ниже на случай, если разорвется ссылка на проблему с github. Прочитайте комментарии в выпуске github, чтобы узнать о безопасности кода, который создает на сервере объекты запрошенного типа.
MessageModelBinderProvider
public class MessageModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType != typeof(ICommand))
{
return null;
}
var binders = new Dictionary<string, IModelBinder>();
foreach (var type in typeof(MessageModelBinderProvider).GetTypeInfo().Assembly.GetTypes())
{
var typeInfo = type.GetTypeInfo();
if (typeInfo.IsAbstract || typeInfo.IsNested)
{
continue;
}
if (!(typeInfo.IsClass && typeInfo.IsPublic))
{
continue;
}
if (!typeof(ICommand).IsAssignableFrom(type))
{
continue;
}
var metadata = context.MetadataProvider.GetMetadataForType(type);
var binder = context.CreateBinder(metadata);
binders.Add(type.FullName, binder);
}
return new MessageModelBinder(context.MetadataProvider, binders);
}
}
MessageModelBinder
public class MessageModelBinder : IModelBinder
{
private readonly IModelMetadataProvider _metadataProvider;
private readonly Dictionary<string, IModelBinder> _binders;
public MessageModelBinder(IModelMetadataProvider metadataProvider, Dictionary<string, IModelBinder> binders)
{
_metadataProvider = metadataProvider;
_binders = binders;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
var messageTypeModelName = ModelNames.CreatePropertyModelName(bindingContext.ModelName, "messageType");
var messageTypeResult = bindingContext.ValueProvider.GetValue(messageTypeModelName);
if (messageTypeResult == ValueProviderResult.None)
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
IModelBinder binder;
if (!_binders.TryGetValue(messageTypeResult.FirstValue, out binder))
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
// Now know the type exists in the assembly.
var type = Type.GetType(messageTypeResult.FirstValue);
var metadata = _metadataProvider.GetMetadataForType(type);
ModelBindingResult result;
using (bindingContext.EnterNestedScope(metadata, bindingContext.FieldName, bindingContext.ModelName, model: null))
{
await binder.BindModelAsync(bindingContext);
result = bindingContext.Result;
}
bindingContext.Result = result;
}
}