Sending JSON between localhost ports - asp.net

I had an ASP.NET MVC app running on port 8799 that posted JSON data via an XMLHttpRequest to an MVC controller handler. This pipeline was working perfectly fine.
I recently spun up a second app, this time an Express app running on port 8080, that again posts JSON in the same way (same exact code, using again an XMLHttpRequest), to the same MVC controller running on port 8799. This time, however, it appears as though while the request is received by the controller, there is no data on it.
I'm wondering if this is because I am making a cross-origin request between these two ports. Going off of this hypothesis, I added the following custom headers to my web.config file:
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost" />
<add name="Access-Control-Allow-Headers" value="X-AspNet-Version,X-Powered-By,Date,Server,Accept,Accept-Encoding,Accept-Language,Cache-Control,Connection,Content-Length,Content-Type,Host,Origin,Pragma,Referer,User-Agent" />
<add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, OPTIONS" />
<add name="Access-Control-Max-Age" value="1000" />
</customHeaders>
This does not seem to solve the issue. How can I debug this problem, I don't seem to be throwing any particularly useful exceptions?

The issue did turn out to be CORS. For whatever reason the web.config file was not being used by my .NET application. Thus, I setup CORS setting in my Startup.cs file instead, using services.AddCors() in my Configure() call.

Related

Microsoft.AspNet.WebApi.Cors: The requested resource does not support http method 'OPTIONS'

I realize that this question seems to come up frequently. I've gone through each one and tried many of the suggested solutions. One solution did seem to get me to a "working" state, but it does not seem like it addressed the actual underlying issue, but instead somehow worked around it. So, let me explain my scenario and what I've tried.
I'm writing an AspNet WebApi application that will be served up by IIS. My application is currently using convention based routing, as opposed to attribute routing. My application needs to handle CORS pre-flight requests (OPTIONS). I've installed the required Microsoft.AspNet.WebApi.Cors package, from Nuget. Below are the versions of the AspNet packages that I'm using:
<package id="Microsoft.AspNet.Cors" version="5.2.4" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.4" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.4" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi.Cors" version="5.2.4" targetFramework="net461" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net461" />
Based on my reading, the Microsoft.AspNet.WebApi.Cors package is supposed to add support for handling CORS pre-flight requests. This means I won't have to add manual OPTIONS action methods, etc - the nuget package brings that for me. So, I've configured the package so that it enables CORS for all available controllers, based on a commonly referenced blog post. To my understanding, this should be all that is needed for my application to begin responding to OPTIONS requests. Below is my configuration for completeness sake - it shows that I also configure some other things:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var jsonMapping = new RequestHeaderMapping("Accept", "text/html", System.StringComparison.OrdinalIgnoreCase, true, "application/json");
var corsAttribute = new EnableCorsAttribute("*", "*", "*");
config.Formatters.JsonFormatter.MediaTypeMappings.Add(jsonMapping);
config.Services.Replace(typeof(IFilterProvider), new UnityFilterProvider(UnityConfig.Container));
config.EnableCors(corsAttribute);
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Lastly, I've seen some reference to some items in the web.config regarding the OPTIONSVerbHandler. Mine is left at whatever the default value is, which is shown below ...
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
Finally, with all of these items in place, it appears that my application still fails to handle OPTIONS requests. Using postman, I simply send an OPTIONS request to any of my endpoints and regardless of endpoint I receive the following 405 response ...
Allow →POST
Cache-Control →no-cache
Content-Length →76
Content-Type →application/json; charset=utf-8
Date →Wed, 14 Feb 2018 20:50:14 GMT
Expires →-1
Pragma →no-cache
Server →Microsoft-IIS/10.0
X-AspNet-Version →4.0.30319
X-Powered-By →ASP.NET
X-SourceFiles →=?UTF-8?B?QzpcVXNlcnNccmNvbGVcRG9jdW1lbnRzXFZpc3VhbCBTdHVkaW8gMjAxN1xQcm9qZWN0c1xQb3J0YWwuUkVTVFxQb3J0YWwuUkVTVFxhY2NvdW50XGF1dGhlbnRpY2F0ZQ==?=
{"Message":"The requested resource does not support http method 'OPTIONS'."}
Here's some of the things I've tried to fix this:
I tried removing the OPTIONSVerbHandler from web.config, which seemed to make no change.
I tried explicitly putting EnableCorsAttribute on individual action methods and controllers, which seemed to make no change.
I explicitly put HttpOptionsAttribute on an action method, which seemed to fix the issue. This seems like the incorrect approach, though, as that attribute is for saying that action method is for handling OPTIONS requests, which is not at all what I want. I thought Microsoft.AspNet.WebApi.Cors package brings its own OPTIONS handler.
I tried switching to attribute-based routing, as opposed to the original convention-based routing. Didn't seem to change anything.
Does anybody know what could be causing this issue? It doesn't seem like my Microsoft.AspNet.WebApi.Cors package is correctly wired up, or doing anything. The response has no CORS headers in it. It looks like AspNet doesn't think it has any OPTIONS handler in place, despite me enabling it in my configuration.
I've seen this question asked in several other locations. Most answers focus on web.config hacks, custom filter attributes, other custom code, etc, as the solution. I want to mark what ended up being the solution in my scenario, which I believe to be many of the other folks' scenarios ...
The Microsoft.AspNet.WebApi.Cors package does indeed work and handle OPTIONS pre-flight requests. It only kicks into action when the appropriate CORS headers are present, though.
So, in my scenario, I was testing this using a tool, such as Postman. I was testing by sending explicit OPTIONS requests. Well, many of these tools don't send CORS pre-flight requests, and even then, when sending manual OPTIONS requests they do not include the required CORS headers that trigger the Microsoft.AspNet.WebApi.Cors functionality.
So, in the end, for me, there was no technical issue or bug, it was a lack of knowledge about how the nuget package works. When I switched back over to my JavaScript project, and made the request across origin, using fetch, the requests worked. Chrome sends the pre-flight OPTIONS, with appropriate CORS headers.
So, make sure you're testing using appropriate CORS headers, if you're using a tool like Postman!
This isn't a CORS issue per se. It's your web server not allowing OPTIONS requests. I realize this isn't an answer, but it should help you get on the path to narrowing it down some more.

