overcoming access-control-allow-origin in simple web map site - ajax

I am trying to set up a simple web page, that will draw a map with some openstreetmap data. I am serving the page for now with a (python) simpleHTTPserver on port 8000 of my local machine.
In my page I run a script that sends an AJAX request to openstreetmap.org:
$(document).ready(function() {
console.log ("Document is loaded.");
var map = L.mapbox.map('mapsection', 'examples.map-vyofok3q');
$.ajax({
url: "http://www.openstreetmap.org/api/0.6/way/252570871/full",
dataType: "xml",
success: function (xml) {
var layer = new L.OSM.DataLayer(xml).addTo(map);
map.fitBounds(layer.getBounds());
}
}); // end ajax
});
(The L. refers to Leaflet javascript libraries I included.) I am having trouble with same origin policy errors. Chrome says, "XMLHttpRequest cannot load http://www.openstreetmap.org/api/0.6/way/252570871/full. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access."
In serving the HTTP locally I followed the advice from a promising SO answer Can I set a header with python's SimpleHTTPServer? and so I ran $ python ajax-allower.py
where ajax-allower.py contains the code below. Can you explain why I still get the error, and suggest how I can get around it?
#!/usr/bin/env python
# runs the simple HTTP server while setting Access-Control-Allow-Origin
# so that AJAX requests can be made.
import SimpleHTTPServer
class MyHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def end_headers(self):
self.send_my_headers()
SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
def send_my_headers(self):
self.send_header("Access-Control-Allow-Origin", "http://www.openstreetmap.org")
#self.send_header("Access-Control-Allow-Origin", "*")
if __name__ == '__main__':
SimpleHTTPServer.test(HandlerClass=MyHTTPRequestHandler)

