2

The API Call

I am making a REST API call with the following message body:

{"Method":{"Token":"0","Value":"0"}}

400 Response

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'."]}}

Code Information

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; }
}

Question

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.

Luke Willis
  • 8,429
  • 4
  • 46
  • 79
  • Your request needs to be one of the child classes e.g. {"FirstMethod":{"Token":"0","Value":"0"}} You cannot instantiate an abstract class – Jon Aug 13 '14 at 20:01
  • Please check if this other question helps http://stackoverflow.com/questions/14124189/can-i-pass-an-interface-based-object-to-an-mvc-4-webapi-post – Claudio Redi Aug 13 '14 at 20:02
  • @Mangist using "FirstMethod" does not work because the key in the json request is the parameter name not the type name. I'll edit my question so this is clearer. – Luke Willis Aug 13 '14 at 20:10
  • @ClaudioRedi I think that something like that *might* work for me, but I'm unsure how to implement a working solution for my situation because I have a parameter with a property of an abstract type instead of an interface for a parameter. – Luke Willis Aug 13 '14 at 20:21
  • 1
    yeah thats not gonna work. you could come up with some other type of custom model binder that knows how to figure out which concrete type to create but the example in that other question doesn't apply here – Robert Levy Aug 14 '14 at 00:22

3 Answers3

4

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.

Robert Levy
  • 28,747
  • 6
  • 62
  • 94
2

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

solidau
  • 4,021
  • 3
  • 24
  • 45
-1

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;
    }
}
Jon
  • 3,230
  • 1
  • 16
  • 28
  • Unfortunately, making `Token` abstract didn't fix the issue. I get the same Bad Request response. Removing `abstract` from `ActionMethod` *does* result in a successful call; however, debugging shows that the `Value` field isn't being included in my `MethodRequest` object. I need some way to tell the method that the `Method` property is of type `FirstMethod`. – Luke Willis Aug 13 '14 at 20:59