.NET Sessions with React Cross Origin

I'm currently working on putting together my first React application, and I'm using fetch in order to communicate with my API which is currently being developed using the .net webapi. I am running into a bit of a wall however with getting my session variables on the .net server to work properly. Currently I'm running the react application separately locally and using the url of my .net server in order to make a request to the API server. Both of which are on the same machine despite using separate ports. The request is going through, but when i try to make a follow up request to get the current user information, it is not pull any information. I have looked, and it seems no cookies are being stored locally in the browser, so I'm assuming this is the issue. The code for my fetch looks something like this:
let url = "http://localhost:50405";
let requestObject = {
mode: 'cors',
credentials: 'include'
}
fetch(url + '/api/currentuser/get', requestObject).then((res) => {
console.log(res);
});
However, res is null even after the session has been set.
Also, on the server end, I have the following in my config file in order to allow for the cross-site request:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:3000" />
<add name="Access-Control-Allow-Credentials" value="true" />
</customHeaders>
</httpProtocol>
I figured I would see if there's something obvious I am missing here.

Azure is blocking request that come from the same server

Context
Umbraco CMS website runs on Azure as App Service
Scheduled Publishing
One of the Umbraco functionalities is to allow to publish content on a given time. The publish functionality makes a HTTP call to same web site (or a different server but same website in load balanced environment).
API call url:
http://sample-site-umbraco.azurewebsites.net/umbraco/RestServices/ScheduledPublish/Index
IP Security
Due to client requirements, access to the site is restricted to a given list of IP addresses. This task is being completed with IP Security restriction in web.config.
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="52428800" />
</requestFiltering>
<ipSecurity allowUnlisted="false" denyAction="NotFound">
<!-- "clear" removes all upstream restrictions -->
<clear />
<!-- permit the loopback address -->
<add ipAddress="127.0.0.1" allowed="true" />
...
...
...
<!-- domain Name for Scheduled Publishing -->
<add allowed="true" domainName="sample-site-umbraco.azurewebsites.net"/>
</ipSecurity>
</security>
Problem
When IP Security is turned on, the HTTP call to publish API is being blocked as not white listed one.
API call response Status Code and Content:
404 - NotFound
"The resource you are looking for has been removed, had its name changed, or is temporarily unavailable."
Problem Thread on our.umbraco.com
Fix attempts
Adding domainName to the list of allowed entries
<!-- domain Name for Scheduled Publishing -->
<add allowed="true" domainName="sample-site-umbraco.azurewebsites.net"/>
This solution doesn't work. Calls are still being blocked.
Question
How this can be fixed? Is there any functionality that can be override?
Ok, I've found the solution. I think it will work.
I've found this question on stackoverflow and it worked :)
Solution
Solution is to add ALL outbound IP addresses into System.WebServer > Security > ipSecurity > [List].
Outbound Ip Addresses are comma separated list of ips.
You need to add all of them to the WhiteList in web.config.
Drawback
I'm not sure if the list of Outbound Ips is static and will not change in the future...

