Using XMLHttpRequest() PUT with GAE Python - javascript

I am currently trying to write some Javascript to interact with an API that I deployed on GAE (using Python) using XMXMLHttpRequest(). I've had no issue getting a GET, however the PUT is giving me a lot of trouble.
Interestingly, I have no issue touching the PUT request from a test HTTP site (https://www.hurl.it/), however I receive a status value of 0 every time I try from my own Javascript code. Below are snippets of my GAE and Javascript code.
(NOTE - I must use a "put" for this call as a requirement.)
Any guidance would be appreciated!
GAE (Server):
def put(self):
# Save variables for update
cardkey = self.request.get('key', default_value=None)
ident = self.request.get('ident', default_value=None)
brand = self.request.get('brand', default_value=None)
year = self.request.get('year', default_value=None)
player = self.request.get('player', default_value=None)
# If card key is provided then update card
if cardkey:
# Get card
card_to_update = ndb.Key(db_models.Card, int(cardkey)).get()
if ident:
card_to_update.ident = ident
if brand:
card_to_update.brand = brand
if year:
card_to_update.year = year
if player:
card_to_update.player = player
# Save changes and print update to requester
card_to_update.put()
card_dict_format = card_to_update.to_dict()
self.response.write(json.dumps(card_dict_format))
return
# If card key is not provided send error
else:
self.response.write('key not provided. must provide key for update.')
return
And the Javascript from my webpage:
<script>
window.onload = function()
{
var myRequest = new XMLHttpRequest();
var url = 'http://cs496-assignment3-mastrokn.appspot.com/updatecard';
var param = 'key=5636318331666432';
myRequest.open('put', url);
myRequest.onreadystatechange = function()
{
if ((myRequest.readyState == 4) && (myRequest.status == 200))
{
// var myArr = JSON.parse(myRequst.responseText);
// myFunction(myArr);
document.getElementById("viewCards").innerHTML = myRequest.status;
}
else
{
document.getElementById("viewCards").innerHTML = myRequest.status;
}
}
myRequest.send(param);
}
</script>

First, your onreadystatechange() handler should look like this:
myRequest.onreadystatechange = function()
{
if (myRequest.readyState == 4) //Don't do anything until the readyState==4
{
if(myRequest.status == 200) //Check for status==200
{
document.getElementById("viewCards").innerHTML = myRequest.status;
}
else //All other status codes
{
document.getElementById("viewCards").innerHTML =
'readyState='
+ myRequest.readyState
+ ' status='
+ myRequest.status
+ ' status text='
+ myRequest.statusText;
}
}
}
Then, from the docs:
If you end up with an XMLHttpRequest having status=0 and
statusText=null, it means that the request was not allowed to be
performed. It was UNSENT.
To see what went wrong, check the javascript console in your browser for an error, e.g.:
[Error] XMLHttpRequest cannot load
http://cs496-assignment3-mastrokn.appspot.com/updatecard. Origin
http://localhost:4567 is not allowed by Access-Control-Allow-Origin.
(4.htm, line 0)
When I run the code above and send the XMLHttpRequest to my own local server, the PUT request succeeds with a status code of 200.
Lastly, I have doubts about the server code you posted because I don't know of any framework where you return None from a request handler--rather you return some string or a response object. Yet, using other means to make a PUT request to your url returns a 200 status code. Is that really your server code? What framework are you using?

Related

Using a python output in javascript

We want to send a boolean value from python to javascript so we can use it in our html website.
We tried using sockets but thats too complicated for us. Our next thought was to use an api and we know how to get information from an api using javascript. What we want to do is post a python boolean value to an api, and then get the boolean value from the api using javascript.
But we don't know how to do so.
We are using a raspberry pi for all our code and a hardware-button which returns true in python when pressed.
We are currently testing code we found from https://healeycodes.com/javascript/python/beginners/webdev/2019/04/11/talking-between-languages.html
But this code doesnt work for us.
We are also using pycharm as our workspace, is this a problem?
Our current code in javascript:
const request = new XMLHttpRequest();
request.open("GET", url, true);
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
success(JSON.parse(request.responseText));
}
};
request.send();
setInterval(get("button-status.json", receiveStatus), 3000);
}
function receiveStatus(response) {
if (response.status !== status) { // only do something if status has changed
status = response.status;
console.log('button status is now', status);
}
}
let status;
// checks every 100ms
get()
Our python code we're using for testing:
import random
import json
import time
button_status = False
path = (r"C:\Users\Sam\Desktop\pythonProject\pythonflask\emplates") # replace with your actual path
def save_button_status():
with open(path + "/button-status.json", "w") as f:
json.dump({'status': button_status}, f)
while True :
value = random.randrange(1, 10)
if ( value <= 5) :
button_status = True
save_button_status()
time.sleep(3)
else :
button_status = False
save_button_status()
time.sleep(3)
print(button_status)
Javascript within a webpage cannot directly run a Python script on your computer or read information from a local terminal. What you could do is have your Python program output a small json file to your localhost folder which is overwritten when the button is pressed or released, like this:
import json
button_status = False # assuming it is initially off
path = "path/to/your/localhost/folder" # replace with your actual path
def save_button_status():
with open(path + "/button-status.json", "w") as f:
json.dump({'status': button_status}, f)
# Then call save_button_status() whenever the status changes
Then in your javascript, set an interval to periodically call a function that gets this json file and does something based on the value if it has changed:
function get(url, success) {
//--- Get JSON data at the given URL and call `success` if successful
const request = new XMLHttpRequest();
request.open("GET", url, true);
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
success(JSON.parse(request.responseText));
}
};
request.send();
}
function receiveStatus(response) {
if (response.status !== status) { // only do something if status has changed
status = response.status;
console.log('button status is now', status);
}
}
let status;
let interval = setInterval(() => get("button-status.json", receiveStatus), 100); // checks every 100ms
There may be some lag as your local server updates the file.
You can try to set up a SQL Database. Write a SQL statement in Python to receive your boolean. After that make a PHP script on your Web server to receive the SQL data. Then sent a request to the URL of the PHP script using an XHTTP JavasScript request.

