I'm trying to set up a service on Google App Engine, but am having trouble getting XmlHttp to work consistently with it.
After deploying, the website can be accessed from 2 different urls: service-dot-project.appspot and version-dot-service-dot-project.appspot, and for some reason there is inconsistencies between the two.
Heres some demo code that verifyably causes me trouble.
# routes.py
from flask import render_template
from . import app
#app.route("/test", methods=["GET"])
def test():
return render_template("test.html")
#app.route("/api/test", methods=["GET"])
def api_test():
return "It Works!"
# templates/test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Debug</title>
</head>
<body>
<div id="out"></div>
<button type="button" onclick="run()">
Test the thing.
</button>
<script>
function run() {
let xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = () => {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200)
document.getElementById("out").innerText = xmlHttp.responseText;
}
xmlHttp.open("GET", "/api/test", true);
xmlHttp.send(null);
}
</script>
</body>
</html>
# service.yaml
runtime: python38
service: name
automatic_scaling:
min_idle_instances: 1
instance_class: F4
entrypoint: gunicorn -b :$PORT main:app
env_variables:
...
When I try and press the button on the version url, it works as intended, and "It Works!" gets printed into the div above the button, but on the service url (without the version specified), the page itself loads, but pressing the button causes the request to hang for a few seconds, before printing this to the console:
GET https://service-dot-project.appspot.com/api/test [HTTP/2 404 Not Found 7912ms]
When testing using a local flask debugging environment, the problem does not occur.
Is there something that Google App Engine does that I should know about that may have caused this issue to happen? Is /api a reserved endpoint? The rest of my endpoints works on the service url, its only the api endpoints that break. My only app.before_request method fails with a 403, not a 404, so this cannot be the cause.
if you go to https://console.cloud.google.com/appengine/versions
and select your service that is having troubles, is there some other version that is receiving the traffic instead of your desired version?
Also, try going to the logs, find the entry for the 404, expand it and see which version is throwing that error, under protoPayload > versionId
It seems that the issue is being caused by one of the other services running on our project.
Our default service is defining in its dispatch.yaml
dispatch:
- url: "/api*"
module: otherservice
Which is intercepting all the requests made to myservice-dot-project and redirecting them to otherservice-dot-project
Why this isn't the case for the version url is probably because there is no version of the default service with the same version number.
The fix is to either change the dispatch url of the default service, or change the url of the new service's API endpoints.
Related
I'm learning AJAX according to MDN tutorial, but when I try the first sample to fetch test.html, local server always response with 404, no matter I use absolute or relative path. I have read other similar questions in stackoverflow, but none of them can solve my problem.
Here is my directory structure and source code:
|--templates
| |--index.html
| |--test.html
|
|--app.py
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>index</title>
</head>
<body>
<button id="ajaxButton" type="button">Make a request</button>
<script>
(function(){
let httpRequest
document.getElementById("ajaxButton").addEventListener('click', makeRequest)
function makeRequest() {
httpRequest = new XMLHttpRequest()
if (!httpRequest) {
alert('Giving up: can not create an XMLHTTP instance')
return false
}
httpRequest.onreadystatechange = alertContents
httpRequest.open('GET', 'test.html', true)
httpRequest.send()
}
function alertContents() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
alert(httpRequest.responseText)
}else {
alert('There was a problem with the request.')
}
}
}
})();
</script>
</body>
</html>
I have tried templates/test.html or put test.html outside templates directory but it always return 404 even though I find the url in console is http://127.0.0.1:5000/templates/test.html, which should be right.
I think I must misunderstand someting about URL and server or it matters with flask?
Just in case, here is app.py:
from flask import Flask, render_template
app = Flask(
__name__,
template_folder='./templates'
)
#app.route('/')
def index():
return render_template('index.html')
You're trying to do requests to test.html, but that route does not exist, you have only defined the / route in your Python code which renders index.html template.
If you want the user to access arbitrary templates (e.g. 127.0.0.1:5000/templates/my-template.html), you can write the following route:
#app.route('/templates/<template_name>')
def view_template(template_name):
return render_template(template_name)
Once you've defined that route, the request to /templates/test.html should be successful:
httpRequest.open('GET', '/templates/test.html', true)
Flask looks for the specified template in templates folder by default when you call render_template.
I am struggling with an application I want to make, which is a web application that will scrape an other website using the selenium package with chromedriver.
I am using python 3.7 on elementary OS.
Everything works okay if I don't include the loading page, even if I didn't put it online yet so I dunno for sure, but my problem is that I would like to let the crawling headless and no sandbox, while having the waiting page in my app.
I'd like to render to a success html template when the job is done, too.
I searched on stackoverflow for answers, but I couldn't make my thing work. (sorry if duplicate)
One thing that I found is Luiz Aoqui's answer to this question : Flask is not render_template before executing long function , and it seems like the OP could solve his issue.
I couldn't, though.
I don't know javascript at all so maybe you will find this very simple, if the problem came from here.
The python flask code :
#app.route('/auto_connect/', methods=["GET", "POST"])
def connect():
if session['mail'] != None:
if request.method == "POST":
session['job'] = request.form['job']
return redirect(url_for('process', fun='auto_connect'))
return render_template('auto_connect.html')
return redirect(url_for('login'))
#app.route("/process/<fun>", methods=['GET', 'POST'])
def process(fun, *args):
if fun == 'auto_connect' or fun == 'auto_apply':
if request.method == 'GET':
return render_template('wait.html', fun=fun)
if request.method == 'POST':
print('test')
if fun == 'auto_connect':
auto_connector(session['mail'], session['password'], session['job'])
return 'done'
elif fun == 'auto_apply':
auto_applyer(session['mail'], session['password'], session['job'], session['location'])
return 'done'
else:
return "error"
return 'error'
The JS code :
var request = new XMLHttpRequest();
request.open('POST', '/process/'.concat({{fun}}));
request.onload = function() {
if (request.status === 200 && request.responseText === 'done') {
// long process finished successfully, redirect user
window.location = '/success/' ;
} else {
// ops, we got an error from the server
alert('Something went wrong. FROM server');
}
};
request.onerror = function() {
// ops, we got an error trying to talk to the server
alert('Something went wrong. TO server');
};
request.send();
The loading page is showing, but the scraping does not start.
I expect it to start with the "open" POST request in the js code which is part of the 'wait.html' template.
PS : there is the flask debugger, I put a print at the top of my scraping script which shows up in terminal when I do no render the loading page, but doesn't when I do.
127.0.0.1 - - [04/Aug/2019 02:03:20] "GET /auto_connect/ HTTP/1.1" 200 -
127.0.0.1 - - [04/Aug/2019 02:03:22] "POST /auto_connect/ HTTP/1.1" 302 -
127.0.0.1 - - [04/Aug/2019 02:03:22] "GET /process/auto_connect HTTP/1.1" 200 -
Thanks in advance for your answers.
I found the answer myself to my question.
I am not a web programmer so I lack some automatism sometimes, but by inspecting my waiting page, I saw an error in the JS script in the concat function.
Calling '{{fun}}' in the script instead of {{fun}} did the thing.
I didn't understand this behavior since my object fun is a python string, but I read the jinja2 doc and all became lucid to me, we can see in this link https://jinja.palletsprojects.com/en/2.10.x/templates/ the following:
{{ ... }} for Expressions to print to the template output
This prints the value, so it is no longer a string but raw text in html template.
(type(print(x)) is None in python3 btw)
If this helps someone then I'm glad.
My application server: python+flask
UI: html + javascript with callback and POST method
Application runs fine with redirection, rest api call etc at:
http://127.0.0.1:5000/
When Submit button is clicked, nothing happens from:
http://localhost:5000/
Also nothing happens from:
http://finhelper.herokuapp.com/ <-- html + java scripts can be read here
I think none of the Javascripts are working for some reason when running without 127.0.0.1.
CSS and separate pages work though.
EDIT:
the below javascripts are taking me to: http://127.0.0.1:5000/approve
Which is same as:
http://finhelper.herokuapp.com/approve
This redirection is not working from Heroku.
<script type="text/javascript">
function Submit(callback) {
//url = 'http://finhelper.herokuapp.com/api';
url = '/api';
ItemJSON = '[ {"Married": 0,"Education": 0,"ApplicantIncome": 5000,"CoapplicantIncome": 1100,"LoanAmount": 150,"Credit_History": 1}]';
//alert(url);
var xhttp;
var l_redir ='/';
xhttp=new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
//alert('in this');
myFunction(this);
}
};
xhttp.open("POST", url, true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.send(ItemJSON);
//alert('sent REST call');
window.location.href = l_redir; //not being used
return false;
}
function myFunction(xhttp) { //Callback function
//alert('in callback');
retval = xhttp.responseText;
//alert(retval);
var redir = '-';
if (retval.includes("approved")) {
redir = "/approve";
} else {
redir = "/reject";
}
//alert(redir);
redirect(redir); //Final redirection depending upon ML Return value
}
function redirect(redir) {
window.location.href = redir;
return false;
}
</script>
2nd EDIT:
I can access this POST api that is running on same app.py from CURL but not from Javascript. Hence not getting httpResponse, identified using alert().
Any reason or suggestions please?
curl http://finhelper.herokuapp.com/api -k -X POST -H "Content-Type: application/json" -d "[{\"Married\": 0,\"Education\": 0,\"ApplicantIncome\": 5000,\"CoapplicantIncome\": 1100,\"LoanAmount\": 150,\"Credit_History\": 1}]"
Use app.run(host= '0.0.0.0') instead of app.run() only
I added alert() at almost each step of the javascript.
It turned out, on Heroku, python REST app was taking bit longer time and hence asynchronous ajax call was timing out.
For now I have added some delay but ideal way would be to implement below:
Need a delay function javascript
For people facing same issue after updating their mac to macOS Monterey, the new Airplay feature is listening to port 5000.
You can check that by typing
lsof -i:5000
to solve this issue, Control Center stops listening to those ports when “AirPlay Receiver” in the “Sharing” System Preference is turned off.
More details on the link below
https://developer.apple.com/forums/thread/682332
I'm fiddling around with Google Cloud Storage. I've created a simple Python Flask handler:
#!/usr/bin/env python3
import secrets
import flask
from flask_cors import CORS
from google.cloud import storage
app = flask.Flask(__name__)
CORS(app)
client = storage.Client()
bucket = client.get_bucket('<my-bucket>')
#app.route('/')
def get_upload_urls():
blob = bucket.blob('00000' + secrets.token_hex())
return flask.jsonify({
'url': blob.create_resumable_upload_session(),
})
if __name__ == '__main__':
app.run('0.0.0.0', 9999)
This is accompanied by a really simple web frontend:
index.html:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width">
<title>Test</title>
</head>
<body>
<input id="input" type="file" />
<button id="button">Upload</button>
<script src="index.js"></script>
</body>
</html>
index.js:
const input = document.getElementById('input')
const button = document.getElementById('button')
button.addEventListener('click', async () => {
const [ body ] = input.files
const response = await fetch('http://localhost:9999')
const { url } = await response.json()
await fetch(url, {
method: 'PUT',
body,
})
})
This frontend allows me to pick a file, and upload it to Google Cloud Storage using a resumable upload session created by the Python backend.
The problem with this is that it actually works. I'd expect the PUT request to fail, but it doesn't.
When a file has been selected and the upload button is pressed, the following errors are logged to the console:
index.html:1 Failed to load
https://www.googleapis.com/upload/storage/v1/b//o?uploadType=resumable&upload_id=:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:3333' is therefore not allowed
access. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
index.js:13 Uncaught (in promise) TypeError: Failed to fetch
async function (async)
button.addEventListener # index.js:5
However, the PUT request was made succesfully and the file shows up in the Google Cloud Storage. I can download it and it appears to be totally fine.
Why doesn't the PUT request fail despite the CORS error in the console?
Edit:
I'm just looking for an explanation, not for a workaround — I'm going to configure CORS properly anyway. I would just like to know why the request doesn't fail, and why fetch does reject.
#sideshowbarker was right, I had the same issue. CORS needs to be set also in PUT response in addition to OPTIONS preflight request. Then fetch will succeed (even though upload worked before).
I am trying to develop an app which sends a HTTP POST request to a NodeJS server (on the localhost). I am using the networking library 'Alamofire' just to try and simplify things.
My issue arises when I try and send multiple requests, I set up a simple button which sends the post request. When I run the app i can click the button and see that it sends the post requests when I click it, but only for the first 4 button presses/post requests.
It may be possible that my server is not set up correctly and not sending a response back or something along those lines.
I get 4 post's each time i open the app - everytime i close and re open the app i get another 4.
The same goes for when I restart the server.
I have tested the server using a pre-existing app (HTTP utility) and it appears to work fine.
When I use Alamofire i use JSON encoding, could this cause the issue???
Here's my swift code:
import UIKit
import Alamofire
class ViewController: UIViewController {
#IBAction func postBTN(sender: AnyObject) {
post(["xx":[10]])
}
override func viewDidLoad() {
super.viewDidLoad()
}
func post(postParrams: [String : AnyObject]) {
Alamofire.request(.POST, "http://10.0.0.10:3000", parameters: postParrams, encoding: .JSON)
}
}
UPDATE *** I added the following code below the 'Alamofire.request' to print the response data:
.validate()
.responseJSON { response in
print(response.request)
print(response.response)
print(response.result)
switch response.result {
case .Success:
print("Validation Successful")
case .Failure(let error):
print("the error is", error)
}
}
The error code that prints is:
the error is Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x7fd5a3d0e170 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=http://localhost:3000/, NSErrorFailingURLKey=http://localhost:3000/, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}
From this i gather that the response result is a 'failure' and there are 2 error codes: Code=-1001, CodeKey=-2102