4

I am making a get request form Javascript to Python. And I am trying to pass a 2D array so something like

[["one", "two"],["foo", "bar"]]

And here is what I am currently trying but it is not working.

So in my javascript I have an array that look similar to the one above, and then pass it like this

var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "http://192.67.64.41/cgi-bin/hi.py?array=" + myArray, false );
xmlHttp.send( null );

And then in python I get it like this

import cgi
form = cgi.FieldStorage()
array = form.getvalue("array")

But it doesn't come out right, in python then if I were to do

print array[0]
#I get -> "o"
print array[1]
#I get -> "n"
print array[2]
#I get -> "e"

and so on, but if I what I want is

print array[0]
#output -> ["one", "two"]

How can I accomplish this?

Thanks

spen123
  • 3,254
  • 10
  • 37
  • 52
  • Can you change how you receive your data in python? It would be better to change your request to read data from body. – Marko Gresak Aug 11 '15 at 00:03
  • @MarkoGrešak yes i could change it I'm just not sure what to change it to? but if you could tell me a better way to send the data fro javascript to pythonn that would be great – spen123 Aug 11 '15 at 00:08
  • [This answer](http://stackoverflow.com/questions/464040/how-are-post-and-get-variables-handled-in-python) could be a start, you send a POST request and put your data as javascript request body. It will be much easier for you if you use a library like [jQuery](http://jquery.com/), where XMLHttpRequests are a breeze with `$.ajax`. And for backend framework, pick one of [web frameworks](https://wiki.python.org/moin/WebFrameworks) listed on python webpage. You will need those for better productivity at any serious work. – Marko Gresak Aug 11 '15 at 00:13
  • @MarkoGrešak yes okay but still how to I pass a 2D array? – spen123 Aug 11 '15 at 00:17
  • You will probably have to turn it into json with `JSON.stringify` before sending and then parse it back in your python code. But I believe jQuery's `$.ajax({url: 'http://192.67.64.41/cgi-bin/hi.py', data: myArray })` will do this automatically. – Marko Gresak Aug 11 '15 at 00:22
  • @MarkoGrešak okay but how do I make the get request or does this `$.ajax({url: 'http://192.67.64.41/cgi-bin/hi.py', data: myArray })` do it? – spen123 Aug 11 '15 at 00:24
  • I said you will have to change it to POST, and you would also have to add `method: POST` to your ajax request object. GET requests can't include a body. – Marko Gresak Aug 11 '15 at 00:30

2 Answers2

3

You can't simply pass an array as a query parameter. You will need to iterate over the array and add it so the URL string such as ?array[]=one&array[]=two

Here is a basic jsfiddle as an example

var a=['one', 'two'];
var url = 'www.google.com';

for (var i=0; i<a.length; ++i) {
    if (url.indexOf('?') === -1) {
        url = url + '?array[]=' + a[i];  
    }else {
        url = url + '&array[]=' + a[i];
    }
}

console.log(url);
Cjmarkham
  • 9,057
  • 4
  • 47
  • 80
  • so I have to pass each string separately? Also like `?array[]=one&array[]=two&array[]=foo&array[]=bar`? But then will in be in an array in python? – spen123 Aug 10 '15 at 23:49
  • You will be able to access the GET values by their key, so `array[0]` would equal `one` – Cjmarkham Aug 10 '15 at 23:51
  • okay and just to clarify `array[2]` would be foo? Is there any way to have it be a 2D array? – spen123 Aug 10 '15 at 23:52
  • You could do so by simply building the query string so the query string would read `array[][]=bar`. Which would return as `array[0][0]` – Cjmarkham Aug 10 '15 at 23:55
  • @carl-markham : Do you remember that query strings aren't infinite? What you propose will work, but, hm, I'd rather aproach the problem head on than cowing down the array. – Dalen Aug 11 '15 at 00:12
-3

No, you don't have to. And, yes, you can do just that! You can pass it as one string like you did, and then get it and evaluate it in Python.

You can use:

evaldict = {}
array = eval("[[1, 2, 3], [4, 5, 6]]", evaldict)

Although I forced the scope of evaluation to be encapsulated in a dict, THIS IS NOT SECURE!

Because someone can pass some other Python expression to be evaluated. Therefore better use literal_eval() from ast module which doesn't evaluate expressions.

I suggest you tu use jquery and its post() method to do this, use a POST HTTP method instead of GET.

Also, this could be nice and securely done using json (send the json instead of just stringifying JS array manually. And using it to avoid evaluating a list directly (in Python).

Here is the client side using jquery:

<html><head><title>TEST</title>
<script type="text/javascript" src="jquery.js"></script>
<script>

pyurl = "http://example.com/cgi-bin/so.py";
function callpy (argdict) {
    $.post(pyurl, argdict, function (data) {    
    // Here comes whatever you'll do with the Python's output.
    // For example:
    document.getElementById("blah").innerHTML = data;
    }, "text");
};

var myArray = [["one", "two"], ["foo", "bar"]];
// This is array shape dependent:
function stringify (a) {
    return "['" + a.join("', '") + "']";
    };
myArrayStr = "[";
for (x = 0; x<myArray.length; x++) {
    myArrayStr += stringify(myArray[x]) +", ";
    }
myArrayStr += "]";
// This would be better, but it is library dependent:
//myArrayStr = JSON.stringify(myArray);
</script>
</head><body>
<a href="#" onclick="javascript:callpy({'array': myArrayStr});">Click me!</a>
<p id="blah">
Something will appear here!
</p>
</body></html>

And this is the server-side CGI:

#! /usr/bin/env python
try:
    # This version of eval() ensures only datatypes are evaluated
    # and no expressions. Safe version of eval() although slower. It is available in Python 2.6 and later
    from ast import literal_eval as eval
except ImportError:
    import sys
    print "Content-Type: text/html\n"
    print "<h1>literal_eval() not available!</h1>"
    sys.exit()
import cgi, cgitb
import sys
cgitb.enable()

print "Content-Type: text/html\n"

i = cgi.FieldStorage()
q = i["array"].value.strip()
print "I received:<br>"
print q
# Put here something to eliminate eventual malicious code from q
# Check whether we received a list:
if not q.startswith("[") and not q.endswith("]"):
    print "Wrong query!"
    sys.exit()

try: myArray = eval(q)
except: print "Wrong query!"; sys.exit()
if not isinstance(myArray, list):
    print "Wrong query!"
    sys.exit()
print "<br>Evaluated as:<br>"
print myArray

Now, note that using json on both sides would be faster and more flexible.

Dalen
  • 3,916
  • 1
  • 15
  • 30
  • would you mind providing more info as Im not sure how to execute wha you are describing – spen123 Aug 11 '15 at 00:13
  • Yes, but give me some time. I just wanted you to know that it is possible, and more than that, pretty easy. I did it before. I'll edit my post soon. – Dalen Aug 11 '15 at 00:15
  • Great, whenever you have the time is good :), by the way Im not worried about security because this is just a temp situation, but if a POST is still better than its all good – spen123 Aug 11 '15 at 00:18
  • POST is definitely better, security excluded. Because you don't have to worry about escaping characters etc. etc., you just send a raw binary data which can be as big as you want (well, big as webserver will allow). – Dalen Aug 11 '15 at 00:28
  • Okay POST it is then, just let me know when you update your answer – spen123 Aug 11 '15 at 00:30
  • @spenf10 : I edited the post! It works! Just don't forget about security, and don't forget that you may only use jquery get() and post() to communicate with the server that serves the HTML with a script. I hope this helps! – Dalen Aug 11 '15 at 14:29
  • great, one more thing what if I just want a 1D array like ['foo', 'bar', 'one'] this no longer works on the javascript side? How could I get that to work? – spen123 Aug 11 '15 at 20:49
  • That's why I said that it's array shape dependent. Then you must change how you convert it. Discard a for loop and use only my stringify() function and it'll work again. Or use JSON and it'll always work, no matter what your array looks like. – Dalen Aug 11 '15 at 22:13