I am implementing a virtual agent using IBM Watson services. My application is developed using Jquery, Angular JS & Java.Currently i am calling the watson services from middle layer that is java. But i want to avoid that and call directly from javascript.When i call from javascript using XML Http request, i am getting CORS error.How to solve this?
Below is my code:
var username = "uid";
var password = "pwd";
var xhr = new XMLHttpRequest();
xhr.open('GET', 'url');
//xhr.withCredentials = true;
xhr.setRequestHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Origin,Content-Type, application/json, Authorization");
xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr.setRequestHeader('Access-Control-Allow-Credentials', '*');
xhr.setRequestHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
xhr.setRequestHeader('Content-Type', undefined);
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(username + " " + password));
xhr.send('"query":"hi"');
The IBM Watson services don’t yet support getting cross-origin requests from browser-based apps.
See the answer at Can't access IBM Watson API locally due to CORS on a Rails/AJAX App:
We don't support CORS, we are working on it but in your case Visual Recognition is not supported yet.
That implies some of the services support CORS but I guess the one you’ve tried isn’t one of them.
So other than what you say you’re doing now (accessing the services from your server-side Java layer instead), your only option to get at the services from JavaScript code running in a web app is, either set up your own server-side proxy with https://github.com/Rob--W/cors-anywhere or such, or send your requests through an open CORS proxy like https://cors-anywhere.herokuapp.com/ (though it’s unlikely you’ll want to do that in the case where your requests include any kind of authentication token that you don’t want to expose to the operator of a third-party proxy service).
The way such proxies works is, instead of using https://gateway.watsonplatform.net/some/api as the request URL that specify in your client-side JavaScript code, you instead specify the proxy URL, like https://cors-anywhere.herokuapp.com/https://gateway.watsonplatform.net/some/api, and the proxy sends the actual request to the service, gets back the response, and adds the needed Access-Control-Allow-Origin
response header and other headers to it and passes it on.
So that response with the CORS headers included is what the browser sees.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS has more details about how CORS works, but the main thing to know is that the browser is the CORS enforcement point. So in the case with the Watson services, the browser will actually get the response from the Watson API—you will be able to use devtools in the browser to see the response—but the browser will expose the response to your client-side JavaScript code only if the response includes the Access-Control-Allow-Origin
response header to indicate the server that sent the response has opted in to receiving cross-origin requests from client-side JavaScript running in web apps.
So that’s why, regardless, all the xhr.setRequestHeader("Access-Control-Allow-
lines in your XHR code snippet above need to just be removed—because Access-Control-Allow-*
headers are response headers, not request headers; sending them in a request to a server has no effect on CORS, because as noted above, the browser’s the CORS enforcement point, not the server.
So it’s not the case that the server receives some request from a browser and says, OK I see this request has the right headers, so I’ll allow it. Instead the server allows all requests from browsers, just as it allows all requests from non-browser tools like your Java code or curl or Postman or whatever (as long as they are authenticated of course) and sends a response.
The difference is, when a non-browser-based app receives a response, it doesn’t refuse to let you access the response if it lacks the Access-Control-Allow-Origin
header. But the browser does refuse to let your client-side JavaScript web-app code access the response if it lacks that.
You might also want to look at some of the Watson SDK's available on GitHub.
Some Watson services support CORS, others do not. However, when accessing over CORS, you must use an Auth Token rather than a username/password combination*.
This is a partial list of which services support CORS: https://github.com/watson-developer-cloud/node-sdk/tree/master/examples/webpack#important-notes
Here are a couple of examples using the Node.js SDK:
- Webpack: https://github.com/watson-developer-cloud/node-sdk/tree/master/examples/webpack
- Browserify: https://github.com/watson-developer-cloud/node-sdk/tree/master/examples/browserify
And, a whole host of examples with the Speech JavaScript SDK:
- https://watson-speech.mybluemix.net/
* There are a couple of services that use API keys rather than username/password combinations. In that case, you can use the API key directly from client-side code if the service supports CORS.
take a look at this tutorial on IBM developerWorks on using Watson's Question and Answer service -
http://www.ibm.com/developerworks/cloud/library/cl-watson-qaapi-app/index.html#N10229