29

I am asking if anyone knows if it is possible to to pass into a Web Api a concrete class that inherits from a abstract class.

For example:

public abstract class A
{
    A();
}

public class B : A
{
}

[POST("api/Request/{a}")]
public class Request(A a)
{
}

At present I have looked around and most solutions seem to say that using TypeNameHandling will work.

JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

However this is not that case. Also my model is being passed from a console app to the webapi. I have read that I may be able to deserialize the json object and after attempting this a few times I decide this was not going to work.

I have looked into creating a customer model binder however, I do not want to make my application more complex that it has to be. At present I inherit from the abstract class with 3 models but may in the future extend this. As you may note adding custom model binders may require multiple binders unless there is a way of making one binder generic for all types of the abstract class.

To expand on this in my console app I have instantiated class b as such and then passed it to the ObjectContent before posting to my webapi

item = B();

//serialize and post to web api
MediaTypeFormatter formatter;
JsonMediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
jsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
formatter = jsonFormatter;

_content = new ObjectContent<A>(item, formatter);
var response = _client.PostAsync("api/Request", _content).Result;

when the webapi action is called the object is null

4
  • You can probably take a look at my answer regarding inheritance and modelbinding over here: stackoverflow.com/a/15518804/1184056 Commented Jun 24, 2013 at 14:33
  • Also looks like the following issue in JSON.NET was fixed in March of this year. Probably try getting latest version of Json.net and see if your issue still repros: json.codeplex.com/workitem/23891 Commented Jun 24, 2013 at 14:50
  • 2
    I have looked into this further and answered my own question. You can not pass an abstract class via model binding as you can not instantiate a class that is abstract. The only way around this issue is to make the base none abstract, this way model binding works perfectly fine. Commented Jul 15, 2013 at 10:28
  • By definition, your request parameter is a DTO. Avoid inheritance for DTOs, it serves little to no purpose, compose if you must. Think about what you'd be implementing - you get a property bag from the HTTP request and then pick a random implementation of A and fill in only the base fields to pass to your method? You don't have a well formed object and it doesn't serve any purpose above a plain concrete DTO. Commented Aug 27, 2019 at 20:17

5 Answers 5

2

If one really wanted to implement what is asked in the question, there is a custom way to do it.

First, create a custom json converter that is inherited from JsonConverter, in it pick a target class and deserialize an instance.

Then, in your WebApiConfig.Register you add your new converter into config.Formatters.JsonFormatter.SerializerSettings.Converters and enjoy this monstrosity in action.

Should you do it? No.

Understanding how to use such API will bring no joy to any new users, documenting this will not be easy, and most importantly - there are no benefits in implementing it this way. If input types are different, then they deserve separate API methods with different URLs. If only few properties are different - make them optional.

Why the example did not work? The TypeNameHandling is from Json.NET, Web API knows nothing about it, and type information is not part of the JSON spec, so there is no standard way to solve this particular issue.

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

Comments

0

Please change [POST("api/Request/{a}")] to [POST("api/Request")]. Your parameter comes from body, not from route.

Comments

0

You could use JsonSubTypes converter for this purposes. It is very useful and simple tool for polymorphic deserialization with usage of Json.NET.

Comments

0

I am using .Net 9 and I could not find a clean way to do this autoamtically. I use Automapper for mapping between objects and get the answer like this

httpClient.GetFromJsonAsync<ADto>

but even Automapper support inheritance, but it did not work smoothly. it works one way by sending data to api but by getting data from Api the derived data is missed and it always return only base class (A) .

at the end I made a flat class for Api:

public abstract class ADto
{
    //fields of class A
    ...
    //fields of class B

}

and by receiving the data from Api in Client a custom convertor is used witch is using Automapper simple mapping to do the job. (I had a field to determine type)

 public static object MapADtoToA(ADto aDto, IMapper mapper)
 {
     A a = new();
     a = mapper.Map(aDto, a);
   if (a.Category == Category.B)
   {
       return mapper.Map<B>(aDto);
   }

    return a;
}

And in Automapper profile instead of using inheritance the normal mapping should be define for Dto.

CreateMap<A, ADto>()
CreateMap<B, ADto>()

Comments

-1

This is possible via the default model binding. check below method.

public abstract class RequestBase
{
    public int ID { get; set; }
}

public class MyRequest : RequestBase
{
    public string Name { get; set; }
}



[RoutePrefix("api/home")]
public class HomeController : ApiController
{
    [HttpPost]
    [Route("GetName")]
    public IHttpActionResult GetName([FromBody]MyRequest _request)
    {
        return Ok("Test");
    }
}

enter image description here

1 Comment

I guess this is not the intention of Ian, he wants to use the base class (not the derived class) as the type accepted by the api method.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.