I'm calling a method of my WebAPI sending a json that I would like to match (or bind) with a model.
In the controller I have a method like:
public Result Post([ModelBinder(typeof(CustomModelBinder))]MyClass model);
'MyClass', wich is given as a parameter is an abstract class. I would like that at, depending of the type of json passed, the correct inherited class is instantiated.
To achieve it, I'm trying to implement a custom binder. The problem is that (I don't know if it's very basic but I can't find anything) I don't know how to retrieve the raw Json (or better, some kind of serialization) that comes in the request.
I see:
- actionContext.Request.Content
But all methods are exposed as async. I don't know who this fits with passing the generate model to the controller method...
Thanks a lot!
You can call async methods normally, your execution will be simply suspended until the method returns and you can return the model in standard manner. Just make a call like this:
It will give you raw JSON.
You don't need to implement it by yourself. JSON.NET has native support for it.
You have to specify the desired TypeNameHandling option for the JSON formatter, like this (in
global.asax
application start event):If you specify
Auto
, like in the above sample, the parameter will be deserialized to the type specified in the$type
property of the object. If the$type
property is missing, it will be deserialized to the parameter's type. So you only have to specify the type when you're passing a parameter of a derived type. (This is the most flexible option).For example, if you pass this parameter to a Web API action:
The parameter will be deserialized to an object of
MyNamespace.MyType
class.This also works fro sub-properties, i.e., you can have an object like this, which specifies that an inner property is of a given type
Here you can see a sample on JSON.NET documentation of TypeNameHandling.Auto.
This works at least since JSON.NET 4 release.
NOTE
You don't need to decorate anything with attirbutes, or do any other customization. It will work without any changes in your Web API code.
IMPORTANT NOTE
The $type must be the first property of the JSON serialized object. If not, it will be ignored.
COMPARISON TO CUSTOM JsonConverter/JsonConverterAttribute
I'm comparing the native solution to this answer.
To implement the
JsonConverter
/JsonConverterAttribute
:JsonConverter
, and a customJsonConverterAttribute
JsonConverter
whenever your types or properties changeIn the author of the answer there's a comment regarding security. Unless you do something wrong (like accepting a too generic type for your parameter, like
Object
) there is no risk of getting an instance of the wrong type: JSON.NET native solution only instantiates an object of the parameter's type, or a type derived from it (if not, you getnull
).And these are the advantages of JSON.NET native solution:
TypeNameHandling
once in your app)(1): if you want to receive parameter values that don't inherit from the same base type, this will not work, but I see no point on doing so
So I can't find any disadvantages, and find many advantages on JSON.NET solution.
WHY USING CUSTOM JsonConverter/JsonConverterAttribute
This is a good working solution that allows customization, that can be modified or extended to adapt it to your particular case.
If you want to do something that the native solution cannot do, like customizing the type names, or inferring the type of the parameter based on available property names, then do use this solution adapted to your own case. The other one cannot be customized, and will not work for your needs.
If you want to use the TypeNameHandling.Auto but are concerned with security or dont like api consumers needing that level of behind the scenes knowledge you can handle the $type deserialize your self.
Then hook this up in global.asax.Application__Start
finally i have used a wrapper class and [JsonProperty(TypeNameHandling = TypeNameHandling.Auto)] on a properpty containing the object with different types as i have not been able to get it to work by configuring the actual class.
This approach allows consumers to include the needed information in their request while allowing the documentation of the allowable values to be platform independent, easy to change, and easy to understand. All without having to write your own converster.
Credit to : https://mallibone.com/post/serialize-object-inheritance-with-json.net for showing me the custom deserializer of that field property.
You don't need a custom model binder. Nor do you need to muck about with the request pipeline.
Take a look at this other SO: How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?.
I used this as the basis for my own solution to the same problem.
Starting off with the
JsonCreationConverter<T>
referenced in that SO (slightly modified to fix issues with serialization of types in responses):And now you can annotate your type with the
JsonConverterAttribute
, pointing Json.Net to a custom converter:Now you can use the base type as a parameter:
And if we were to post:
Then
arg
would be an instance of theDerivedClass
, but if we posted:Then you'd get an instance of the
DefaultClass
.EDIT - Why I prefer this method to
TypeNameHandling.Auto/All
I do believe that using the
TypeNameHandling.Auto/All
espoused by JotaBe is not always the ideal solution. It might well be in this case - but personally I won't do it unless:When Json.Net
TypeNameHandling.Auto
orAll
are used, your web server will start sending out type names in the formatMyNamespace.MyType, MyAssemblyName
.I have said in comments that I think this is a security concern. Mention was made of this in some documentation I read from Microsoft. It's not mentioned any more, it seems, however I still feel it's a valid concern. I don't ever want to expose namespace-qualified type names and assembly names to the outside world. It's increasing my attack surface. So, yes, I can not have
Object
properties/parameters my API types, but who's to say the rest of my site is completely hole-free? Who's to say a future endpoint doesn't expose the ability to exploit type names? Why take that chance just because it's easier?Also - if you are writing a 'proper' API, i.e. specifically for consumption by third-parties and not just for yourself, and you're using Web API, then you're most likely looking to leverage the JSON/XML content-type handling (as a minimum). See how far you get trying to write documentation that's easy to consume, which refers to all your API types differently for XML and JSON formats.
By overriding how JSON.Net understands the type names, you can bring the two into line, making the choice between XML/JSON for your caller purely based on taste, rather than because the type names are easier to remember in one or the other.