OPTIONS preflight not handled in jQuery - c#

I have a webpage at localhost:63342 with a jQuery ajax call in that webpage, to my webservice server at localhost:55000. In the webservice I set the Access-Control headers.
In Chrome's developer tools, Network tab, I can see that the OPTIONS preflight thing is sent, and the response header has the following, which looks great.
Access-Control-Allow-Headers:x-requested-with, X-Auth-Token, Content-Type, Accept, Authorization
Access-Control-Allow-Methods:POST, OPTIONS, GET
Access-Control-Allow-Origin:*
Cache-Control:private
Content-Length:0
Date:Fri, 06 Jun 2014 13:30:58 GMT
Server:Microsoft-IIS/8.0
However, the response to the OPTIONS request hits the error function of my jQuery ajax call. Developer tools shows me that the browser prepares the POST, but fails it because it thinks the resource does not have the Access-Control-Allow-Origin header set. The browser does not try to send the POST. Here is the error message from the console of the browser:
XMLHttpRequest cannot load http://localhost:55000/webservice/ws.svc/CreateOuting. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342' is therefore not allowed access.
Its as though jQuery is interfering in the OPTIONS, POST process. Any ideas on what I should do to make this work?
Here is my ajax call
$.ajax({
type: 'POST',
data: JSON.stringify(obj),
headers: { "Content-type": "application/json" },
url: base_url + 'CreateOuting',
crossDomain: true,
success: function (an_outing) {
$('#listviewOutings').listview('refresh', true);
$('#boxOutingName')[0].value = '';
myLib.OpenBasicPopup('Success', 'The outing name was saved.')
},
error: function (err) {
alert(err.statusText); // response to OPTIONS request ends up here
}
});
Here is how I set the headers in the method on the server (.NET C#):
public bh_Outing CreateOuting(string strOuting) {
try
{
//for all cors requests
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
//identify preflight request and add extra headers
if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST, OPTIONS, GET");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "x-requested-with, X-Auth-Token, Content-Type, Accept, Authorization");
return null;
}
// do stuff
Here is the interface for that method. I dont think its perfect yet, but I dont think its the problem either.
[WebInvoke(UriTemplate = "*", Method = "*", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
bh_Outing CreateOuting(string strOuting);
Thanks for taking a look at this. I am truly stuck.
Update, 6/17/14, 5:38 PM EST
I added a element to my webconfig as in this post, and this made no change to my results.

This may be not the reason, but have you tried enabling cors in jQuery? Before any cors ajax request:
jQuery.support.cors = true;

The cause of the problem that made me post the above question, seems to be that Chrome was incorrectly representing what was really happening. As I wrote above, what was happening was:
... the response to the OPTIONS request hits the error function of my
jQuery ajax call. Developer tools shows me that the browser prepares
the POST, but fails it because it thinks the resource does not have
the Access-Control-Allow-Origin header set. The browser does not try
to send the POST.
To try to get more detail, I installed and used Microsoft Message Analyzer. It showed that the OPTIONS request was being sent; the OPTIONS response was being sent; the POST request was being sent back to the webservice (!!!) and a POST response was being sent.
This was a huge breakthrough because now, instead of trying to solve a CORS problem (the error message that Chrome developer tools showed) I started working to solve a "Not Found" and then a "Bad Request" error (the error messages that MS Message Analyzer showed).
The other technique I used that helped a lot was that I set up tracing in my webservice. Tracing wrote stuff to a log file which default-opens in an event viewer and provided the details that I needed to figure out the (real) issues. Here is what I put in the webconfig to enable tracing.
<system.diagnostics>
<trace autoflush="true" />
<sources>
<source name="System.ServiceModel"
switchValue="Critical, Error, Warning"
propagateActivity="true">
<listeners>
<add name="myListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "c:\logs\ServiceModelExceptions.svcLog" />
</listeners>
</source>
</sources>
</system.diagnostics>
I made no changes to the CORS settings (jQuery and web.config) and its running fine in Chrome now. Also, I have the webservice out on the www, so it is truly cross-domain, not just localhost:63342 to localhost:55000.

Related

Credentials flag is 'true', but the 'Access-Control-Allow-Credentials

I am trying to connect to a ASP.NET Web-API Web Service from an AngularJS page and I am getting the following
Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin 'http://localhost:221' is therefore not allowed access.
var cors = new EnableCorsAttribute("http://localhost:221", "*","GET,PUT,POST,DELETE");
config.EnableCors(cors);
Using this AngularJS
$http({
method: 'GET',
url: 'http://localhost:1980/api/investors/62632',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
withCredentials: true
// withCredentials: true,
}).then(function onUserComplete(response) {
// this callback will be called asynchronously
// when the response is available
}, function onError(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
After reading many articles I add this to the web.config
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:221" />
</customHeaders>
</httpProtocol>
and I get this error message
The 'Access-Control-Allow-Origin' header contains multiple values localhost:221, localhost:221, but only one is allowed. Origin localhost:221 is therefore not allowed access.
Which really doesn't make any sense as I have added it once and it doesn't find it but I add it to web.config and get an error saying its been added multiple times. I have read many articles and can't seem to find the right answer. I am using Google Chrome. Would be very grateful for help as I am pulling my hair out right now.
For whom, who uses WebApiConfig.cs:
config.EnableCors(new EnableCorsAttribute("*", "*", "*") { SupportsCredentials = true });
The header is added twice once by the code and the other by the web.config. The CORS support is used to allow for the addition of headers for CORS purposes. The configuration custom headers also add response headers to any request, so you may want to remove the config setting.
var cors = new EnableCorsAttribute..
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:221" />
</customHeaders>
Since both of those areas are adding the same origin twice, you get the multiple values on the header.
When making an AJAX call with the parameter withCredentials: true, the response header should have the Access-Control-Allow-Credentials = true. You need to add that via code using SupportsCredentials = true for the CORS attributes. Otherwise you will get the error
"Credentials flag is 'true', but the 'Access-Control-Allow-Credentials is ''"
For more information, on the withCredential parameter and the response header look at this article:
http://www.ozkary.com/2015/12/api-oauth-token-access-control-allow-credentials.html
hope it helps.
I came across this question while trying to hit a webapi on .net core from an angular2 app. I had to add AllowCredentials() to the cors configuration in my Configure method in the Startup.cs to look like the following.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCors(builder =>
builder
.AllowCredentials()
.WithOrigins("http://localhost:3000"));
...
}
Try the method outlined here for preflight requests:
enabling cross-origin resource sharing on IIS7
And use the Chrome extension Postman or Fiddler for easier debugging of CORS. I'm willing to bet that you are adding the header twice, but without your entire code, it is difficult to debug. (heck, CORS is difficult to debug even with the code).
To me, it appears that you shouldn't have both the web.config setting as well as the global EnableCors() attribute - this causes the doubles.
You don't appear to be adding the Access-Control-Allow-Credentials anywhere server side, but it might be added by the AllowCors attribute, I am not sure. (I am partial to handling CORS in OWIN myself)

Cannot load web api method using Backbone.js

I have a blank asp.net solution with 5 different projects. One of them is asp.net web api and other one is blank web site with bunch of html pages. I am using backbone.js/jQuery to make calls to the web api within the solution. My blank web site is running on different port and my web api is running on different port.
//Collection
var AuditsCollection = Backbone.Collection.extend({
url: 'http://localhost:56501/api/searchaudits',
sync: function (method, model, options) {
if (!options.crossDomain) {
options.crossDomain = true;
}
options.timeout = 1000;
alert(method + ": " + JSON.stringify(model));
return Backbone.sync(method, model, options);
},
});
var audits = new AuditsCollection();
// Model
var Audit = Backbone.Model.extend({ });
var audit = new Audit({ auditNumber: "A12" });
audits.add(audit);
// POST CALL - I am sending this model and expecting back another JSON object in response.
audit.save({}, {
success: function (response) {
alert("Got audits successfully" + response);
},
error: function (response) {
alert("Error.. Go home now");
}
});
I still get this error
XMLHttpRequest cannot load http://mydomain:56501/api/searchaudits. Request header field Content-Type is not allowed by Access-Control-Allow-Headers.
I think I figured out what the issue is. The project is setup in the following structure.
Error:
-- Solution
-- Project 1 (Web API) - running on `http://localhost:80/api`
-- Project 2
-- Project 3
-- Project 4 (Views) - running on `http://localhost:3000/`
So when I started making ajax requests, it started giving exceptions like
XMLHttpRequest cannot load http://localhost:80/api/searchaudits. Request header field Content-Type is not allowed by Access-Control-Allow-Headers.
OPTIONS localhost:80/api/searchaudits 405 (Method Not Allowed)
XMLHttpRequest cannot load /api/searchaudits
I did not know that it is going to consider domains with different ports ad different domains.
Solution:
Override Backbone.Sync method - reference
In your Web Api projects web.config file, add the following
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept"/>
</customHeaders>
</httpProtocol>
</system.webServer>
There are answers in StackOverflow regarding this issue in bits and pieces, but this is an effort to put the complete solution together in one place.

Ajax - 'Origin localhost is not allowed by Access-Control-Allow-Origin'

I'm relatively new to Ajax and was just tasked with this cross-domain call. We have a text box on our web page that a user will use to preform a search of company names. By clicking a button next to the text box, the Ajax call will be requested. Unfortunately the web service is located in a separate domain, so this is naturally causing issues.
Below is my best attempt at making this work. I should also note, the purpose of this call is to return the results in an XML format, which will be parsed in the success portion of the request.
Here is the error message again:
Origin http://localhost:55152 is not allowed by Access-Control-Allow-Origin.
I'm at a loss as to what to do for a work-around, any ideas would be greatly appreciated.
function GetProgramDetails() {
var URL = "http://quahildy01/xRMDRMA02/xrmservices/2011/OrganizationData.svc/AccountSet?$select=AccountId,Name,neu_UniqueId&$filter=startswith(Name,\'" + $('.searchbox').val() + "\')";
var request = $.ajax({
type: 'POST',
url: URL,
contentType: "application/x-www-form-urlencoded",
crossDomain: true,
dataType: XMLHttpRequest,
success: function (data) {
console.log(data);
alert(data);
},
error: function (data) {
console.log(data);
alert("Unable to process your resquest at this time.");
}
});
}
This error is due to the restriction enforced in cross-domain resource sharing. This has been implemented as a part of security feature to restrict the clients(domain) of a resource via cross domain calls. When you send a request to the webservice or api or similar, it adds Origin header in the request for the server or destination (here your api) to validate if the request is coming from an authorized source or not. Ideally the api/server should look for the Origin in the Request header it received and probably validate against the set of origins(domains) which it is permitted to serve the resources to. If it is coming from a permitted domain it will add the same domain in the response header as "Access-Control-Allow-Origin" value. wildcard is also permitted for this, but the issue is that with wild card permission any one can make a request and get it served (with some restrictions like an api is authenticated via windows auth or cookies where you need to send the withCredentials value * is not allowed). it is not a good practice to use wildcard origin the response header which makes it open to everyone.
These are some ways to set the response header with the values:-
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: http://yourdomain.com
you can even add multiple Access-Control-Allow-Origin headers in the same response (I believe works in most browsers)
Access-Control-Allow-Origin: http://yourdomain1.com
Access-Control-Allow-Origin: http://yourdomain2.com
Access-Control-Allow-Origin: http://yourdomain3.com
On the server side (c# syntax) you would do this:-
var sourceDomain = Request.Headers["Origin"]; //This gives the origin domain for the request
Response.AppendHeader("Access-Control-Allow-Origin", sourceDomain ); //Set the response header with the origin value after validation (if any) .Depending on the type of application you are using syntax may vary.
Hope this helps!!!

WCF REST based GET request returing raw XML

What I am trying to accomplish is adding a GET method to my WCF REST based service and accessing it via the WebRequest class from a Silverlight 3 client application.
I am getting the error The remote server returned an error: NotFound. which, as I understand it, can be just a generic error for any 500 error encountered on the server.
WCF operation contract:
[OperationContract, WebGet(UriTemplate = "path/{id}")]
Stream Get(string id);
Operation implementation:
public Stream Get(string id)
{
WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml; charset=utf-8";
return new MemoryStream(Encoding.UTF8.GetBytes("<xml><id>1</id><name>Some Name</name></xml>));
}
Client code that throws exception:
HttpWebRequest webRequest = WebRequest.CreateHttp("http://domain.com/my-service.svc/path/1");
webRequest.BeginGetResponse(
x =>
{
try
{
using (WebResponse webResponse = webRequest.EndGetResponse(x)) <--Exception thrown here
using (Stream stream = webResponse.GetResponseStream())
{
//do stuff here...eventually.
}
}
catch (Exception ex)
{
}
},
null);
I suspect that it has something to do with the return type and have also tried returning XmlElement to no avail. I am really stumped here, any ideas what I might be doing wrong?
Note that I can successfully hit the method via Fiddler and a web browser.
Try putting the code below into your web.config file(change the file name in the initializeData attribute appropriately.
If you are using full IIS, and not Casini or IIS Express (I use the latter), make sure to put the log file somewhere where your web application has write permissions). This will cause WCF to generate a fairly detailed log file. I've found the log to be pretty handy.
<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true">
<listeners>
<add name="traceListener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData= "c:\temp\WEBTraces.log" />
</listeners>
</source>
</sources>
</system.diagnostics>
Here's another thing to check: Is domain.com exactly the same domain name as your silverlight app is running from (example -- is your SL app starting as localhost/xx and your web service call going to domain.com?
For security reasons, Silverlight will not make cross-domain web service calls unless the called domain grants it permission (same as Flash). If this is the case, you will need a clientaccesspolicy.xml file.
You can read about it here: http://weblogs.asp.net/jgalloway/archive/2008/12/12/silverlight-crossdomain-access-workarounds.aspx
There is a video here: http://www.silverlight.net/learn/data-networking/introduction-to-data-and-networking/how-to-use-cross-domain-policy-files-with-silverlight
There are some helpers here: http://timheuer.com/blog/archive/2008/04/06/silverlight-cross-domain-policy-file-snippet-intellisense.aspx
NotFound should mean 404 and not 500. A 404 error could be produced by a wrong URI.
Uri resturi = new Uri(String.Format("http://{0}:8080/MyService/", hostname)); // http
WebHttpBinding rest = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly); // WebHttpSecurityMode.Transport for ssl
host.AddServiceEndpoint(typeof(IMyService), rest, resturi);
In the code example above your service would be aviable via http://host:8080/MyService/path/1

CORS Support within WCF REST Services

I have a WCF REST service hosted within a Windows service and I would like to send the Access-Control-Allow-Origin HTTP header (defined as part of CORS) with every response.
My attempted solution was to have something like the following within an IDispatchMessageInspector implementation:
public void BeforeSendReply(ref Message reply, object correlationState)
{
var httpResponse = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
if (httpResponse != null)
{
// test of CORS
httpResponse.Headers["Access-Control-Allow-Origin"] = "*";
}
}
Normally this would work, but unfortunately my service also uses HTTP basic authorization, which means that when a request comes in without the Authorization header, WCF automatically sends a 401 response asking for credentials. Unfortunately WCF does not call my IDispatchMessageInspector during this initial exchange, so Access-Control-Allow-Origin header is not added to the initial exchange.
The problem occurs when I try to call the service from a browser. CORS specifies that cross-origin requests should only be allowed if the origin domain matches the domain listed in the Access-Control-Allow-Origin response header (* matches all domains). Unfortunately when the browser sees the initial 401 response without the Access-Control-Allow-Origin header, it prevents access (according to the same origin policy).
Is there any way add a header to the initial 401 response sent automatically by WCF?
This guy saved my day.
http://blogs.microsoft.co.il/blogs/idof/archive/2011/07.aspx
I am going to place some of his notes here, just in case that web page dies some day.
(I hate finding "Your answer is right HERE" links, and then the link is dead.)
<behaviors>
<endpointBehaviors>
<behavior name="webSupport">
<webHttp />
<CorsSupport />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="CorsSupport" type="WebHttpCors.CorsSupportBehaviorElement, WebHttpCors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<services>
<service name="Service.JSonService">
<endpoint address="http://localhost:8080" behaviorConfiguration="webSupport” binding="webHttpBinding" contract="Service.IJSonService" />
</service>
</services>
Now, you have to find his downloadable library called "WebHttpCors.dll".
But there is enough there (above) to help you google/bing your way to a resolution.
The part that was throwing me for a loop (in my scenario) is that IE was working, but Firefox was not working.
My originating page was:
http://localhost:53692/test/WCFCallTestViaJQ14.htm
So my service is at:
http://localhost:8002/MyWCFService/MyWCFMethodByWebGet?state=NC&city=Raleigh
So I had localhost <<-->> localhost traffic.
**** But the ports were different. (53692 and 8002) ****
IE was ok with it. Firefox was not ok with it.
Then you gotta remember that each browser handles their .Send() requests differently (inside JQUERY that is).
It all makes sense now.
//JavaScript snipplet from JQuery library
if (window.XMLHttpRequest) {
returnObject = new XMLHttpRequest();
} else if (window.ActiveXObject) {
returnObject = new ActiveXObject("Microsoft.XMLHTTP");
} else {
msg = "Your browser doesn't support AJAX!";
}
Here are some key words, phrases that I've been googling/binging that finally led me somewhere.
Result: [Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIXMLHttpRequest.statusText]" nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)" location: "JS frame :: http://localhost:53692/test/WCFCallTestViaJQ14.htm :: HandleJQueryError :: line 326" data: no]
XMLHttpRequest Send "NS_ERROR_FAILURE"
JQuery Ajax WCF Self Hosted CORS JSON
NOW, YOU NEED TO READ HIS BLOG BLOGS TO UNDERSTAND WHAT THE CODE IS DOING:
For example, he says:
“Access-Control-Allow-Origin” header with the value of “*”
This may or may not be what you want. You may want to have better control of this value (headers) and the others (methods and the origins).
Development environment is one thing. (Use all the *'s you want).
Production is something else, you may want to tweak down those * values to something more discriminate. In a nutshell, you need to understand what CORS is actually doing for you in terms of security, and not just add a behavior that lets everything in.
allowed-origins: '*'
allowed-headers: '*'
allowed-methods: '*'
To achieve what you want you need to handle the authorization yourself which is possible by impelementing + registering a HttpModule... there you would issue the 401 and along with it any http header you want... there is even a sample implementation here on SO - see Adding basic HTTP auth to a WCF REST service
EDIT - after comment from OP:
Since the OP's comment says that he is self-hosting the solution is not with HTTPModule but actually with IDispatchMessageInspector.BeforeSendReply and with IDispatchMessageInspector.AfterReceiveRequest.
The Authorization must be configured to "None" and custom implemented/handled in IDispatchMessageInspector - this way you can add any header when issuing a 401 . Otherwise the runtime handling Basic Auth wouldn't call your IDispatchMessageInspector before proper/positive Auth.
Although this works BEWARE that this means you implement security-sensitiv code yourself and thus need to take appriopriate measure to ensure its proper implementation...
Adding the following line to the first method that gets called in the WCF Service worked for me.
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
Requires the following import
System.ServiceModel.Web;
Refer this for original answer
I tried many ways but could not find anything and then suddenly in the end I came to know that headers should be sent through OPTIONS request only and then I found some helpful SO code here!
This one resolves my problem completely.
Actually the point here is that you have to add headers in OPTIONS request along with just a 200 OK response and thats what is done on this link.

Categories