ASP.Net, CORS, and HTTPS

I want to access some things in local and session storage located on the same domain, but using a different protocol. The data is cached on HTTP and needs to be retrieved under HTTPS.
How can I configure ASP.Net to allow this without hard-coding the web site name in my web.config? This is what I have now
<add name="Access-Control-Allow-Origin" value="https://my-web-site" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
Do you also need Access-Control-All-Methods?
<add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE" />
I suggest you follow the methods suggest here:
http://brockallen.com/2012/06/28/cors-support-in-webapi-mvc-and-iis-with-thinktecture-identitymodel/

Can ASMX be configured to respond to an HTTP 1.1 OPTIONS request?

It seems that ASMX implicitly does not allow the OPTIONS verb. I'm posting this question because I'm using jQuery AJAX calls with POST which first queries the server for available OPTIONS before the POST verb** is issued.
By default Web.config maps all verbs to legacy ASMX, as shown in this partial configuration sample, so everything should be routed properly:
<system.webServer>
<requestFiltering>
<verbs>
<add verb="OPTIONS" allowed="true"/>
<add verb="GET" allowed="true"/>
<add verb="POST" allowed="true"/>
</verbs>
</requestFiltering>
<handlers>
<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
However the HTTP Response is always 405 for an OPTIONS request. For example, the following request is given:
OPTIONS http://localhost:35920/MarkupTransfomer.asmx HTTP/1.1
Host: localhost:35920
Access-Control-Request-Method: POST
and always results in:
HTTP/1.1 405 Method Not Allowed
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.50727
The jQuery AJAX call looks like the following, a recommendation from a fairly recent Encosia blog post for working with ASMX:
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "http://localhost:35920/MarkupTransfomer.asmx",
data: "{'hi'}",
dataType: "json"
});
** Note: I don't want to switch the client to use GET instead of POST.
Summary question:
Can I configure ASP.NET to allow ASMX to handle OPTIONS
Requests inherently without error?
Other ideas I've considered
I can look for a way to tell jQuery.ajax(..) not issue the OPTIONS verb before POST?
If what I'm asking from legacy ASMX isn't possible, I've considered two other potential ideas:
implement a System.Web.IHttpHandler, stick it in the web.config handlers section to manage only the verbs=OPTIONS requests to path=*.asmx outside of the default ASMX behaviour. This would be a doable workaround.
I can switch over to using WCF. However I want to know if ASMX is too unwieldly first.
Before I take action, I want to query the community for things I might have overlooked, or for better options (no pun).
UPDATE #1
Yep, my web service is being hosted on a different domain. Apparently there are a plethora of issues to navigate with Same Original Policy. This update is definitely worthwhile information to the context of the question all things considered!
However I want to ensure this question stays at the ASMX server and HTTP levels. Although I might be facing related browser issues in the near future, those aren't of consequence to solving the HTTP protocol level of this question. Thanks.
Looks like maybe it doesn't apply because I don't see it in the question, but my answer to this was that In the web config I had to remove the instruction to <remove name="OPTIONSVerbHandler" />
Kudos to the person who posted it near the end of this SO question
I don't know that ASMX was ever able to handle OPTIONS, even for AJAX requests. What exactly do you want to return in the case of an OPTIONS verb? What does the client intend by using OPTIONS?
Also, I've looked into this from the point of view of ASP.NET dealing with the OPTIONS verb. In my case, it was prohibited at the level of the root web.config, not at the ASP.NET or ASMX level. The default mapping for that verb is
<add path="*" verb="*" type="System.Web.HttpMethodNotAllowedHandler" validate="True" />
This corresponds to the 405 error you received.

Resources