The ajax request fails because the object in question has been deleted.
If you try it with an object that still exists (e.g. http://www.openstreetmap.org/api/0.6/way/666/full), your jQuery code should perfectly work. Or if you grab the full history of the deleted object (http://www.openstreetmap.org/api/0.6/way/252570871/history), but that's probably not really what you want, I suppose.
It should be said, that the API probably should also send the CORS-headers for the failing (HTTP 410 Gone) requests, so that your client can detect the error code. This looks like a bug that should be reported on github.

Related

Vue.js + Vue Resource No 'Access-Control-Allow-Origin'

Cross site ajax request with Vue.js 1.0 and Vue Resource. I get the following error: XMLHttpRequest cannot load http://dev.markitondemand.com/MODApis/Api/v2/Lookup/jsonp?input=NFLX&callback=handleResponse. No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have a basic understanding of the problem but not sure how to add a callback function with the request or if that is the best solution for this example. I put in the full request URL here just to make it easier to follow.
new Vue({
el: '#stockList',
data: function() {
return {
query: '',
stocks: []
};
},
ready: function() {
this.getStocks();
},
methods: {
getStocks: function() {
this.$http.get('http://dev.markitondemand.com/MODApis/Api/v2/Lookup/jsonp?input=NFLX&callback=handleResponse',
function(data) {
this.stocks = data;
}
);
}
}
})
I have almost zero understanding of networking, but I was able to get several remote apis to work using:
this.$http.jsonp
instead of
this.$http.get
"No Access-Control-Allow-Origin" header usually is a problem with the server. It means that the server is configured to only allow a person access to the API if the request comes from the same domain as the server. You either need to run the script from the website that you are requesting data from, or you need to change the server config to allow access to all domains.
If you don't have access to the server, and you don't want to run the script in the browser, then I think what you could do is use a headless browser like PhantomJS to navigate to the page, insert a script element into the dom that contains you script, execute the function and then return the data from the API. I could write the code out for you, but to be honest, it's a bit complex. You would have to know how to use node.js, and phantom.js. I've personally only used phantom.js for the Node 'html-pdf' package, but I'm sure with a little bit of reading you could figure out how to do it.
Set your local environment to http instead of https if you have no control over dev.markitondemand.com.

This web API does not have CORS enabled; how can I use it?

I want to make an Ajax request to the ChemSpider Web Api from a web application that I'm making -- for example, using the GetCompoundInfo search function.
This API returns its data in XML format. For example, a search for pyridine:
GetCompoundInfo?CSID=1020&token=redacted-security-token
results in
<?xml version="1.0" encoding="utf-8"?>
<CompoundInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.chemspider.com/">
<CSID>1020</CSID>
<InChI>InChI=1S/C5H5N/c1-2-4-6-5-3-1/h1-5H</InChI>
<InChIKey>JUJWROOIHBZHMG-UHFFFAOYSA-N</InChIKey>
<SMILES>C1=CC=NC=C1</SMILES>
</CompoundInfo>
Seemed simple enough. This was my Ajax request:
$.ajax({
crossDomain: true,
type: 'GET',
url: "http://www.chemspider.com/Search.asmx/GetCompoundInfo",
data: {
"CSID": 1020,
"token": "redacted-security-token",
},
success: function(xmlstring, st, x) {
success_stuff(xmlstring);
},
failure: function(xmlstring, st, x) {
failure_stuff(xmlstring);
}
});
However, the server doesn't seem to have Cross-Origin-Resource-Sharing enabled.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://www.chemspider.com/Search.asmx/CompoundInfo?CSID=1020&token=redacted-security-token. This can be fixed by moving the resource to the same domain or enabling CORS.
I can't use JSONP, because the data is returned as XML, not JSON. And setting crossDomain = true doesn't seem to be working.
How can I make a simple GET request to this web API?
First, get in touch with the ChemSpider development team. Ask them if they'd open the API up for CORS requests. They might be happy to help.
If that isn't an option, you'll need to set up a proxy which makes the calls directly as a GET request rather than an ajax request. Then make your ajax requests hit the proxy instead of the API's domain.
If you're a python programmer, uwsgi & urllib make this pretty simple. Save the following code as ajaxproxy.py.
Please note the following code is only to demonstrate how this works and doesn't address security issues. It effectively runs an open proxy. Don't use this as-is for production unless you want all sorts of funny business done in the name of the server you host this on.
"""
Run with:
uwsgi --http :9090 --wsgi-file ajaxproxy.py
"""
import urllib2
def application(env, start_response):
# Get the resource url from the proxy's url & query string.
resource_base_url = env['PATH_INFO'].split('/getresource/')[-1]
if resource_query == '':
resource_url = resource_base_url
else:
resource_url = '{}?{}'.format(resource_base_url, resource_query)
# Get the resource.
print('Getting resource: {}'.format(resource_url))
resource_response = urllib2.urlopen(resource_url)
resource_content = resource_response.read()
# Return the resource to the requester.
start_response('200 OK', [('Content-Type','text/html'), ('Access-Control-Allow-Origin', '*')])
return [resource_content]
uwsgi can be easily installed with
pip install wsgi
All that would change in your ajax request would be the url:
$.ajax({
url: 'http://127.0.0.1:9090/getresource/http://www.chemspider.com/Search.asmx/GetCompoundInfo',
...
});
Of course for anything besides development & testing you'll need to run the proxy on a publicly-available server and change 127.0.0.1 to its address.

AJAX post not working with HTTPS

I am having a rather frustrating problem with the jquery post function that probably stems from not understanding how it works correctly.
I have a function that should post some form information to a php script that I wrote and that script then runs curl requests against an API to get around the cross-domain policy of javascript. It seems to work fine as long as it submits to "http" but when I send it to "https" the form never gets submitted.
I ran wireshark on my computer and it showed no traffic towards the destination ip until I made the url use http. I have basic auth on the server so I am passing the user and password through the url, but tested without that there and got the same results.
Here is the not working code:
$j.post("https://<api user>:<password>#<ip>:444/ProxyScript.php",
$j("#spoke_ticket").serialize(),
function(msg) {
log_status(msg);
fade_status();
$j(':input','#createtheticket')
.not(':button, :submit, :reset, :hidden')
.val('')
.removeAttr('checked')
.removeAttr('selected');
});
Here is the working function:
$j.post("http://<other ip>/ProxyScript.php",
$j("#spoke_ticket").serialize(),
function(msg) {
log_status(msg);
fade_status();
$j(':input','#createtheticket')
.not(':button, :submit, :reset, :hidden')
.val('')
.removeAttr('checked')
.removeAttr('selected');
});
Any ideas as to why the traffic is not being sent?
Let me know if I left out some key information or anything.
Thanks for the help
If you are doing the AJAX post from a http page to a https URL then the Cross-Domain policy kicks in because the protocol is also part of the origin specification, as it is described here. The browser will refuse to make the AJAX call, so that's why you're not seeing any traffic.
A solution is discussed here:
Ajax using https on an http page
So your best bet is the Access-Control-Allow-Origin header which should be supported on most modern browsers now.
So make your server add the following header to the responses:
Access-Control-Allow-Origin: https://www.mysite.com
If for some reason you cannot enforce this, then the only choice left would be JSONP.
Why not use a proxy to get over the cross-domain issue? It sounds more easy. An simple example is when i want to retrieve the danish administration national geo-data for counties,road names and so on (lucky for me, their data is in json or XML optional)
simplified proxy.php
<?
header('Content-type: application/json');
$url=$_GET['url'];
$html=file_get_contents($url);
echo $html;
?>
in ajax, get the lat/longs for a county borderline
var url= "proxy.php?url=https://geo.oiorest.dk/"+type+"/"+nr+"/graense.json";
$.ajax({
url: url,
dataType: 'json',
success: function (data) {
...
});
notice the https - the url could be, real example, https://geo.oiorest.dk/kommuner/0810/graense.json

Cross domain javascript ajax request - status 200 OK but no response

Here is my situation:
Im creating a widget that site admins can embed in their site and the data are stored in my server. So the script basically has to make an ajax request to a php file in my server to update the database. Right? Right :)
The ajax request works excellent when i run it in my local server but it does not work when the php file is on my ONLINE server.
This is the code im using:
var url = "http://www.mydomain.net/ajax_php.php";
var params = "com=ins&id=1&mail=mymail#site.net";
http.async = true;
http.open("POST", url, true);
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
//do my things here
alert( http.responseText );
}
}
http.send(params);
In firebug it shows: http://www.mydomain.net/ajax_php.php 200 OK X 600ms.
When i check the ajax responnseText I always get a Status:0
Now my question is: "Can i do cross-domain ajax requests by default? Might this be a cross-domain ajax problem? Since it works when the requested file resides in my local server but DOESN'T work when the requested file is in another server, im thinking ajax requests to another remote server might be denied? Can you help me clear on this?
Thanks..
Cross-domain requests are not directly allowed. However, there is a commonly-used technique called JSONP that will allow you to avoid this restriction through the use of script tags. Basically, you create a callback function with a known name:
function receiveData(data) {
// ...
}
And then your server wraps JSON data in a function call, like this:
receiveData({"the": "data"});
And you "call" the cross-domain server by adding a script tag to your page. jQuery elegantly wraps all of this up in its ajax function.
Another technique that I've had to use at times is cross-document communication through iframes. You can have one window talk to another, even cross-domain, in a restricted manner through postMessage. Note that only recent browsers have this functionality, so that option is not viable in all cases without resorting to hackery.
You're going to need to have your response sent back to your client via a JSONP call.
What you'll need to do is to have your request for data wrapped in a script tag. Your server will respond with your data wrapped in a function call. By downloading the script as an external resource, your browser will execute the script (just like adding a reference to an external JS file like jQuery) and pass the data to a known JS method. Your JS method will then take the data and do whatever you need to do with it.
Lots of steps involved. Using a library like jQuery provides a lot of support for this.
Hope this helps.

