Model Binder of Json.Net not being used when i post an object - asp.net-web-api

To clarify...
I configure my WebApiConfig like so:
config.Formatters.JsonFormatter.SerializerSettings.Binder = new TypeNameSerializationBinder("namespace.{0}, assembly");
config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
This should allow me to bind derived classes to base class.
And binder does work when WebApi serializes objects to JSON, and sends them to client, but when I post them back to server, the binder isn't used (BindToType method never gets called), and my objects get bound to base class.
When i serialize/deserialize objects manually with this settings it all works fine.
Any ideas?

I had the same problem when trying to deserialize complex objects with a custom JsonConverters. I needed this because I'm using DbGeometry for storing users locations.
I broke my head on this a couple of days, I really thought I was doing something wrong, because every time I posted an geometry to the Web API, the complex type parameter was set to null. This while JsonConverter was perfectly able to convert the json to an filled object.
My workaround for this is written below. I don't like that I can't just use the parameter as I'm supposed to do. But it works, at last.
[HttpPost]
public MyComplexType SaveMyComplexType()
{
var json = Request.Content.ReadAsStringAsync().Result;
var myComplexType = JsonConvert.DeserializeObject<MyComplexType>(json);
//todo: validation and save to db
return myComplexType;
}

After some research, I found that this is a bug in ASP.NET Web Api. When the url encoded parameters are parsed, it just creates a new JsonSerializer (without passing global settings).
I filed it here
http://aspnetwebstack.codeplex.com/workitem/609

Related

Api Platform - returning data from external API

I'm currently using API Platform to display some data in Elasticsearch. This works fine, but I now have another feature that I'm looking at.
My application needs to deal with a 3rd-party API that needs to hit an endpoint and return some data.
Within my app, I'd like to be able to hit (/api/logistics/{action} - where action is an endpoint, such as login) and this then hits my app layer and returns data (3rd-party can be re-named)
The API calls to the 3rd party are fine, but I'm unsure how to display the response.
I've seen https://api-platform.com/docs/core/data-providers/ which looks like i can create a custom response.
Do i still need to create an entity/model and configure the #ApiResource() with a controller that uses my Data Provider?
If so, then what do i need to add in my annotation, since I won't have an id identifier
I'm fairly new to API Platform and I've not used the Data Provider functionality before
I will not be storing the data from the 3rd party API, just doing a HTTP call, retrieving the response and hopefully displaying it via Api Platform
Thanks
You are mosly right about the dataprovider. But as the docs page General Design Considerations states, "you have to write a plain old PHP object (POPO) representing the input and output of your endpoint. This is the class that is marked with the #ApiResource annotation. This class doesn't have to be mapped with Doctrine ORM, or any other persistence system."
So no, it does not need to be an Entity, but there must be a class marked with the #ApiResource annotation (but putting it in the Entity folder may help to make the #ApiResource() tag work - or adding the folder of your class in api/config/packages/api_platform.yaml).
For an item "get" endpoint your POPO needs an id. The poperty - or if there is only a getter, the getter - must be marked with the #ApiProperty(identifier=true) tag. Usually the easiest way to make one is by imploding/encoding some strings from the response of the external api call that together are unique for the response and will not change. Your dataprovider will have to explode/decode the id and use the components to make the external api call.
For a "post" operation you need a datapersister instead of a dataprovider. Apip will instatiate and populate your POPO and pass it to the datapersister and from there you can make the call to the external api and return an object as the result. If your object is not the same type of POPO you should specify "output"=TheOutputClass::class or put the operation on the output class and specify "input"=TheInputClass::class (replace TheOutputClass or TheInputClass by the actual class name)
For "put" and "patch" you need both a dataprovider, a datapersister and an id. They can have different input and output classes, see the docs about DTOs.
A collectionoperations with method "get" may seem convenient because you can just pass it any query string but your CollectionDataProvider must return an iterable.

OData V4 (webapi) patch with NavigationProperty collection breaks deserailization on server