Same domain AJAX calls being redirected by 3rd party authentication

In our asp.net core application, I need to make an AJAX call to an IIS 8.0 Server.
The server URL is protected by CA SiteMinder SSO.
When I make Get requests using AJAX I get the desired response. But on the contrary, whenever a Put or Post request is made, I get a 302 response and the url which suggests SiteMinder is requesting credentials.
I was of the opinion, that as long as a User is authenticated by SiteMinder, then requests made to the same domain from the same browser session would not explicitly require users credentials.
As far as I can see, SiteMinder is requesting user credentials even when the user has been authenticated and the requests (PUT & POST) are being made to the same domain.
CA SiteMinder provides a cookie (HttpOnly), which I believe is used to authenticate requests made to the server. I can confirm that the SiteMinder cookie is being included in the requests headers.
My question is why does SiteMinder treat GET and POST/Put requests differently? Or is there any way I can make my POST/PUT request work(without getting a redirect from SiteMinder) with Siteminder using the XHR?
Here is the link to function that makes the XHR request.
function fixRecords() {
_buildingId = ($("input:hidden[id='buildingIdModal']").val());
_roomNo = $("#roomNoModal").val();
if ((_roomNo === "" || _roomNo == null) && _suggestedRoomNo) {
_roomNo = document.getElementById("roomNoModal").value;
}
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status.toString() == 200) {
var message = "Record updated sucessfully.";
var jsonResponse = JSON.parse(this.responseText);
console.log(jsonResponse);
_roomNo = jsonResponse.roomNo;
_buildingId = jsonResponse.building;
_runId = jsonResponse.runId;
var _prevRoomId = _roomId;
_roomId = jsonResponse.roomId;
_recId = jsonResponse.recordId;
message = jsonResponse.comment;
_valid = jsonResponse.valid;
_suggestion = '<div class="glyphicon glyphicon-alert text-info">' + jsonResponse.suggestion+'</div>';
_suggestedRoomId = jsonResponse.suggestedRoomId;
_suggestedRoomNo = jsonResponse.suggestedRoomNo;
var _protocol = jsonResponse.protocol;
var _invAcct = jsonResponse.account;
var _pi = jsonResponse.pi;
displayInformationInFixModal(message + _suggestion, null);
$('#fixModal').on('show.bs.modal', inflateModal(message, _buildingId, _cageId, _roomId, _roomNo, _recId, _runId, _frequencyId, _frequencyType, _valid, _suggestedRoomId, _suggestedRoom, _suggestion, _protocol, _pi, _invAcct));
$('#fixModal').modal('show').on("hide", function () {
$('#fixModal').modal('hide');
});
//document.write(this.responseText);
}
$('#showLoadingImage').modal('hide');
return false;
};
if (_roomNo == null || _roomNo.trim()===''|| _roomNo==="Room Num Unknown") {
alert("Please enter a valid Room No.");
var message = "Please enter a valid Room No.";
$('#fixModal').on('show.bs.modal', populateModalMessage(message));
$('#fixModal').modal('show').on("hide", function () {
$('#fixModal').modal('hide');
});
}
if (_recId <=0) {
xhttp.open("POST", "/FileUploads/InsertFixRecordToDB?BuildingId=" + _buildingId );
}
else {
xhttp.open("PUT", "/FileUploads/FixARecord?BuildingId=" + _buildingId );
}
$('#showLoadingImage').modal('show');
$('#showLoadingImage').css('zIndex', 1500);
xhttp.send();
}
If the SiteMinder cookie is HttpOnly then it explicitly will not be sent by the ajax engine. That's what HttpOnly means.
Separately, SiteMinder (now called CA SSO) definitely distinguishes between different HTTP methods, so the SiteMinder rules can be different for GET POST and PUT. Your SiteMinder admin will need to check the rules applicable for your application to make sure they specifically cover GET POST and PUT (PUT especially is often not included).
HTH!

