local testing of API Post events that expect JSON data - c#

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.

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.

WebClient Request in Console Application doesn't hit method when published

The basic setup is this: I have an MVC application that starts up a console application based on user input. Suffice to say, at the end of the console application's code, I'm trying to make a web request to notify the system that the console application has finished execution. Now, when I'm debugging and running locally, I hit the web API method no problem.
However, when I publish the MVC application and copy it over to the server (in the respective wwwroot folder, it ceases to work.
The code for generating the request is relatively simple:
public void SendRequest(Uri uri)
{
using(var client = new WebClient())
{
// previously I was sending data, but this works locally regardless of the last parameter
// also, an example of a URI would be something like 'http://localhost:666/Ctrl/SomeFunction' though 'Ctrl' is replaced by the name of the controller
client.UploadValuesAsync(uri, "POST", new NameValueCollection());
}
}
Then the endpoint function is pretty barebones (for the sake of the question mostly):
public JsonResult SomeFunction()
{
// do stuff
}
I have logging code around the web request to see if it threw any exceptions, and other debugging code in the controller method as well. It doesn't throw any errors, doesn't run into any exceptions, and works fine locally. I'm not exactly sure what I'm missing.
As far as avoiding any SSL errors, it was suggested by a team member that I use
ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;. I know it's far from ideal, and should generally be avoided, but for now, it'll have to do.
UPDATE: I used UploadValues and checked the response. It comes back with some HTML that seems like an authentication redirect. So now the idea is to figure out how to exclude that route from authentication. So far the [AllowAnonymous] attribute on the controller hasn't been helpful (or messing around with the [Authorize] attributes in general). The suggestion I was given by a team member was to remove authorization for that controller action in the Startup.Auth.Cs file. I'm not sure if there's a better way for that or not. I might post a second question in regards to that.
at the end of the console application's code, I'm trying to make a web request
May be it's because local request is fast enough to be performed right before console application is closed, but not when it's published?
Try await client.UploadValuesTaskAsync(uri, "POST", new NameValueCollection()); or client.UploadValues(uri, "POST", new NameValueCollection()); It should not let console application shut down before request is over.

Failure to deserialize with [FromBody] tag

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.

OutputCacheAttribute - does it vary on HTTP method?

I have a following method in my controller:
[AcceptVerbs("GET", "OPTIONS", "HEAD")]
[OutputCache(Duration="3600" VaryByParam="None" VaryByHeader="Access-Control-Request-Headers;Origin")]
public new ActionResult Index()
{
//action body
}
It is handling both GET and OPTIONS (CORS) calls and so far there has been no problem. I've recently added the OutputCache attribute and started wondering if it's possible to cache a flawed OPTIONS response, by calling the GET. Namely, let's say a user with malicious intents calls my GET call with the CORS headers that I vary on. Is it possible that a user making the OPTIONS call with the same headers (this time used properly), will instead get the response from the previously cached GET, therefore nuking the whole OPTIONS call for the duration of the cache?
I was looking for an information, if the HTTP method is considered when creating an output cache entry, but just can't find it anywhere. I have tested this locally and it seemed that the GET and OPTIONS output could never get mixed up, no matter how hard I messed with the headers. Still, I would be much more relieved if I knew that what I described could really never happen.

Caching GET requests with Web API on Azure

I'm developing a Web API RESTful service on the Azure platform.
I thought that the default client-side caching behavior would be to cache GET requests (since GET is idempotent and all).
To my surprise when I deployed the service to Azure all responses were sent with a Cache-Control: private header or other cache-disallowing header.
I tried the solution suggested in this question, it did work locally in IIS but did not work once we deployed to Azure. I could not find anything in the documentation about this ability which I thought was very basic in a RESTful service, I really hope that I'm missing something obvious, in MVC it was very easy.
tl;dr
We need to cache GET requests on the client side when using Azure and Web API.
I don't believe Azure is doing anything to you in this respect. It's a matter of you needing to specify exactly what caching properties you want for your resource(s).
With WebAPI you can control what caching properties your response has via the CacheControlHeaderValue which is accessible via the myHttpResponseMessage.Headers.CacheControl property.
Assuming you had a controller action like this:
public Foo Get(int id)
{
Foo myFoo = LoadSomeFooById(id);
return myFoo;
}
You'll need to do something like this to take control of the caching explicitly:
public HttpResponseMessage Get(int id)
{
Foo myFoo = LoadSomeFooById(id);
HttpResponseMessage myHttpResponseMessage = this.Request.CreateResponse(HttpStatusCode.OK, myFoo)
CacheControlHeaderValue cacheControlHeaderValue = new CacheControlHeaderValue();
cacheControlHeaderValue.Public = true;
cacheControlHeaderValue.MaxAge = TimeSpan.FromMinutes(30);
myHttpResponseMessage.Headers.CacheControl = cacheControlHeaderValue;
return myHttpResponseMessage;
}
Many of the other properties related to caching that you'd expect are also available on the CacheControlHeaderValue class, this is just the most basic example.
Also, bear in mind my example is extremely brute force/simplistic in that all the caching behavior/logic is right there in the action method. A much cleaner implementation might be to have an ActionFilterAttribute which contains all the caching logic based on attribute settings and applies it to the HttpResponseMessage. Then you could revert to the more model centric action method signature because you would, in this case, no longer need access to the HttpResponseMessage anymore at that level. As usual, many ways to skin the cat and you have to determine which works best for your specific problem domain.
Take a look at this http://forums.asp.net/post/4939481.aspx it implements caching as an attribute that modifies the HTTP response.
Disclaimer: I haven't tried it.
I would recommend this https://github.com/filipw/AspNetWebApi-OutputCache
Simple, quick and has various options to cache.
Hope that helps

Categories