Reputation: 8580
I am making a REST API call with the following message body:
{"Method":{"Token":"0","Value":"0"}}
I am getting a 400 Bad Request response from the api with the following body:
{"Message":"The request is invalid.","ModelState":{"request.Method.Token":["Could not create an instance of type Namespace.ActionMethod. Type is an interface or abstract class and cannot be instantiated. Path 'ActionMethod.Token'."]}}
The method which is receiving the api call looks like this:
public MethodResponse MakeMethodCall([Required] [FromBody] MethodRequest request)
MethodRequest
has a Method
property which is an abstract type.
public class MethodRequest
{
public ActionMethod Method { get; set; }
}
public abstract class ActionMethod
{
public string Token { get; set; }
}
public class FirstMethod : ActionMethod
{
public string Value { get; set; }
}
How can I call the REST API and have it recognize that the type of Method
is FirstMethod
, instead of it trying to instantiate the abstract type ActionMethod
?
Note that I will need to have more implementations of ActionMethod
in the future (ie. SecondMethod
), so the solution will need to include an extensible ActionMethod
(interface would also be fine).
EDIT
It would also be reasonable to include an enum
to identify which implementation of ActionMethod
was being targeted by the API call.
I'm currently using a solution which has an ActionMethodType
enum and both FirstMethod
and SecondMethod
fields. I'm checking these fields based on the value of ActionMethodType
. This works, but I would like to have a single [Required]
field into which I could pass any implementation of ActionMethod
.
Upvotes: 2
Views: 4113
Reputation: 4081
If I understand you correctly, you could implement this with a custom model binder and a factory pattern.
public class MethodRequestBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
HttpRequestBase request = controllerContext.HttpContext.Request;
//use the request object to make a call to your factory for the
//appropriate ActionMethod subtype you want to create, or however
//else you see fit.
var curActionMethod = MyFactory.Get(request.QueryString);
var boundObj = new MethodRequest()
{
Method = curActionMethod
}
return boundObj;
}
}
register your model binder in app_start:
ModelBinders.Binders.Add(typeof(MethodRequest), new MethodRequestBinder());
now, just decorate your controller action method:
public ActionResult Index([ModelBinder(typeof(MethodRequestBinder))] MethodRequest request)
{
//etc..
}
I used this as a starting point: http://www.codeproject.com/Articles/605595/ASP-NET-MVC-Custom-Model-Binder
Upvotes: 2
Reputation: 29083
Can't be done. How would the framework know to instantiate FirstMethod for this parameter? What if you had another subclass of ActionMethod that also had a Value property? Now it's even more ambiguous for the framework to figure out on it's own. You could do a bunch of work, creating a custom formatter (http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx) but ultimately it would be easier to just have a single class that includes all possible properties a client could send OR have separate API endpoints for the client to call using different concrete types as the parameter.
Upvotes: 4
Reputation: 3255
Remove the abstract keyword from your ActionMethod, or mark the Token property abstract and override it in the inherited classes:
public abstract class ActionMethod
{
public abstract string Token { get; set; }
}
public class FirstMethod : ActionMethod
{
public string Value { get; set; }
public override string Token
{
get;
set;
}
}
Upvotes: -1