How do you use a simple REST API’s with JavaScript - javascript

How do you get data from a REST API with JavaScript. I have several basic API's that I would like to get data from that don't require any authentication. All of the API's return the data I want back in JSON. For example https://www.codewars.com/api/v1/users/MrAutoIt. I thought this would be a very simple process using xmlhttprequest but it appears the same-origin policy is giving me problems.
I have tried following several tutorials but they don’t seem to work on cross domains or I don’t understand them. I tried to post links to the tutorials but I don't have a high enough reputation on here yet.

If you are trying to access a web service that is not on the same host:port as the webpage that is issuing the request, you will bump into the same origin policy. There are several things you can do, but all of them require the owner of the service to do things for you.
1) Since same origin policy does not impact scripts, allow the service to respond by JSONP instead of JSON; or
2) Send Access-Control-Allow-Origin header in the web service response that grants your webpage access
If you cannot get the service owner to grant you access, you can make a request serverside (e.g. from Node.js or PHP or Rails code) from a server that is under your control, then forward the data to your web page. However, depending on terms of service of the web service, you may be in breach, and you risk them banning your server.

In fact, it depends on what your server REST API supports regarding JSONP or CORS. You also need to understand how CORS works because there are two different cases:
Simple requests. We are in this case if we use HTTP methods GET, HEAD and POST. In the case of POST method, only content types with following values are supported: text/plain, application/x-www-form-urlencoded, multipart/form-data.
Preflighted requests. When you aren't in the case of simple requests, a first request (with HTTP method OPTIONS) is done to check what can be done in the context of cross-domain requests.
That said, you need to add something into your AJAX requests to enable CORS support on the server side. I think about headers like Origin, Access-Control-Request-Headers and Access-Control-Request-Method.
Most of JS libraries / frameworks like Angular support such approach.
With jQuery (see http://api.jquery.com/jquery.ajax/). There are some possible configurations at this level through crossDomain and xhrFields > withCredentials.
With Angular (see How to enable CORS in AngularJs):
angular
.module('mapManagerApp', [ (...) ]
.config(['$httpProvider', function($httpProvider) {
delete $httpProvider.defaults.headers.common['X-Requested-With'];
});
If you want to use low-level JS API for AJAX, you need to consider several things:
use XMLHttpRequest in Firefox 3.5+, Safari 4+ & Chrome and XDomainRequest object in IE8+
use xhr.withCredentials to true, if you want to use credentials with AJAX and CORS.
Here are some links that could help you:
Understanding and using CORS: https://templth.wordpress.com/2014/11/12/understanding-and-using-cors/
4 jQuery Cross-Domain AJAX Request methods: http://jquery-howto.blogspot.fr/2013/09/jquery-cross-domain-ajax-request.html#cors (see "1. CORS (Cross-Origin Resource Sharing)")
Unleash your AJAX requests with CORS: http://dev.housetrip.com/2014/04/17/unleash-your-ajax-requests-with-cors/
Using CORS for Cross Domain AJAX requests: http://techblog.constantcontact.com/software-development/using-cors-for-cross-domain-ajax-requests/
Cross origin resource sharing cors AJAX requests between jQuery and Node.js: http://www.bennadel.com/blog/2327-cross-origin-resource-sharing-cors-ajax-requests-between-jquery-and-node-js.htm
Hop it helps you,
Thierry

Here is how you get data.
var request = new XMLHttpRequest();
request.open('GET', 'https://www.codewars.com/api/v1/users/MrAutoIt', true);
request.onload = function() {
if (this.status >= 200 && this.status < 400) {
var resp = this.response; // Success! this is your data.
} else {
// We reached our target server, but it returned an error
}
};
request.onerror = function() {
// There was a connection error of some sort
};
request.send();
As far as running into same origin policy... You should be requesting from an origin you control, or you can try disabling Chrome's web security, or installing an extension such as Allow-Control-Allow-Origin * to force headers.

For a get method you could have something like this:
#section scripts{
<script type="text/javascript">
$(function()
{
$.getJSON('/api/contact', function(contactsJsonPayload)
{
$(contactsJsonPayload).each(function(i, item)
{
$('#contacts').append('<li>' + item.Name + '</li>');
});
});
});
</script>
}
In this tutorial check the topic: Exercise 3: Consume the Web API from an HTML Client

Related

How can I send a MailChimp API request via JavaScript in Google Tag Manager?

I am trying to update MailChimp members from Google Tag Manager, using MailChimp API v3.0, but I am having issues. This is my code, which I'm running from a real domain (not localhost), secured with a valid SSL certificate (if it matters).
const xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = () => {
try {
if (xmlHttp.readyState !== 4) return;
if (xmlHttp.status !== 200)
throw new Error(
xmlHttp.statusText || 'HTTP STATUS ' + xmlHttp.status
);
console.log(xmlHttp.responseText);
} catch (err) {
console.error(err);
}
};
xmlHttp.open('POST', 'https://us12.api.mailchimp.com/3.0/lists/d5bed898ae/members/0740287eb1c63371a10d32ebf58391f9');
xmlHttp.setRequestHeader('Authorization', 'Basic ' + btoa('anystring' + ':' + 'my-api-key-here'));
xmlHttp.setRequestHeader('content-type', 'application/json');
xmlHttp.send('{"email_address":"user#example.com", "status":"subscribed", "member_rating":"4"}');
I get a CORS error:
Access to XMLHttpRequest at 'https://us12.api.mailchimp.com/3.0/lists/d5bed898ae/members/0740287eb1c63371a10d32ebf58391f9' from origin 'https://subdomain.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What am I doing wrong?
UPDATE:
Note, I am not trying to add a new member, I am trying to update an existing one. Also, the API key is not visible, I am picking it up as a GTM variable. Also, the code is running on the domain, as GTM is loaded on the page.
Where am I supposed to run this code? On Mailchimp servers? What am I missing?
First of all, EEK! You should never put a private API key in client-facing code, in this case in GTM. If a website visitor noticed, they could easily use your API key themselves to do pretty much whatever they wanted with your Mailchimp account, including deleting everything in it. From Mailchimp's docs:
API keys grant full access to your Mailchimp account and should be protected the same way you would protect your password.
If you have already put this code live, even just for a few minutes, you will want to immediately disable/revoke that API key and create a new one.
Second, the CORS error is because MailChimp specifically does not support API requests that originate in browsers from outside their own domain - e.g. Cross-Origin requests that require a proper CORS response header. The reason for this is the same as my warning above - you should not be making API calls from client-side code due to the inherent security risks. On the same MailChimp doc page, they repeat this warning:
Because of the potential security risks associated with exposing account API keys, Mailchimp does not support client-side implementation of our API using CORS requests or including API keys in mobile apps.
See this StackOverflow question for a similar discussion.
To address this, you need to have the actual API call come from actual server-side code. Most people would probably proxy the request through their own site - so if your site is example.com, you might have GTM make an AJAX call to example.com/mailchimp-proxy.php?action=update&list=d5bed898ae&user=0740287eb1c63371a10d32ebf58391f9&info=... and then that PHP script would in turn call the actual API endpoint of https://us12.api.mailchimp.com/3.0/... with the data. Because the code is running server-side, if you put your key in it properly, no one should be able to read it.
Another alternative is to use something like Zapier, or "serverless" functions, to proxy the request so you don't need to run your own server.

how to call IBM Watson services from javascript

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

Execute route if only the local Angular App calls it true the controller

I have a few API routes that only the app itself (controller.js) should have access to. Is there a way to use an IP address (possibly insecure because of spoofing) to create a restriction of who uses this part of the api?
Server size (server.js)
app.get("/api/specs",function(req,res){
// Only the app should have access to it, not external entities
res.json({used:getUsed()});
});
Client side (controller.js)
$http.get('/api/specs').success(function(specs,code){
console.log(specs);
});
By default your browser doesn't allow you to make Cross-site HTTP requests because are subject of the same origin policy.
Note:
In particular, this meant that a web application using XMLHttpRequest
could only make HTTP requests to the domain it was loaded from, and
not to other domains.
Which it means in your case that only the js in the same domain of your api can have access to them.
What if I want to extend the use of the API to other domains?
Well in this case you have to setup in your backend api the Access-Control-Allow-Origin header.
Some eg:
// Cross-site HTTP requests from http://siteA.com
Access-Control-Allow-Origin: http://siteA.com
// Cross-site HTTP requests from all
Access-Control-Allow-Origin: *
If you want to debug this behaviour you can just open firebug and check in networks the headers of your requests.
References:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
I suppose that using a GET parameter is the easiest way.
Node.js(express)
app.get("/api/specs",function(req,res){
var queryParam = url.parse(req.url,true).query
if(queryParam.whois == "yourUniqueName"){
res.send("okay");
}else{
res.status(404);
res.send("NG");
}
});
angular
$http.get("/api/specs", {
params: { whois: "yourUniqueName" }
});
define names something unique for each APIs.
and set the server returns response only when a client sends correct name.
server returns 404 except you pass yourUniqueName as query parameter whois.
/api/specs?whois=yourUniqueName
okay
.
/api/specs?whois=otherName
/api/specs?otherParam=something
/api/specs
NG

CORs does not get enabled when using XMLHttpRequest?

I have spent hours trying to access a resource from a different domain.
http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/ which is referenced in other SO posts states that by simply using XMLHttpRequest in a browser that supports CORS, CORS policy should be enabled. However I am still getting
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.nczonline.net/. This can be fixed by moving the resource to the same domain or enabling CORS.
When using it in Firefox 34 which according to http://caniuse.com/#feat=cors should be sufficient.
I am trying a simple example from http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/
<script type="text/javascript">
function log(msg){
var output = $('#output');
output.text(output.text() + " | " + msg);
console.log(msg);
}
function createCORSRequest(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
xhr.open(method, url, true);
log("'withCredentials' exist in xhr");
} else if (typeof XDomainRequest != "undefined"){
xhr = new XDomainRequest();
xhr.open(method, url);
log("XDomainRequest is being used");
} else {
xhr = null;
log("xhr is null");
}
return xhr;
}
function main(){
log("Attempting to make CORS request");
var request = createCORSRequest("get", "https://www.nczonline.net/");
if (request){
request.onload = function(){
log("LOADED!");
};
request.send();
}
}
$(window).load(function(){
main();
});
</script>
And I am getting the following output:
Attempting to make CORS request
'withCredentials' exist in xhr
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.nczonline.net/. This can be fixed by moving the resource to the same domain or enabling CORS.
Trying it on fiddle https://jsfiddle.net/zf8ydb9v/ gives same results. Is there another lever somewhere that needs to switched on to be able to use CORS bBesides using XMLHttpRequest?
The same origin policy (which prevents making of CORS requests) is there for your security, not the security of the server: it prevents malicious scripts to access your data on other servers using your cookies.
So, if you want you can still disable it at your own risk, on your browser.
In Chrome/Chromium, if you want to disable the same origin policy you can start it with the --disable-web-security option:
chromium-browser --disable-web-security
Anyway, if you want it to work for your users, they will not able to make CORS requests if they have not disabled this security check in their browsers (which is discouraged if not for testing).
As noted in other answers, some servers can purposely allow this kind of requests if they believe this can be useful and not harmful for their users, and they can do this with the Access-control headers.
Moreover, if you still want to find a way to provide this kind of functionality to the users, you might make a Chrome extension, which is not bound to the same origin policy.
A common solution to this is to make the cross origin request server side, returning the result to your application. You should be careful coding this: passing the url to fetch to the server will easily cause security concerns for your server side software. But if you have to fetch the same url every time, you could hard code it server side, in PHP would look like something like this:
<?php
echo file_get_contents("http://your_cross_request/");
?>
then making an ajax request to this page (which will be from the same origin) will return the content of the remote url.
CORS headers are found in the response sent by the server to your request. If the requested page isn't sending the header, it doesn't matter what you did with the request in a stock browser, you'll get a security error
The relevant CORS headers look like this, the last being the most important one
Access-Control-Allow-Credentials: false
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: *
I tried opening "nczonline.net" and when I looked at the response headers I did not see any of these, so the server is not configured to permit being loaded in this way
If you are an administrator of that website, you may want to consider adding the required headers to your responses, perhaps being specific about permitted origins rather than using the wildcard
If you're simply trying to demo your code and want to try it with a third party, load a page which does send these headers e.g. developer.mozilla.org

Twitter API Error: 'internal server error'

I tried to use Twitter API to post a tweet using Javascript. Details Below
Base String
POST&http%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.json&oauth_consumer_key%3DXXXXXXXXXXX%26oauth_nonce%3D9acc2f75c97622d1d2b4c4fb4124632b1273b0e0%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1305227053%26oauth_token%3D159970118-XXXXXXXXXXXXXXXXXXXXXXX%26oauth_version%3D1.0%26status%3DHello
Header
OAuth
oauth_nonce="9acc2f75c97622d1d2b4c4fb4124632b1273b0e0",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1305227053",
oauth_consumer_key="XXXXXXXXXXXXXXXXX",
oauth_token="159970118-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
oauth_signature="IWuyoPJBrfY03Hg5QJhDRtPoaDs%3D",
oauth_version="1.0"
I used POST method with body "status=Hello"
But i get a INTERNAL SERVER ERROR.. IS there any mistake on my side ?? Thanks in advance.
Javascript code used
h is the header given above
tweet="Hello"
encodeURLall is user defined which is working in all other occasions.
var xhr = new XMLHttpRequest();
xhr.open("POST","http://api.twitter.com/1/statuses/update.json", false);
xhr.setRequestHeader("Authorization",h);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 )
{
console.log("STATUS="+xhr.status);
console.log("RESPONSE="+xhr.responseText);
}
}
xhr.send("status="+encodeURLall(tweet));
}
You cannot access Twitter's site using an XMLHttpRequest, due to Same origin policy. Use JSONP instead or a server-side proxy (call your own server that redirects your request to Twitter).
BTW, what does encodeURLall() do? Shouldn't you just use encodeURIComponent?
Update: To quote Google:
Regular web pages can use the XMLHttpRequest object to send and receive data from remote servers, but they're limited by the same origin policy. Extensions aren't so limited. An extension can talk to remote servers outside of its origin, as long as it first requests cross-origin permissions.
Please read on there to see which settings you should change in order to make this work.

Categories

Resources