I have the following configuration:
builder.RegisterWebApiModelBinderProvider();
RegisterModelBinder<TypeModelBinder, object>(config, builder).InstancePerLifetimeScope();
Which uses the following method:
private static IRegistrationBuilder<TBinder, ConcreteReflectionActivatorData, SingleRegistrationStyle> RegisterModelBinder<TBinder, TTarget>(HttpConfiguration config, ContainerBuilder builder)
{
var targetType = typeof(TTarget);
var regBuilder = builder.RegisterType<TBinder>()
.WithParameter("validateAllProperties", true)
.AsModelBinderForTypes(targetType);
config.ParameterBindingRules.Add(targetType, p => p.BindWithModelBinding());
return regBuilder;
}
I haven't marked up my controllers in any way.
But the an example of the signature is:
[Route("data/{type}")]
public IHttpActionResult Post(string type, object data)
The binder:
public class TypeModelBinder : IModelBinder
{
public TypeModelBinder(ITypeResolver resolver, ITypeSerializer serializer, IContextETagExtractor eTagExtractor, bool validateAllProperties)
: base(resolver, serializer, eTagExtractor, validateAllProperties)
{
}
public override bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var typeInfo = default(RuntimeTypeInfo);
var data = default(T);
try
{
typeInfo = Resolver.Resolve(typeof(T).Name);
data = this.BindData(actionContext);
Validate(typeInfo.Type, data, bindingContext);
if (this.IsConcurrentMethod(actionContext.Request.Method) && typeInfo.IsVersioned)
{
ValidateAndSetConcurrencyToken(typeInfo.Type, data, actionContext, bindingContext);
}
}
catch (ArgumentException ae)
{
bindingContext.ModelState.AddModelError("Body", ae.Message);
}
catch (JsonSerializationException jse)
{
bindingContext.ModelState.AddModelError("Body", jse.Message);
}
catch (FormatException fe)
{
var errMsg = string.Format("{0} {1}", Constants.MODEL_ERROR_PREFIX_IF_MATCH_INVALID, fe.Message);
bindingContext.ModelState.AddModelError(Constants.MODEL_ERROR_KEY_IF_MATCH, errMsg);
}
catch (Exception e)
{
bindingContext.ModelState.AddModelError("Body", e.Message);
}
bindingContext.Model = data;
return bindingContext.ModelState.IsValid;
}
}
I have written a test to check that when I pass up an invalid object the model binding in the TypeModelBinder
fails.
However I have noticed the following behaviour. The first time through it acts as I expect and bindingContext.ModelName
is set to "data"
However I have noticed that when binding fails it immediately re-enters the the BindModel method of the binder, this time with bindingContext.ModelName
set to ""
it then adds another copy of whichever model error has been added and something in the owin stack throws a 500...
Is there anything obvious I am doing wrong when wiring up my modelbinders?