WebApi Core 2.2 OData resource/path not found - asp.net-core

I'm using WebApi Core 2.2. The Microsoft OData Client is adding a new parent record plus a subrecord (Deal+DealFee) from a WPF application. I'm hosting in IIS on Windows 10.
When I call container.SaveChanges(), it successfully calls the service to add the parent Deal record, but then it does a SECOND POST operation to this url (this is generated by the MS odata client lib):
POST http://localhost/mysite/odata/Deals(14)/DealFees
(note this includes the ID 14 which was just generated when adding the Deal)
This is two separate POSTs from the MS odata client lib, not a "deep insert" apparently. However, this results in a 404 (NotFound), which I can observe in Fiddler. The following urls DO work perfectly:
/odata/Deals
/odata/Deals(14)
/odata/DealFees
It seems like either the WebApi Core 2.2 service is not handling the POST to /Deals(14)/DealFees path, OR /Deals(14)/DealFees isn't a valid odata Uri? Is this kind of path generally supported in OData?
I don't know. Can anyone shed some light on what's going on?

Deep insert is not supported in WebAPI OData as of now. To me, it seems like the client is updating the resource set and the resource set for the navigation with two separate post requests and the reason you are getting a 404 is that there is no action mapped to the second request URI in the service.
The service can support this either by introducing a PostToDealFeesFromDeals controller action with default OData routing convention or use attribute routing to map the action for such requests.
If the action already exists then it might be that the first request did not finish creating the new record and the second request was fired, hence 404.

Related

ASP.NET Core multi attribute routing

In my ASP.NET Core Web API application, I have declared a route with multiple attributes like following
[HttpGet]
[Route("{tenantId?}/user/getsettings/{id?}")]
When I made a request from swagger, the server is returning 404 not found.
http://localhost:5163/api/1/user/getsettings/2
Is this possible with .NET Core?
Because your url actually not contains api. You can call url by using http://localhost:5163/1/user/getsettings/2. If you want to add api to your url you can add attribute to controller class by using [Route("api/v{version:apiVersion}/[controller]")].
Tip: You can achive same config with only one HttpGet("{tenantId?}/user/getsettings/{id?}") attribute.

Does ASP.NET Core handle pre-flight requests even without CORS being added as a middleware

I'm reading the book API Security in Action by Neil Madden. In the book, there is a section about CORS and how to attach the proper headers in Java. I am aware that there is already an AddCors and UseCors built in to ASP.NET Core, but for my edification I wanted to roll my own middleware.
However, I found that I was unable to receive any sort of OPTIONS requests, they were automatically being rejected somehow, and I was unable to respond to them manually, even when it was the first middleware in the pipeline. Since the UseCors middleware is able to intercept these pre-flight requests, I'm curious whether or not it is hooking into a deeper level of ASP.NET Core than I am able to.
TL;DR: Is ASP.NET Core (or Kestrel) performing some sort of automatic preflight request checking even when UseCors is not called?
The answer in the end is no, ASP.NET does not do anything fancy when it comes to preflight requests. I looked into the source code and the CorsMiddleware (and associated extensions) are relatively simple; most of the logic is contained within the ICorsService, and doesn't impact the middleware pipeline directly.
I'm unsure what my original issue was being caused by, but it is now resolved.
See below for the source code:
https://github.com/dotnet/aspnetcore/blob/a450cb69b5e4549f5515cdb057a68771f56cefd7/src/Middleware/CORS/src/Infrastructure/CorsMiddleware.cs
It is worth noting that if you add the CORS headers manually, you will get the following exception
System.InvalidOperationException: Endpoint ProjectName.Controllers.SomeController.Login (ProjectName) contains CORS metadata, but a middleware was not found that supports CORS.
Configure your application startup by adding app.UseCors() inside the call to Configure(..) in the application startup code. The call to app.UseCors() must appear between app.UseRouting() and app.UseEndpoints(...).
You can avoid this error by setting a flag to true in the custom CORS handler
httpContext.Items["__CorsMiddlewareWithEndpointInvoked"] = true;
However, I feel it's important to stress that this should only be done for education purposes; you should be relying on built-in CorsMiddleware and not rolling your own whenever possible (and it should hopefully always be possible).

Any alternatives to aspnet-request:serverVariable when using NLog with .Net Core?

