14

I realize that the Web API is REST focused, but I would still like to configure a single controller method that can handle a Command/Response scenario. So far I haven't been successful... is there a way to have the following class structure recognized by a single API endpoint?

[Serializable]
public abstract class Command{
    public int CommandId{get; set;}
}
[Serializable]
public class RegisterNewPersonCommand:Command{
   public string Name{get; set;}
}
//etc... various Command subclasses. Similar thing for Responses.

//now have a single endpoint to Handle Commands
public class CommandsController : ApiController{
   public Response HandleCommand(Command command){
      //handle Command based on which subclass it is
      //return appropriate Response subclass
   }
}

Thus far it doesn't seem the serialization system can handle this scenario, but I hope someone out there has found a way to do it :)

Maggie Ying
  • 10,095
  • 2
  • 33
  • 36
smalltowndev
  • 715
  • 8
  • 13
  • Even if it works, how does this help re-use any code? – Aliostad Nov 28 '12 at 17:06
  • 1
    I want to reduce code in my Controller. Instead of a boilerplate method for each subclass of Command (`HandleNewUserCommand(NewUserCommand){}, HandleUserPasswordChangeCommand(UserPasswordChangeCommand){}', etc), the single `HandleCommand` method will be responsible for using reflection and IoC to pass the Command along to the appropriate domain logic. – smalltowndev Dec 03 '12 at 23:46

1 Answers1

15

In order for polymorphism to work in Web API, you will need to enable type name handling and the data has to contain the type information.

You'll need to turn on TypeNameHandling in WebApiConfig.cs if you're using JSON in your scenario:

config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = 
    Newtonsoft.Json.TypeNameHandling.All;

Then, the content body that you are sending to your HandleCommand(...) action must contain the type information:

{"$type":"MvcApplication.Models.RegisterNewPersonCommand, MvcApplication", ... }

For XML, you'll need to use DataContract's KnownType...

By the way, is there any specific reason why you are using [Serializable] (since POCO types and [DataContract] types are also supported...)?

Maggie Ying
  • 10,095
  • 2
  • 33
  • 36
  • This seems to be the right idea. Is there a way to configure the HttpClient to include the $type info? In my tests, `var task= client.PostAsJsonAsync("api/commands/handlecommand", testCommand);` does not include the $type information when it serializes to json. – smalltowndev Dec 03 '12 at 22:32
  • 1
    This answered my question and helped me figure out how to specify formatting on the client end. For what it's worth, the simplest client code looks something like: `private void SendCommand(Command command){var json = new JsonMediaTypeFormatter {SerializerSettings = {TypeNameHandling = TypeNameHandling.All}};var response = client.PostAsync("api/commands/handlecommand", command, json).Result;//handle response}` – smalltowndev Dec 03 '12 at 23:03
  • This helped me.. The only other thing I had to search all over the internet to find was that Interfaces are not serializable, something I've known forever, but due to all the magic of Json serialization in .Net I seemed to forget:) So NO ICollection, IEnumerable, instead use List<> etc... Now I have polymorphic serialization working :) And now I wonder if I should be flattening my DTO's :p – GetFuzzy Dec 30 '13 at 20:55
  • `TypeNameHandling.Auto` is nicer because it doesn't write the instance type name when it matches the type of the field/property, which is usually the case for most types. – Roman Starkov Feb 16 '16 at 10:15