I’m trying to go against a Web API Patch method that accepts a Delta. My entity has a navigational property that is a collection. The domain model is trivial, I have a team (entity) and a collection of members (navigational property).
I’m using HttpClient directly instead of the OData client.
The problem I’m running into is that when I send a patch request with the payload = a Team, the Delta deserialized in my controller is null when I include the navigational property collection in my payload.
Eg (ignore that the missing quotes, this is typed in}:
{ Name: Foo } -> serializes to Delta successfully.
{Name: Foo, Members : [] } -> fails to serialize and Delta is null.
Is this supported? I could not find concrete documentation online about whether you can supply navigational properties as an entire collection on patch (not looking for merge support, just want full replace of the collection.)
I also tried tweaking my client to directly send a Delta payload, but the default JsonMediaTypeFormatter is unable to serialize this into a valid request body (always produces empty Json), and the ODataMediaTypeFormatter throws an exception saying it cannot write an object of type Delta, although I have initialized it with every ODataPayloadKind.
I’m sure I’m either doing something wrong or missing something basic, assuming using Delta + patch with HttpClient is not this challenging.
For OData spec, it says:
11.4.3 Update an Entity
...
The entity MUST NOT contain related entities as inline content. It MAY contain binding information for navigation properties. For single-valued navigation properties this replaces the relationship. For collection-valued navigation properties this adds to the relationship.
That is you can't put the navigation properties in the Patch request content.
From Web API OData, it has these codes and these codes. It means that it doesn't allow to patch navigation property form entity.
However, you can shoot your target by following steps:
Patch the principal entity (team)
Patch the dependent entities (members)
use the $ref to update the link
You can refer to my simple test project.

How to Pass a Route Parameter to a Property on my Controller with WebApi?

Suppose I have a route like the following:
/api/v1/{organisationId}/resource/{resourceId}
Since there are likely to be a number of different types of resources hanging off an organisation, I'd like a common way of dealing with the organisationId.
What I thought of doing is creating a base controller (inheriting from ApiController, obviously) which would have an OrganisationId property.
Is there any way to get WebApi to pass the organisationId route parameter to a property on the controller, rather than to a parameter on the action method?
How about this?
public int OrganisationId
{
get
{
return Convert.ToInt32(Request.GetRouteData().Values["organisationId"]);
}
}
This is not using the model binding layer and is assuming that organisationId would be present in the route data. It is simple and a bit less flexible than what web API model binding offers.
The downside to reading the route from within the property getter is unit testing could be a challenge. Another possible approach is to explore implementing your own IHttpControllerActivator that sets the OrganizationId property. Yes, for this to work, you will need a base controller or the property defined in the individual controllers, when not derived from the base. If you go with base, you can cast IHttpController type to the base and set the property. Or you can resort to reflection.

Can I reuse a Spring MVC View instance?

I created a custom view that uses Json.Simple to serialize the model and write the JSON to the response directly.
For some requests, I need to send back a static JSON message, so I am wondering can I reuse a View instance I created earlier (with the message already set)?
My View class is thread-safe.
Sure. As long as you make sure it's thread-safe, as you say, there's no reason that your controller can't return the same View object multiple times. Unorthodox, but valid.
I do not see any problem in reusing an already created view since the render method gets current response object.

Can I store xmlDocument object in Session?How to do seralization?

I have one XML document which I want to store it inside session so on each post back I do not need to load it from its physical path again. We are using state server.
When I tried to store it in Session I get an error:
Exception Details: System.Runtime.Serialization.SerializationException: Type 'System.Xml.XmlDocument' in Assembly 'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.
My code is something like this:
string resumeSection = string.Empty;
resumeSection = resume.GetXMLSection(1)
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(resumeSection);
Session["USERXML"] = xmloc;
How to do seralization?
As I am getting below error
Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode.
When you store any object in Session it should be marked [serealizable] so you should serialize your Object before storing it to session or viewstate.
to be honest you shouldn't really be putting complex types into session state
you should only store simple types or light weight business entities
not objects like XmlDocument.
I think the best way to go would be to use custom serialization. If the
class is not too big, you can persist the XmlDocument to a string and then
just store that value when serializing the instance. Then, when
de-serializing, you can just pull that from the SerializationInfo instance.
you can get quick idea from here
this past SO post may also answer your question to some extent
I know this is 2 years old, however I was facing this same problem myself, and since I found a solution, I wanted to share it with you. Devjosh is right, we shouldn't store complex objects in sessions, however sometimes is very usefull, so it is good to know there is a solution.
I noticed that if you store the object like xml/dataset/class, you might experience this error. I tried to store it as a generic object and seems to work. On the same code that was storing a dataset, I had the error, by storing in like generic object, works fine.
Here is a simplified example:
public static void InsertSessionObject(string item, object obj)
{
HttpContext.Current.Session.Add(item, obj);
}
public static object GetSessionObject(string item)
{
return HttpContext.Current.Session[item];
}
public static void RemoveSessionObject(string item)
{
HttpContext.Current.Session.Remove(item);
}
DataSet lastResults = GetDatasetResults();
InsertSessionObject("myDataset", lastResults);
lastResults = (DataSet)GetSessionObject("myDataset");
RemoveSessionObject("myDataset");

Resources