In the RC1 I use the following code for abstract classes or interfaces binding:
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;
}
}
This binder only reads model type (messageType
parameter) from query string and overrides metadata type. And the rest of the work performed by standard binders such as BodyModelBinder
.
In Startup.cs I just add first binder:
services.AddMvc().Services.Configure<MvcOptions>(options => {
options.ModelBinders.Insert(0, new MessageModelBinder());
});
Controller:
[Route("api/[controller]")]
public class MessageController : Controller {
[HttpPost("{messageType}")]
public ActionResult Post(string messageType, [FromBody]ICommand message) {
}
}
How can I perform this in RC2?
As far as I understand, now I have to use IModelBinderProvider
. OK, I tried.
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);
}
}
But I cannot specify NoResult
. If I do not specify bindingContext.Result
, I get null model in controller.
If I specify bindingContext.Result
, I get empty model without setting model fields.
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…