django ajax proxy view

A django web app needs to make ajax calls to an external url. In development I serve directly from django so I have a cross domain problem. What is the django way to write a proxy for the ajax call?
Here's a dead simple proxy implementation for Django.
from django.http import HttpResponse
import mimetypes
import urllib2
def proxy_to(request, path, target_url):
url = '%s%s' % (target_url, path)
if request.META.has_key('QUERY_STRING'):
url += '?' + request.META['QUERY_STRING']
try:
proxied_request = urllib2.urlopen(url)
status_code = proxied_request.code
mimetype = proxied_request.headers.typeheader or mimetypes.guess_type(url)
content = proxied_request.read()
except urllib2.HTTPError as e:
return HttpResponse(e.msg, status=e.code, mimetype='text/plain')
else:
return HttpResponse(content, status=status_code, mimetype=mimetype)
This proxies requests from PROXY_PATH+path to TARGET_URL+path.
The proxy is enabled and configured by adding a URL pattern like this to urls.py:
url(r'^PROXY_PATH/(?P<path>.*)$', proxy_to, {'target_url': 'TARGET_URL'}),
For example:
url(r'^images/(?P<path>.*)$', proxy_to, {'target_url': 'http://imageserver.com/'}),
will make a request to http://localhost:8000/images/logo.png fetch and return the file at http://imageserver.com/logo.png.
Query strings are forwarded, while HTTP headers such as cookies and POST data are not (it's quite easy to add that if you need it).
Note: This is mainly intended for development use. The proper way to handle proxying in production is with the HTTP server (e.g. Apache or Nginx).
I ran across this question while trying to answer it myself, and found this Django app:
http://httpproxy.yvandermeer.net/
...which is a little heavyweight for what I needed (recording and playback, requires a syncdb to add in model stuff). But you can see the code it uses in its generic proxying view, which is based on httplib2:
http://bitbucket.org/yvandermeer/django-http-proxy/src/1776d5732113/httpproxy/views.py
Am I right that you are asking about how to write view in Django that could accept incoming AJAX request, issue request to the remote server and then return received response to browser?
If so, then it's not really Django-specific question - remote calls could be done with Python's urllib2 or httplib, and then you just have to put:
return HttpResponse(received_response)
-- in your Django proxy-view. I assume no reponse processing here, because if it's just a proxy for AJAX call then JavaScript expects unprocessed data.

Resources