Failure to deserialize with [FromBody] tag - c#

I'm got an api endpoint I'm trying to hit that uses the [FromBody] tag with an object in the parameter line like so:
[HttpPost]
[Route("update")]
public IHttpActionResult MyWebApiMethod([FromBody] MyObject objectValue)
{
I've been writing a UWP app that communicates with this web api, and I've successfully hit the endpoint just fine before; however, for whatever reason, as of lately, I can no longer hit this specific endpoint. The controller for the api initiliazes, and when I enable tracing, I can see the controller has managed to associate the correct endpoint, and seems to have deduced that the data model of the object I'm sending matches the one the api is expecting, but when it goes to do the deserialization as a result of the [FromBody] tag, it just goes into never never land, and doesn't do anything. Eventually my app times out with its request.
Out of many tests I did to try and resolve what was causing this issue. I found 2 notable things, but I can't figure out how to fix the root issue.
First off, was a temporary workaround. If I changed the endpoint to receive a JObject instead and used
var newValue = JsonConvert.DeserializeObject<MyObject>(JsonConvert.Serialize(objectValue);
then, the endpoint would get hit, and my newValue would be properly deserialized.
Second, going with the chance that my datamodel on the client was different than the one our api was using, I turned the data model into a nuget (I'm having issues making this nuget compatible for Universal Windows 10 apps due to some reference issues, so I made a normal Class Library) then install the nuget into a fairly bare bones console application, and made the same call to the same endpoint, and it hit the end point fine without having to resort to using JObject in the method parameter.
So, somewhere, somehow, the deserialization process happening when the data packet comes from a Universal app is different than the one coming from a generic console application. Unfortunately, I can't figure out how to tie into this deserialization process when the [FromBody] takes over before going into the Method.
Any help on resolving this issue would be great.

Related

Generating links to self when running behind reverse proxy

How can I generate absolute links to other resources in my RESTful API app when the app is meant to be accessed via a reverse proxy that publishes just the paths under /api?
My app is an API with a common layout of routes like /api, /swagger and /health. It is published on my employer's API management under a path of the form /business-area/api-name/v1. Calling the API both directly and through the API gateway overall works: calling https://api-gateway.company.com/business-area/api-name/v1/some-resource results in internal call to https://my-app.company.com/api/some-resource.
The issue is that the links in my app's responses point directly to the backend app (https://my-app.company.com/api/another-resource), not the the API gateway (https://api-gateway.company.com/business-area/api-name/v1/another-resource). They are generated using IUrlHelper.
I solved the domain by the ForwardedHeadersMiddleware and adding the X-Forwarded-Host by a policy on the API management. Sadly, we are allowed to use just extremely simple policies, so if we published the API using multiple gateways, the current solution would generate link to just a single one. But that is an issue to be solved somewhen later; now it works OK.
I could not get the path to work well. I tried changing the paths using a middleware as hinted in the ASP.NET Core behind proxy docs:
app.Use((context, next) =>
{
context.Request.PathBase = "/business-area/api-name/v1";
if (context.Request.Path.StartsWithSegments("/api", out var remainder))
{
context.Request.Path = remainder;
}
return next();
});
When I insert this middleware high in the pipeline, it breaks the routing, but if I insert it low enough, the routing works OK and only link generation is affected. But it seems that only PathBase change really affects link generation as the /api is still in the generated URI. I can see that the Path of the request object is really changed, though, so it is probably just that link generation uses the routing info directly, without passing through my middleware, which makes sense, but it rules out the middleware solution.
Is wrapping the standard IUrlHelper in my own implementation and postprocessing the URLs it returns a good way to go? I don't know how to go about that. I use the IUrlHelper from the ControllerBase.Url property and debugger tells it is actually an instance of Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper. Doing the wrapping in every action seems wrong (repetitive, error-prone).
Changing the routing so that /api moves to the root is my last resort option as it mixes up the namespaces: technical endpoints like /health and /swagger would live among the actual resources of the API. Is there a reasonable way to avoid that while keeping the links working? This all seems like a pretty standard problem and I am surprised I cannot find how to solve it.
We use .NET 5 and we will migrate to .NET 6 as soon as it is out, if that makes any difference.

In Signalr .net core - where should validation take place?

If I have a hub method that accepts parameters
e.g.
public IObservable<MyStreamItem> StreamData(SomeRequestData request)
{}
How do I propogate validation errors in the request?
An actual http request is only made when the socket connection is established.
So subsequent calls to Hub methods dont pass through any middleware. They are just frames/messages in the open websocket.
I've had a look at this package which is for the previous version of Signalr (for the full .net framework)
https://github.com/AGiorgetti/SignalR.Validation
This uses a HubPipelineModule which doesn't seem to exist in the new .net core Signalr.
Is there an appropriate place in the pipeline that I can tap into to do the validation?
Or should it be done in the hub method itself? And if so, how would you conditionally return a structured set of errors, as opposed to what the actual return type is meant to be?
thanks
There are currently no HubPipelineModules in SignalR alpha but we're looking at an equivalent for preview 2. Today, you'd need to do it in the method and potentially throw an error to get it back to the client.

local testing of API Post events that expect JSON data

I have a POST IHttpActionResult method in my API controller that uses the [FromBody] attribute.
[Route("gameworld/generate")]
public IHttpActionResult PostNewWorld([FromBody] WorldData json)
Normally, when I want to test my controllers locally, I just goto the URL.
However, I can't do that with this method because I get this error:
The requested resource does not support http method 'GET'.
Is there a way for my local environment to see that this is a POST event?
Thanks!
First of all, ASP.NET Web Api is smart enough to deserialize your json into an object, so unless you really expect a string value in the body you can just put your type in there.
[Route("gameworld/generate")]
public IHttpActionResult PostNewWorld([FromBody] WorldData newWorld)
You can either test it manually with tools like Postman or Swagger, but manual tests are usually done once and then forgotten. This opens up for regression bugs where you make a change in the future, forget to retest the endpoint and break the application using the api.
Therefor, you should write unit tests to keep checking your code and prevent regression bugs. I've been using MyTested.WebApi on several projects and you can test both the routing and the actual calls. For example:
MyWebApi
.Server()
.Working()
.WithHttpRequestMessage(req => req
.WithRequestUri("api/bugs")
.WithMethod(HttpMethod.Post)
.WithContent(myJsonString)
.ShouldReturnHttpResponseMessage()
.WithStatusCode(HttpStatusCode.Created)
.WithResponseModelOfType<BugReport>()
.Passing(r => r != null);
There's a lot of things to be tested, so be sure to read the docs.

Discovery API failing with JSON error

I have a very simple program meant to use the Google Discovery API. However, when I try to use the getRest call, it fails as below. Any ideas? I'm using the most recent version of the Discovery API from NuGet, VS 2012 Pro.
DiscoveryService service = new DiscoveryService(new Google.Apis.Services.BaseClientService.Initializer());
//this works no problem
service.Apis.List().Execute();
//this works on the API reference page's Try It! area, but fails here
service.Apis.GetRest("admin", "directory_v1").Execute();
This is the resulting error that is thrown in the Google.Apis.Requests.ClientServiceRequest class, on the Execute() method - line 96:
JsonSerializationException was unhandled
Additional content found in JSON reference object. A JSON reference object should only have a $ref property. Path 'schemas.User.properties.name.description', line 1251, position 20.
This is a documented error. Get the generated discovery to discovery its own discovery document #79
The only work around I have currently found is to use the Client lib to log in and then manually make my requests from the Discovery API. Its messy and doesn't work very well because you can use the class structure that the Discovery API class library would have given you.

Interesting Issue found when using WCF Data Service Client Library to query data from a WCF Data Service

I have a simple data model with 3 tables (Account, Contact, and User) with the following relationships:
User -> Account (1 - Many) Account -> Contact (Many - 1)
I am exposing my data via an OData (v3) WCF Data Service, which is consumed by a .NET client that uses the WCF Data Service Client Library. I used the Add Service utility to generate the client proxy code to call the data service.
All methods in the client class uses the class's single DataServiceContext object for calling the web service. i.e.:
DC.WhEntities svcClient = new DC.WhEntities(new Uri(BaseUrl));
What I am having a hard time trying to figure out is why the same query request to the service starts failing after the 6th time. I have literally tried all possible ways to construct a call to the data service:
First approach:
DataServiceQuery<DC.User> users = svcClient.Users.Expand("Accounts");
QueryOperationResponse<DC.User> response = users.Execute() as QueryOperationResponse<DC.User>;
var user = response.FirstOrDefault(u => u.Id == long.Parse(key.ToString()));
Second approach:
string queryString = string.Format("Users({0}L)?$expand=Accounts", key.ToString());
foreach (var user in response) {...}
The last statement in both of the above solution starts failing with a message below after it has executed successfully 6 times in a row:
The response payload is a not a valid response payload. Please make sure that the top level element is a valid Atom element or belongs to 'http://schemas.microsoft.com/ado/2007/08/dataservices' namespace.
**StackTrace:**
at System.Data.Services.Client.Materialization.ODataMaterializer.CreateODataMessageReader(IODataResponseMessage responseMessage, ResponseInfo responseInfo, Boolean projectionQuery, ODataPayloadKind& payloadKind)
at System.Data.Services.Client.Materialization.ODataMaterializer.CreateMaterializerForMessage(IODataResponseMessage responseMessage, ResponseInfo responseInfo, Type materializerType, QueryComponents queryComponents, ProjectionPlan plan, ODataPayloadKind payloadKind)
at System.Data.Services.Client.DataServiceRequest.Materialize(ResponseInfo responseInfo, QueryComponents queryComponents, ProjectionPlan plan, String contentType, IODataResponseMessage message, ODataPayloadKind expectedPayloadKind)
at System.Data.Services.Client.QueryResult.ProcessResult[TElement](ProjectionPlan plan)
at System.Data.Services.Client.DataServiceRequest.Execute[TElement](DataServiceContext context, QueryComponents queryComponents)
When this happens, my WCF Data Service just stopped working and returns a response with
error on line 1 at column 83: Unescaped '<' not allowed in attributes values.
I am not sure if I am missing anything fundamental or if I'm constructing the WCF Data Service Client request incorrectly or if there is something on the WCF Data Service side that doesn't like the same client requesting the same thing more than 6 times.
I've already spent a few days and I meant 3+ days trying to figure this out. I am new to WCF Data Service and I thought I could learn from this tutorial, but so far I got more pain than gain.
I am experiencing similar issue, suddenly my server started (maybe some updates inflicted this, yet the cause is unknown) to return bad responses. If I start my server it works for some time, lets say responds to few requests in a normal manner and then starts to break xml structure of the OData feeds, resulting in <, hexadecimal value 0x3C, is an invalid attribute character. Line 2, position 72. exception.
SOLUTION:
I solved the problem by following this feed
If you have WCF tracing configured, make sure logMessagesAtTransportLevel="false" is turned off, otherwise you will experience this issue.
I tried setting logMessagesAtTransportLevel to false and still got the error.
Then i remembered seeing this issue before when I had an assembly conflict. I went and created a brand new service and this solved my problem even when I had logMessagesAtTransportLevel set to true on my client. This ensured me that the problem was the service.
Although my solution solved my problem, I still don't know the exact issue and I already ran out of time to find it out. However, It is good to see that people are willing to help out and I truly appreciate the effort.
Thanks everyone again for your help.
Qster123.

Categories