Depending on the request content type you will have to decide which concrete class to instantiate. Let's take an example with application/json. For this content type out-of-the-box the Web API is using the JSON.NET framework to deserialize the request body payload into a concrete object.
So you will have to hook into this framework in order to achieve the desired functionality. A good extension point in this framework is writing a custom JsonConverter. Let's suppose that you have the following classes:
public abstract class ProductBase { public string ProductType { get; set; } } public class ConcreteProduct1 : ProductBase { public string Foo { get; set; } } public class ConcreteProduct2 : ProductBase { public string Bar { get; set; } }
and the following action:
public HttpResponseMessage Post(ProductBase product) { return Request.CreateResponse(HttpStatusCode.OK, product); }
Let's write a custom converter to handle this type:
public class PolymorphicProductConverter: JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(ProductBase); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var obj = JObject.Load(reader); ProductBase product; var pt = obj["productType"]; if (pt == null) { throw new ArgumentException("Missing productType", "productType"); } string productType = pt.Value<string>(); if (productType == "concrete1") { product = new ConcreteProduct1(); } else if (productType == "concrete2") { product = new ConcreteProduct2(); } else { throw new NotSupportedException("Unknown product type: " + productType); } serializer.Populate(obj.CreateReader(), product); return product; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
and the last step is to register this custom converter in the WebApiConfig:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add( new PolymorphicProductConverter() );
And that's pretty much it. Now you can send the following request:
POST /api/products HTTP/1.1 Content-Type: application/json Host: localhost:8816 Content-Length: 39 {"productType":"concrete2","bar":"baz"}
and the server will properly deserialize this message and respond with:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Expires: -1 Server: Microsoft-IIS/8.0 Date: Sat, 25 Jan 2014 12:39:21 GMT Content-Length: 39 {"Bar":"baz","ProductType":"concrete2"}
If you need to handle other formats such as application/xml you might do the same and plug into the corresponding serializer.
application/json?application/xml?application/x-www-form-urlencoded?