As stated on the NLog GitHub the ${aspnet-request:serverVariable=String} layout renderer is not supported in .Net Core.
The documentation doesn't provide alternatives to many of the variables available under serverVariable.
My question is, are there any alternatives? Like to access remote address, server name, port etc? Or do I just have to write a bunch of custom layout renderers documented here and dependency inject all the stuff by hand?
For ASP.NET Core there as many new layout renders. The reason is that the API of ASP.NET Core is very different and the server variables can't be read like in ASP.NET (so non-core)
There are currently 13 layout renders for ASP.NET Core that renders a part of the request.
${aspnet-request} - ASP.NET Request variable.
${aspnet-request-contenttype} - ASP.NET Content-Type header (Ex. application/json)
${aspnet-request-cookie} - ASP.NET Request cookie content.
${aspnet-request-form} - ASP.NET Request form content.
${aspnet-request-headers} - ASP.NET Header key/value pairs.
${aspnet-request-host} - ASP.NET Request host
${aspnet-request-ip} - Client IP.
${aspnet-request-method} - ASP.NET Request method (GET, POST etc).
${aspnet-request-posted-body} - ASP.NET posted body / payload
${aspnet-request-querystring} - ASP.NET Request querystring.
${aspnet-request-referrer} - ASP.NET Request referrer.
${aspnet-request-url} - ASP.NET Request URL.
${aspnet-request-useragent} - ASP.NET Request useragent.
See also https://nlog-project.org/config/?tab=layout-renderers&search=package:nlog.web.aspnetcore
If you need something else, you could indeed create a custom renderer. If you need the http request you could use:
AspNetLayoutRendererBase.Register("aspnet-request-myrenderer", (logevent, httpcontext, config) => ... );
You need to reference the NLog.Web.AspNetCore package for that.

Web API - How to Prevent 404 HTML Response?

How can I change ASP.NET Web API to never return a text/html 404 response? I'd rather it send back XML/JSON with an ExceptionMessage or Message. It doesn't make sense to return HTML from an API, IMO.
Just to clarify, this is for cases when the URL truly is invalid.
Another problem is that I am hosting MVC and Web API in the same project, so I need to respond differently. I am guessing it will depend on whether the URL starts with "api".
You do not get those HTML data if you simply call
throw new HttpResponseException(HttpStatusCode.NotFound);
somewhere in your ApiController's methods.
You get this page only when routing mechanism can't fit your request to any of your ApiController's methods. So the HTML message is attached on the application level not in ApiControllers. Here is very good example how you can handle errors in your ASP.NET application Custom error pages on ASP>NET MVC3.
I would even go further and check if the request comes to WebAPI (not to MVC), and if so redirect to IHttpActionResult in ErrorsContoller.
How to prepare IHttpActionResult for response you can read here Action Results in Web API 2
Have you tried using an Exception Filter? This may allow you to capture exceptions and then set the response type "applications/json", message, etc.

ASP.NET WebAPI fails from MVC4 controller

I'm new to ASP.NET Web API and I'm struggling with a very strange problem.
I have some code which calls a RESTful service and it executes fine from a console project, but I can't get it to run from an MVC4 project running under .NET 4.0
The code to call the service is very simple:
internal string Test()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://testserver");
var task = client.GetAsync("/someUri")
var response = task.Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
}
As mentioned, called from a console project it works as expected and I get a response in milliseconds, however if I call the method from an action in my MVC4 controller after a few seconds I get a message stating that:
"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to repond".
Weirdly, when debugging the MVC4 version, the task status always shows as WaitingForActivation.
Running fiddler doesn't show any request being made from the MVC4 version, but again does for the Console version.
After a fairly serious bit of googling I can't find anyone else who seems to have had this problem, so I'm guessing that I've fundamentally misunderstood something, but at the moment I'm not sure what!
Updated 16:55 BST, 11/09/2012
To make things even weirder, I've just created a new MVC4 site and I can call the method without any problems! I'm now trying to compare the sites, however one was an existing site that was upgraded to MVC4 and the other is a new blank site, so spotting the relevant difference could be tricky.
Updated 16:44 BST, 14/09/2012
This is now looking like some infrastructure / networking issue.
I upgraded the project to VS2012 with .NET 4.5 so that I could use async/await to try the suggested implementations to avoid a deadlock. This didn't change anything so I went back to square 1.
I created a new solution with a new MVC4 project, a new services library and a unit test project to run the service library outside of MVC.
In the service library I created one method to call a public "what's my IP" service, and another to call a company service that's exposed publicly but only responds properly to company IP addresses.
For some background, I connect in to the company LAN via a VPN.
When disconnected from the VPN, in both unit tests and MVC, the IP service responds HTTP 200, the company service responds HTTP 404 as expected.
When connected to the VPN, unit tests both respond HTTP 200, MVC both timeout.
Next I ran MS Soap Tool locally and used that to proxy calls to the company services. All calls (whether from unit tests or MVC) show a request and response, but the unit test registers the response whilst the MVC controller does not.
My only other thought is that it could be something to do with the size of the reply? All the "successes" have very small replies other than the unit test calling the company service?
The Microsoft recommended way to upgrade an MVC3 to MVC4 site is to start with a completely new MVC4 site a migrate your views, controllers & code over. So I think that your upgrade steps may be part of your issue, since you were able to get it to work in the new MVC4 site you created. If you need to manually upgrade your existing site, I would follow the steps outlined in Upgrading ASP.NET MVC 3 Project to ASP.NET MVC 4