JSON XMLHttpRequest POST/PUT to Cherrypy application - getting no data

I want to create a Singlepage Application following REST principles. I have managed to make both GET and DELETE work, but i also need to PUT or POST data.
After failing for some time, i looked around and found some sample code here https://gist.github.com/EtienneR/2f3ab345df502bd3d13e
which, first of all, taught me that setting a request header may be helpful. With that done, however, and following the exact example, I'm still getting "None" for the field i expect to receive data on.
I may be missing something absolutely basic, and looking around some more i just can't find out what it is.
on the javascript side i've got:
update_px (path_spl, success_ppl, fail_ppl, formdata) {
this.success_p = success_ppl;
this.fail_p = fail_ppl;
var data = {};
data.firstname = "John";
data.lastname = "Snow";
var json = JSON.stringify(data);
var xhr = new XMLHttpRequest();
xhr.open("POST", path_spl, true);
xhr.setRequestHeader('Content-type','application/json; charset=utf-8');
xhr.onload = function () {
var users = JSON.parse(xhr.responseText);
if (xhr.readyState == 4 && xhr.status == "201") {
console.table(users);
} else {
console.error(users);
}
}
xhr.send(json);
}
and on the python side of things:
def POST(self, path_spl="NoPathError", id = None, data_opl=None):
print("POST called with path_spl " + path_spl )
if(id != None) :
print(" and id " + id)
print (data_opl)
#process data
return ' '
The method is exposed; output shows I'm receiving the correct path and ID, but data is just None even after swapping in this sample code i found.
Where am i going wrong?
I have found a way to get it to work.
1) ** was missing on the expected data - that alone didn't help, now i was getting an empty dict instead of None
2) i replaced the content type; the header is now
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
with that, i am receiving:
{'{"firstname":"John","lastname":"Snow"}': ''}
Which actually leads me into another question - how did {"firstname":"John","lastname":Snow"} become a key with an empty value during json-ification? But anyway. That's for another day to find out; i can work with what i'm getting now.

Link sniffing - http get response codes wrong

I have the task to go trough around 200k links and check the status code of their responses. Anything other than 2xx would mean a problem which means that link has to be manually checked (added to a DB later).
The links I have from a DB and are both http and https, some of them are not valid (e.g. ww.fyxs.d). The format I get is JSON and it's something like this
{
"id": xxx,
"url": xxxx
}
I went with a really simple solution which unfortunately doesn't work.
I am taking the links from a json file and then starting from the back sending a http/https.get request, waiting for the response, checking and processing the status code and moving to the next link after removing the previous one from the list to preserve memory. The problem is that I keep getting 4xx almost all the time and if I do a GET from a REST client I get a 200 OK.
I don't know if it's possible but I only need the correct status code and the body I'm not interested in hence the HEAD method. I also tried with
method: 'GET' - still wrong status codes and http/https.request - I don't even get a response.
Here is my code:
var https = require('https');
var http = require('http');
var urlMod = require('url');
var links = require('./links_to_check.json').links_to_check;
var callsRemaining = links.length;
var current = links.length - 1;
startQueue();
function startQueue(){
getCode(links[current].url);
current--;
}
function getCode(url){
var urlObj = urlMod.parse(url);
var options = {
method: 'HEAD',
hostName: urlObj.host,
path: urlObj.path
};
var httpsIndex = url.indexOf('https');
if(httpsIndex > -1 && httpsIndex < 5){
https.get(options,function(response){
proccessResponse(response.statusCode);
}).on('error', (e) => {
startQueue();
});
}else{
if(url.indexOf('http:') < 0) return;
http.get(options,function(response){
proccessResponse(response.statusCode);
}).on('error', (e) => {
startQueue();
});
}
}
function proccessResponse(responseCode){
console.log("response => " + responseCode);
if(responseCode != 200){
errorCount++;
}
ResponseReady();
}
function ResponseReady(){
--callsRemaining;
if(callsRemaining <= 0){
//Proccess error when done
}
links.pop();
startQueue();
}
I would really appreciate some help - when we succeed I will publish it as a module so if someone needs to check a set of links they can just use it :)
After we solve this I was thinking of using async.map and splitting the links to chunks and running the analysis in parallel so it's faster. The current process written in shell takes around 36 hours.

