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.

5
  • Your request needs to be one of the child classes e.g. {"FirstMethod":{"Token":"0","Value":"0"}} You cannot instantiate an abstract class Commented Aug 13, 2014 at 20:01
  • Please check if this other question helps stackoverflow.com/questions/14124189/… Commented Aug 13, 2014 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. Commented Aug 13, 2014 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. Commented Aug 13, 2014 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 Commented Aug 14, 2014 at 0:22

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

Comments

2
+50

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

3 Comments

I mostly agree. But factory pattern is optional. It is sufficient to make some sort of distinguishing in the model binder.
You are not wrong. However, using Factory maintains stronger SRP IMO, and, it is what I would do. :)
Ah, got it. I understand the ModelBinder now. Thanks.
-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;
    }
}

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.