I have a very strange problem with an AJAX request.
The server app.py
#### app.py
from flask import Flask, request, render_template
app = Flask(__name__)
app.debug = True
#app.route("/myajax", methods=['GET', 'POST'])
def mypostajaxreq():
print(request.form)
if request.method == "POST":
name = request.form["name"]
return " Hello " + name
else:
return "Just Hello"
#app.route("/")
def index():
return render_template("indexlistener.html")
if __name__ == "__main__":
app.run()
The indexlistener.html
<!DOCTYPE html>
<html>
<head>
<title>Practice AJAX</title>
<script type="text/javascript" src = "/static/js/myajaxrequestlistener.js"></script>
</head>
<body>
<form method="post">
<label>Name:<input type="text" id="name" value="" /></label>
<button type="button" id="btn-post">Click</button>
<div id="result"></div>
</form>
</body>
</html>
The myajaxrequestlistener.js file
function do_ajax ()
{
var req = new XMLHttpRequest();
var result = document.getElementById('result');
req.onreadystatechange = function()
{
if(this.readyState == 4 && this.status == 200) {
result.innerHTML = this.responseText;
}
}
req.open('POST', '/myajax', true);
req.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
req.send("name=" + document.getElementById('name').value);
};
document.addEventListener('DOMContentLoaded', function()
{
document.getElementById("btn-post").addEventListener("click", function()
{
do_ajax();
})
})
document.addEventListener('DOMContentLoaded', function()
{
document.addEventListener("keydown", function(event)
{
if(event.key === "Enter")
{
do_ajax();
}
})
})
This works all well when I click the button, as expected it fires the mypostajaxreq in the python code, however when I press Enter it returns Error 405. Method not allowed. It is unclear to me why it's happening, I checked with the debugger and I am entering the listener event to keydown, in fact, even weirder, the code works when I use the debugger but it doesn't when I press Enter directly. I suspect it's due to the listener but I don't understand why it shouldn't work. Besides I don't understand the logic of the error (405) I'm receiving: in my understanding this should happen only when the route on the server side doesn't accept the request method is called from, but here I accept both and besides I'm firing only POST requests from the webpage. I'm new to web programming, thanks in advance.
Pressing enter in the only textbox in a form will submit the form, sending a POST to / which is not an allowed method to that route.
You can attach a submit handler to the form instead of a keydown
Also, you don't have to use multiple DOMContentLoaded event handlers.
document.addEventListener('DOMContentLoaded', function()
{
document.querySelector('form').addEventListener("submit", function(event)
{
event.preventDefault();
do_ajax();
});
document.getElementById("btn-post").addEventListener("click", do_ajax);
});
Related
I have a flask app that processes a post request from a javascript file using the XMLHttpRequest() object on the client side. Note this app is running on the localhost.
I am trying to return a response based on whether server raised an exception or not. The server processes the request fine but I can not access the response.
Here is the flask route on the server side.
#app.route("/updatecontact", methods=['POST', 'GET'])
def update_contact():
if request.method == 'POST':
try:
sqltools.update(request.json['msg'])
return "success"
except Exception as e:
return str(e), 400
And here is the function in javascript that sends the POST request and (is meant to) proccess the response back
function pushtodatabase(key, newvals) {
var xhttp = new XMLHttpRequest()
xhttp.open('POST', 'updatecontact', true);
var msg = {"msg": newvals.join("|")};
var msgjson = JSON.stringify(msg)
xhttp.setRequestHeader("Content-type", 'application/json;charset=UTF-8');
xhttp.send(msgjson);
console.log(xhttp.responseText);
console.log(xhttp.status);
}
Yet the status is 0 and the responseText empty
I've tried with different response types in flask. I've tried adding these headers
resp = Response("Foo bar baz")
resp.headers['Access-Control-Allow-Origin'] = '*'
resp.headers["Access-Control-Allow-Methods"] = "GET, POST, DELETE, PUT"
resp.status_code = 200
return resp
Any help would be greatly appreciated. Thanks.
You need to listen to load event of xhttp object and add the event handler for it. See Using XMLHttpRequest
E.g.
main.py:
from flask import Flask, request, render_template
app = Flask(__name__)
#app.route('/updatecontact', methods=['POST', 'GET'])
def update_contact():
if request.method == 'POST':
try:
return 'success'
except Exception as e:
return str(e), 400
else:
return render_template('updatecontact.html')
updatecontact.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>update contact</title>
</head>
<body></body>
<script>
function pushtodatabase(key, newvals) {
var xhttp = new XMLHttpRequest();
xhttp.open('POST', 'updatecontact', true);
var msg = { msg: newvals.join('|') };
var msgjson = JSON.stringify(msg);
xhttp.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
xhttp.send(msgjson);
xhttp.addEventListener('load', reqListener);
console.log('xhttp.responseText:', xhttp.responseText);
console.log('xhttp.status:', xhttp.status);
}
function reqListener() {
console.log('this.responseText:', this.responseText);
console.log('this.status:', this.status);
}
window.onload = function () {
pushtodatabase('key,', ['a', 'b']);
};
</script>
</html>
The output of the console.log:
xhttp.responseText:
xhttp.status: 0
this.responseText: success
this.status: 200
This is my index.html file
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script type="text/javascript">
// setup some JSON to use
var cars = [
{ "make":"Porsche", "model":"911S" },
{ "make":"Mercedes-Benz", "model":"220SE" },
{ "make":"Jaguar","model": "Mark VII" }
];
window.onload = function() {
// setup the button click
document.getElementById("theButton").onclick = function() {
doWork()
};
}
function doWork() {
// ajax the JSON to the server
$.post("result", JSON.stringify(cars), function(){
});
// stop link reloading the page
event.preventDefault();
}
</script>
This will send data using AJAX to Python:<br /><br />
<form action = "/result" method = "POST">
<button id = "theButton" class ="button">
<span>
<i >Click</i>
</span>
</button>
<form>
This is my json_io.py file to run Flask:
#!flask/bin/python
import sys
from flask import Flask, render_template, request, redirect, Response
import random, json
app = Flask(__name__)
#app.route('/')
def output():
# serve index template
return render_template('index.html')
#app.route('/result', methods = ['POST', "GET"])
def worker():
# read json + reply
data = request.get_json(force = True)
print(data)
return render_template('result.html', result = data[0]["make"])
if __name__ == '__main__':
# run!
HOST = '127.0.0.1'
PORT = 4100
app.run(HOST, PORT, debug = True)
After running the command line and click on click button. I got what I want in the Chrome console.
In order to get into http://127.0.0.1:4100/result , I will comment event.preventDefault(); in index.html. However, when I rerun again, it shows me Bad Request Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)
Are there any ideas on how I can fix this ?
In the index.html file, make a placeholder that will be filled out by the js code handling your AJAX request:
<span id='ajax_result'>placeholder</span>
Then, in the python code, you don't really need to go through the template and can return a string straight away:
#app.route('/result', methods = ['POST', "GET"])
def worker():
data = request.get_json(force = True)
return data[0]['make']
Then, in js, grab the result and put it in the placeholder span:
function doWork() {
$.post("result", JSON.stringify(cars), function(reply){
$('#ajax_result').text(reply);
});
event.preventDefault();
}
Click the button and enjoy your Porsche!
I'm improving a system for controlling a telescope remotely. A Raspberry Pi runs flask, and provides a video stream for a camera attached to the telescope. The telescope's focuser is actuated by a stepper motor controlled with an Arduino.The server provides a website that shows the video stream, and offers two buttons to move the focuser in and out.
When either button is clicked, the client sends a POST to the RasPi, and then the RasPi tells the Arduino to move the focuser. But crucially I did not want the page to refresh while refocusing. Hence, I used jQuery and Ajax to suppress the page refresh.
The relevant code snippets are here:
Python/Flask code:
#app.route('/stream/<wcam>', methods=['GET'])
def stream_get(wcam):
class FocuserForm(FlaskForm):
nsteps = IntegerField('# steps: ', default=1)
focuser_in = SubmitField('Focuser in')
focuser_out = SubmitField('Focuser out')
form = FocuserForm()
return render_template('stream.html', wcam=wcam, form=form)
#app.route('/stream/<wcam>', methods=['POST'])
def stream_post(wcam):
results = request.form
arduino_serial = SerialFocuser()
if results['caller'] == "focuser_in":
command = "MVD" + results['steps'] + "\n"
arduino_serial.send_command(command)
elif results['caller'] == "focuser_out":
command = "MVU" + results['steps'] + "\n"
arduino_serial.send_command(command)
return ''
Web (stream.html):
<html>
<head>
<title>Video Streaming</title>
<style>
...
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {});
</script>
</head>
<body>
<h1>Streaming camera {{ wcam }}</h1>
<br>
<img id="bg" src="{{ url_for('video_feed', wcam=wcam) }}", height="480" width="640">
Back
<br>
<!--######################################################-->
<!--# Focuser handling -->
<!--######################################################-->
<br>
<form id="flaskform" method="POST">
<p>
{{ form.nsteps.label }} {{ form.nsteps() }}
{{ form.focuser_in() }}
{{ form.focuser_out() }}
</p>
</form>
<script>
// $(document).ready(function() { // Moved to header
var form = document.getElementById('flaskform');
function onSubmit(event) {
console.log('onSubmit function');
var objectID = event.explicitOriginalTarget.id;
var nsteps = form.nsteps.value;
var return_data = {caller: "", steps: nsteps};
if (objectID == "focuser_in") {
return_data.caller = objectID;
console.log("Focuser_in detected");
} else if (objectID == "focuser_out") {
return_data.caller = objectID;
console.log("Focuser_out detected");
} else if (objectID == "nsteps") {
console.log("nsteps detected");
event.preventDefault();
return;
} else {
console.log("No matches");
return;
}
console.log("About to run Ajax");
$.ajax({
url: "stream.html",
type: "post",
data: return_data,
success: function(response) {
console.log('It worked!');
},
error: function(xhr, status, text) {
console.log('An error occurred:', status,"; ", text);
},
timeout: 1000 // 1s
}); // Ajax
console.log("After running Ajax");
if (event) { event.preventDefault(); }
}
// prevent when a submit button is clicked
form.addEventListener('submit', onSubmit, false);
//<!--form.addEventListener('submit', onSubmit, false);-->
// prevent submit() calls by overwriting the method
form.submit = onSubmit;
//}); // Moved to header
</script>
</body>
</html>
The problem is as follows:
If I refresh the page on the client's browser and then click a button, ajax does POST, but flask does not seem to receive it. The request times out.
If I now restart the server (I'm developing this with PyCharm, so I just click re-run) without refreshing the page in the client, and then click a button, flask does get the POST, and the focuser works like a charm.
If I refresh the page again, then the buttons stop working until I reset the server.
Why does this happen? Obviously the code works in its main purpose, but somehow the page refresh is breaking something.
I had a similar issue once with a camera thread blocking all calls. When you reset the server, does your camera feed still run (before clicking the button)?
Because basically you are calling your camera feed twice - first with the get call when you refresh your page, then again with the post call.
I'd advice you to refactor your submitted code into an alternative function for clarity:
#app.route('/stream/<wcam>', methods=['POST'])
def moveCommand:
if form.is_submitted():
# POST method
results = request.form
arduino_serial = SerialFocuser()
if results['caller'] == "focuser_in":
command = "MVD" + results['steps'] + "\n"
arduino_serial.send_command(command)
elif results['caller'] == "focuser_out":
command = "MVU" + results['steps'] + "\n"
arduino_serial.send_command(command)
So basically you keep your get method for only the streaming and use the post for the moving around.
Thanks to #Peter van der Wal for pointing me towards the solution.
The video streamer has a while True loop, which continually takes frames from the camera, hence locking the thread.
The solution was to start the app with the threaded option on:
Before:
app.run(host='0.0.0.0', debug=True)
Now:
app.run(host='0.0.0.0', debug=True, threaded=True)
This allows the video streaming thread to continue on its own, while allowing the server to process other commands.
I've ran into an weird problem. I have created a login page which send data to a PHP page which return some response code like "00000" for ok "404" for not found etc. I have tested my server with Postman tool and found server is working perfectly fine. When my html send data to server server responds with response code. If the response code comes wrong html alert's it. However if I enter correct credentials and when server respond with success , My login page reloads for no reason.
Here's my javascript
function validatelog(){
var user_email_log=document.getElementById("user_email_log").value;
var user_pass_log=document.getElementById("user_pass_log").value;
if (user_email_log&&user_pass_log!=null)
{
var hr = new XMLHttpRequest();
var url = "../logic/login.php";
var vars =
"user_email_log="+user_email_log+"&user_pass_log="+user_pass_log;
hr.open("POST", url, true);
hr.setRequestHeader("Content-type", "application/x-www-form-
urlencoded");
hr.onreadystatechange = function() {
if(hr.readyState == 4 && hr.status == 200) {
var saman = hr.responseText.trim();
if(saman=="00000"){
alert(saman);
}else if (saman == "404"){
alert("Failed with 404");
}
else{
alert(saman);
}
}
}
hr.send(vars);
}
}
And my html looks like this
<input id="user_email_log"/>
<input id="user_pass_log"/>
<button onclick="validatelog();">Log in</button>
Add type="button" to the button:
<button type="button" onclick="validatelog();">Log in</button>
When it is not specified, it is the same as type="submit", and this will cause your page to reload.
if you use jquery you could do this.
$(document).on('click', 'button', function(e) {
e.preventDefault();
$.get('url')
})
I think the page is reloading because that is the default behavior.
I wrote a small form to log-in into my website :
<form id="log_form" onsubmit='return loginjs()' method="post">
<input type='text' placeholder="login" size='30' name='login' class='test'/>
<input type='password' placeholder="password" name='password' size='30'/>
<input type='submit' value='Connect' id='signin' />
</form>
and I wrote this Javascript function to send the form's data to a php page which going to check if everything is ok and make the session up.
function loginjs() {
'use strict';
var form = document.getElementById('log_form');
var btn = document.getElementById('signin');
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if(request.readyState === XMLHttpRequest.DONE) {
if(request.status === 200) {
if (request.responseText != 'ok')
alert(request.responseText);
}
}
}
var post = "login=" + form.login.value + "&password=" + form.password.value;
request.open('POST', 'functions/func_login.php');
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.send(post);
location.reload();
};
My function is perfectly called each time I press ENTER or click on Submit, but sometimes the alert doesn't show up and the location.reload(); aren't called.
I don't have any error in my console... and if I manually reload the page, i'm logged so my ajax was sent.
I'm looking for 2 days to find the bug, and doesn't succeed to find. Could someone help me?
I can't use jQuery or another library I've to use JS Vanilla :)
Thank you
Try moving the location.reload(); code in the success block of the ajax, i.e. reload the page after the ajax response is received (if no error is received).