Using webpy with AJAX

I am relatively new to web development and am trying to get the client javascript to send GET requests to a python script running on the server and the server to return data based on that request. I have tried adapting the examples of the webpy library I found online to no avail. Whenever a GET request is sent, the responseText attribute of XMLHttpRequest() returns the text of the python file rather than the data. Any advise would be much appreciated!
The javascript function:
function sendSerialCommand(selection, command) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
if (command !== 5) {
document.getElementById("output2").innerHTML = xmlhttp.responseText;
document.getElementById("output2").style.color = "green";
} else {
document.getElementById("output1").innerHTML = xmlhttp.responseText;
console.log(xmlhttp.responseText);
document.getElementById("output1").style.color = "green";
}
}
};
xmlhttp.open("GET", pythonFileName + "?sel=" + selection + "?cmd=" + command, true);
xmlhttp.send();
}
...and the test python script:
import web
urls = (
'/', 'Index'
)
app = web.application(urls,globals())
#MAIN LOOP
class Index:
def GET(self):
webInput = web.input()
return 'message: GET OK!'
if __name__ == "__main__":
app.run()
The trick was to use the CGI library for python as such:
#!/usr/bin/python
# Import modules for CGI handling
import cgi, cgitb
# Create instance of FieldStorage
form = cgi.FieldStorage()
# Get data from fields
first_name = form.getvalue('cmd')
last_name = form.getvalue('sel')
print "Content-type:text/html\r\n\r\n"
print "Hello %s %s" % (first_name, last_name)
This captures the keys and data from the GET request and the print command returns data to the xmlhttp.responseText attribute on the client-side.
The script has to be placed into a file the websever is able to execute the script from. That is usually the default /cgi-bin folder located in either /var/www or /etc.

Categories

Resources