I am using GAE for web development, on jinja2 and python2.7
I can get data from Python. But I failed to send data from JavaScript to Python. Here is the JavaScript code:
function toSave() {
var val_no = $('#field-no').val();
var val_name = $('#field-name').val();
var val_address = $('#field-address').val();
var val_phone = $('#field-phone').val();
var val_car = $('#field-car').val();
var val_penalty = $('#field-penalty').val();
$.ajax({
type: 'GET',
url: "https:/test/User_update",
data: {
"no": val_no,
"name": val_name,
"address": val_address,
"phone": val_phone,
"car": val_car,
"penalty": val_penalty
},
dataType: "json"
}).done(function () {
$('#modal-1').modal('hide');
table.row.data([val_no, val_name, val_address, val_phone, val_car, val_penalty, UserIndex_CreateEditButton(val_no, val_name, val_address, val_phone, val_car, val_penalty), UserIndex_CreateDeleteButton(val_no)], $('#' + tempUpdate));
});
}
And the Python code (class User_update in main.py):
import os
import webapp2
import MySQLdb
import json
import logging
import googlemaps
import jinja2
import sys
import urllib
import urllib2
import json as simplejson
import codecs
reload(sys)
sys.setdefaultencoding('utf-8')
from operator import eq
from datetime import datetime
from collections import OrderedDict
class User_update(webapp2.RequestHandler):
def get(self):
jsonData = json.loads(self.get('data'))
# penalty = self.request.get('data')
# phone = urllib.request.urlopen(req).read()
# data = urllib.requset.urlopen("http://www.daum.net").read()
# phone=urllib2.urlopen("https://firststep-2016.appspot.com/Log").read()
self.response.headers['Content-Type']='text/plain'
db = connect_to_cloudsql()
cursor = db.cursor()
cursor.execute("SET names utf8")
cursor.execute('SET SQL_SAFE_UPDATES=0;')
cursor.execute("""update User set name='%s',address='%s',phone='%s',car_num='%s',penalty='%s' where no='%s' """%(jsonData.name,jsonData.address,jsonData.phone,jsonData.car,jsonData.penalty,jsonData.no))
db.commit()
db.close()
How can I get the JavaScript data from Python?
HTTP Methods
First it is good to understand how the communication works in general.
HTTP has various methods, such as GET and POST (see https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol).
Relevant methods for you in this case:
GET is meant for read-only requests. Unlike other HTTP methods, GET requests do not carry body with data that is sent to the server. All of the data would get sent via URL parameters. In some case also as part of the header. GET requests may be repeated or cached as they are read-only. Which is why they shouldn't be used for updates.
POST and PUT can update data, generally POST is used to create objects whereas PUT is used to update an existing object. Both HTTP methods do accept a body with the data.
Since you are updating a user, PUT seems to be appropriate.
Sending data to the server, via jQuery's ajax
Most developers might assume it but it is worth listing out all of the direct dependencies, in this case you seem to be using jQuery to get the value and send the request to the server. In particular $.ajax: http://api.jquery.com/jQuery.ajax/
As the documentation for the data attribute:
Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests.
So even though GET doesn't accept a body, jQuery adds it to the URL for you. As mentioned before I would advice against using GET for updates.
At this point you could make use of the browser's developer tools to inspect the network requests. That way you will see what exactly is sent to the server.
Receiving data in the server
I believe the API you are using is: http://webapp2.readthedocs.io/en/latest/guide/handlers.html
You should be able to get the individual values like so (there won't be a 'data' parameter):
val_no = self.request.get("no")
val_name = self.request.get("name")
# ...
For PUT or POST it should work like this (http://webapp2.readthedocs.io/en/latest/guide/request.html#post-data):
val_no = self.request.POST['no']
val_name = self.request.POST['name']
# ...
Security
Your server code is vulnerable to https://en.wikipedia.org/wiki/SQL_injection
It is not part of your question but quite serious. I wouldn't make that code available on a public server and if it is now I would remove it until fixed.
Looks like you are trying to post data using the wrong method. You should use POST instead of GET to send data to the server
$.ajax({
type: 'POST',
url: "https://firststep-2016.appspot.com/User_update",
data: {
"no": val_no,
"name": val_name,
"address": val_address,
"phone": val_phone,
"car": val_car,
"penalty": val_penalty
},
dataType: "json"
}).done(function () {
$('#modal-1').modal('hide');
table.row.data([val_no, val_name, val_address, val_phone, val_car, val_penalty, UserIndex_CreateEditButton(val_no, val_name, val_address, val_phone, val_car, val_penalty), UserIndex_CreateDeleteButton(val_no)], $('#' + tempUpdate));
});
Then handle the incoming data in the Python server
def post(self):
....
Related
Context: I am using JavaScript to send a string as a parameter to a python function over the flask.
But I always get "the missing 1 parameter error" on the python side.
This is what my Ajax query looks like:
$.ajax({
url : 'ListMaker',
data: 'Q1',
success: function(data) {
//does something
}
});
This is what my python function looks like:
#app.route("/ListMaker",methods=["POST","GET"])
def ListMaker(text):
#make it a string just incase
quarter=str(text)
//do things with string
Any other similar questions I can find online, only talk about issues with Ajax and don't really cover the python side. In my case, the function is clearly being called, but it claims to receive no data to work with.
Am I sending the data wrongly from the Ajax side? Or am I parsing it wrongly on the python side?
Clarification for NoneType error from the comments below:
I am sending over:
data: JSON.stringify({"letter": "Q", "value": "25%"})
I am receiving it on the python side like so:
data=request.get_json()
letter=data["letter"]
value=data["value"]
The parameters in a Flask route are for variables in the path, called "variable rules" in Flask:
#app.route("/ListMaker/<text>", methods=["POST", "GET"])
def ListMaker(text):
...
As jQuery Ajax requests are GET by default, your data will be converted to a query string, in this case /ListMaker?Q1. You can access the query string in Flask with flask.request.args.
However, query strings are key-value, and you are only supplying a key, which will correspond to the Python dict {'Q1': ''}. You should either set the Ajax data to a string like quarter=Q1, or use an object:
$.ajax({
url : 'ListMaker',
data: {'quarter': 'Q1'}, // or 'quarter=Q1'
success: function(data) {
//does something
}
});
Then you will be able to access it in Flask:
#app.route("/ListMaker", methods=["POST", "GET"])
def ListMaker(): # no parameters needed
quarter = flask.request.args["quarter"]
# quarter == "Q1"
If you want to use a POST request, set method: 'POST' in the Ajax-call, and use flask.request.get_data() or flask.request.get_json() on the Flask side.
Is it possible to make a GET request to a JSON RPC API? I'm trying to do this using the random.org api (https://api.random.org/json-rpc/1/). It's functioning if I use a POST request but I need to make GET requests for all APIs that I'm using in the application I'm working on.
Here is the post request that is working:
function getNewThing(apiUrl) {
data = {
"jsonrpc": "2.0",
"method": "generateIntegers",
"params": {
"apiKey": "key",
"n": 1,
"min": 0,
"max": 1000000,
"replacement": true,
"base": 10
},
"id": 683489
}
// ajax call to the api
return $.ajax({
type: "POST",
url: apiUrl,
data: JSON.stringify(data),
success: function(result) {
console.log(result)
},
error: function(err) {
console.log(err)
}
});
}
The reason I think this can be turned into a GET request is because this documentation implies it can be: http://www.jsonrpc.org/historical/json-rpc-over-http.html#encoded-parameters
I've tried to set up the URL the following ways with no luck:
With URL encoding for the params:
https://api.random.org/json-rpc/1/invoke?jsonrpc=2.0&method=generateIntegers¶ms=%7B%22apiKey%22%3A%20%229b6ed250-67fc-4afd-b60b-09c6076e5178%22%2C%22n%22%3A%201%2C%22min%22%3A%200%2C%22max%22%3A%201000000%2C%22replacement%22%3A%20true%2C%22base%22%3A%2010%7D&id=683489
Without:
'https://api.random.org/json-rpc/1/invoke?jsonrpc=2.0&method=generateIntegers¶ms={"apiKey": "9b6ed250-67fc-4afd-b60b-09c6076e5178","n": 1,"min": 0,"max": 1000000,"replacement": true,"base": 10}&id=683489'
What am I missing? Thanks in advance!
Some kind folks from the NYJS meetup group helped me with an answer on this one. For anyone who happens find this question:
POST is the only option you'll have because GET is heavily discouraged.
https://www.simple-is-better.org/json-rpc/transport_http.html#get-request
http://www.jsonrpc.org/historical/json-rpc-over-http.html#http-header
For the random.org API, POST is the only option
https://api.random.org/json-rpc/1/introduction
All invocations must be made via HTTP POST. In particular, HTTP GET is not supported.
With the note: "It's not really up to you and your implementation if a POST request can be processed additionally as a GET request - This is something a server would have to be setup in order to handle."
Here's what I'm trying to accomplish:
User enters JSON in a textarea element.
{
"test":[
{"a":"b"}
]
}
Client side JavaScript parses the JSON.
myObject = $.parseJSON($("#my-textarea").val());
JSON is sent over an ajax post request to the server with
dataType: json,
data: {"my_object": myObject}
Post parameters are checked on the server side in sinatra and the JSON looks like this now.
{
"test": {
"0": {
"a": "b"
}
}
}
I'm wondering why the test array was changed into a hash and if there's anything I can do to avoid that. I'm thinking that the original JSON is improperly formatted, but I'm unsure.
EDIT:
Here is a stripped down version of the ajax request and controller action.
function test() {
return $.ajax({
url: "/test",
type: "post",
dataType: "json",
data: {"test":[{"a":"b"}]},
success: function(response) {
}, error:function(jqXHR,exception) {
ajaxError(jqXHR,exception);
}
})
}
post '/test' do
puts params
return {}
end
I would stringify the resulting JSON object before you send it, like this:
dataType: json,
data: {"my_object": JSON.stringify(myObject)}
If you have to support browsers that don't have JSON natively, you can conditionally import the json js to add that support. (jQuery does not natively have a JSON stringify method).
Try this
var test = "{ \"test\":[{\"a\":\"b\"}]}"
$.parseJSON(test) // or even JSON.parse(test)
If you trace the object before it goes to the server side you will confirm that it was parsed correctly. So the problem is in sinatra I would say.
Just first check what's the parse result before doing the server call.
If you don't know if it's your client doing the bad translation, create a native javascript object (without the parse) and send it instead. If it's still bad, I doubt the problem is on the client.
Output I got on the chrome console:
JSON.parse(test)
Object {test: Array[1]}
test: Array[1]
0: Object
Recently I realized that I needed to use easyXDM instead of jQuery's $.ajax in order to make a cross domain post request. After getting easyXDM set up I see that the functions line up fairly closely:
jQuery:
$.ajax({
url: "/ajax/",
method: "POST",
data: myData
});
easyXDM:
xhr.request({
url: "/ajax/",
method: "POST",
dataType: 'json', // I added this trying to fix the problem, didn't work
data: myData
});
myData is setup something like:
myData = {};
myData[1] = 'hello';
myData[2] = 'goodbye';
myData[3] = {};
myData[3][1] = 'sub1';
myData[3][2] = 'sub2';
myData[3][3] = 'sub3';
When I make the request with jQuery it handles the sub fields properly, but not with easyXDM.
Here is how the POST request comes into the server with jQuery:
screenshot-with-shadow.png http://img37.imageshack.us/img37/4526/screenshotwithshadow.png
And here is how it comes in with easyXDM:
screenshot-with-shadow.png http://img204.imageshack.us/img204/4526/screenshotwithshadow.png
How can I send an javascript object/array of key-value pairs via an easyXDM / XHR request like jQuery does?
In light of the limitations of easyXDM discussed in the comments, the only way you can use it would be to serialize your data manually when passing it to .request i.e.
xhr.request({
url: "/ajax/",
method: "POST",
data: {jsonData: JSON.stringify(myData)}
});
Alternatively you could create your own postMessage solution but you will be excluding IE7 and below.
I think you are mistaken about sending a request cross-domain via AJAX. You CAN indeed send a request cross-domain via AJAX regardless of the JavaScript API. However, in order to receive a response cross-domain, the response needs to be of data type JSONP.
JSONP is simply JSON with padding, for example:
JSON:
{ Key: "Hello", Value: "World" }
JSONP:
callback({ Key: "Hello", Value: "World" })
It is a subtle difference but JSONP by-passes browser same-origin policy and allows you to consume JSON data served by another server.
To consume JSON data coming from another server via jQuery AJAX try this:
$.ajax({
url: "http://mydomain.com/Service.svc/GetJSONP?callback=callback",
dataType: "jsonp",
data: myData,
success: function(data) {
alert(data);
}
});
For this to work you must make sure that your web service is returning results as JSONP and not JSON.
As easyXDM can't serialize properly you need to serialize data manually:
JSON.stringify(myData)
Since the request will now contain a json string rather than object then Index.html should not parse the properties to create json structure. Go to index.html that comes with easyXDM and locate the following code:
var pairs = [];
for (var key in config.data) {
if (config.data.hasOwnProperty(key)) {
pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(config.data[key]));
}
}
data = pairs.join("&");
Don't execute this code in a case of POST request. Just assign config.data to data:
data = config.data;
$j.ajax({
type: "POST",
url: 'http://acme.host.vhost/objects/create/',
dataType: 'http',
async: false,
data: {object: data},
success: function (msg) {
// window.location =
alert(msg);
},
statusCode:{
200: function(){
alert(200);
}
}
});
In inspector, I see that the response is an entire web page. How do I change my POST request so that the browser will actually be redirected to that web page?
the alert(200) happens, so.. everything is ok? but success: isn't triggered. =\
ajax works like a "secondary browser", which it sends requests, receives it - and it stops there. it does not touch your browser display until you parse what's in it (and do whatever things that follow).
what you can do is to create your own standard of knowing when to redirect, or more generally, how the server communicates it's commands to the client side. I preferably use JSON to do this, sending commands from the server to the client like:
{
"errors": [], //an array of all errors from the server (like validation and stuff)
"details": [ //an array of additional details the client side must kno
"redirect": "url_here.html"
],
data: {} //the actual payload of the request. may be html, or further data or templates etc.
}
one of my apps return a JSON object with the details property to:
know what page of a search result i am in
how many it contains
what was the search query
and so on..
so instead of returning (or in this case "echoing") the result page in HTML, use JSON to forward the user to a "success" page instead.