I have a flask app with an index.html that has a submit input that runs a process. I'm trying to catch the error traceroute, then navigate to an error page to display that traceroute from the process if an error occurs. But on the server, my render_template()
is not being called.
index.html that has a submit input that runs a process by redirecting to b.html where a method using the Fetch api fetches the mapped path /run
, and the process runs. The process runs fine, but if I insert an error for testing, an error is thrown and it sends the following JSON object to b.html:
{'run_result':'run_failure' , 'error_output':output}
Above, output is stdout=PIPE from subprocess.run() if returncode is not zero.
In b.html, I have this:
const nav = async() => {
const response = await fetch('run');
if( response.status !== 200 ) {
throw new Error('error message');
}
return await response.json();
};
document.addEventListener('DOMContentLoaded', () => {
nav().then(data => {
if(data != null) {
if(typeof(data) === 'object') {
if(data['run_result'] === 'run_success'){
// ...successful run <== works fine!
} else {
var error_output = data['error_output'];
var sent_data = { 'data': error_output};
fetch(`/run_error`, { // posts data to server uri
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(sent_data)
})
// ***I used to have '=> response.json()' here, but I got SyntaxError: "JSON.parse: unexpected character at line 1 column 1..."
.then(response => response)
// ***console only displays 'success:' here, not sent_data value
.then(console.log('Success:', sent_data));
}
}
}
});
});
On the server, I have:
# server output shows the below is called:
# 127.0.0.1 - - [..date..] "POST /run_error HTTP/1.1" 200
@app.route('/run_error', methods=['POST'])
def error():
if request.method = 'POST':
request_json = request.json
error_output = request_json['data']
# perfectly prints out a str version of error trace
print('***error:', error_output)
# run_error.hthml is **not** rendered with output - Why?
return render_template('run_error.html', error_output=error_output)
Why doesn't render_template() get called?
4/20/21 RESPONSE
I was unable to do like you suggested below due to syntax errors I got when I ran.
.then(
console.log('Success:', sent_data);
var response = JSON.parse(response);
var error_output = response["error_output"];
window.location.href = "display_error/"+error_output;
);
...but here is where I am now (the commented out then() statement prints the correct value to console):
document.addEventListener('DOMContentLoaded', () => {
nav().then(data => {
if(data != null) {
if(typeof(data) === 'object') {
if(data['run_result'] === 'run_success'){
// ...successful run <== works fine!
} else {
var error_output = data['error_output'];
var sent_data = { 'data': error_output};
fetch(`/run_error`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(sent_data)
})
.then(response => response.text())
.then(response => JSON.parse(response))
//.then(response => console.log(response['error_output']))
.then(response => response['error_output'])
.then(response => {
window.location.href = 'display_error/' + response;
})
}
}
}
});
});
@app.route('/run_error', methods=['POST'])
def error():
if request.method = 'POST':
request_json = request.json
error_output = request_json['data']
return json.dumps({'error_output': error_ouput})
@app.route('/display_error/<error_output>', methods=['GET'])
def display_error(error_ouput):
return render_template('run_error.html', error_output=error_output)
When it hits the error, I get a 404. The browser URL field shows:
127.0.0.1:5000/display_error/Traceback....
^^^Traceback has no quotes. Should it?
The server console shows:
127.0.0.1 - - [date] "GET /display_error/Traceback...." 404
Suggestions?
It is because you are returning the template within the handling of a POST request. You are actually parsing the template as a response to the client instead of as a page to be rendered. Instead, I recommend you to redirect to another view function to display the error with a GET request from the client side, which you pass the error_output
to as a value of a query string. If you need to run some logic on the output beforehand, you can keep the original view function error
. Else, you can just make use of the new one display_error
, without having to go through the first one.
@app.route('/run_error/', methods=['POST'])
def error():
if request.method = 'POST':
request_json = request.json
error_output = request_json['data']
return json.dumps({'error_output': error_ouput})
@app.route('/display_error/', methods=['GET'])
def display_error():
error_output = request.args.get('error_output')
return render_template('run_error.html', error_output=error_output)
The client side will then after a successfull post request (or without it) redirect to the display_error
view function and pass error_output
as a parameter:
.then(response => response)
.then(
console.log('Success:', sent_data);
var response = JSON.parse(response);
var error_output = response["error_output"];
window.location.href = "/display_error/?error_output="+error_output;
);