Running the dev server for App Engine standard python 3 is not routing requests properly.
dev_appserver.py app.yaml
The app.yaml file has 3 handlers.
runtime: python37
instance_class: F1
inbound_services:
- warmup
handlers:
- url: /api/.*
script: auto
secure: always
- url: /
static_files: public/index.html
upload: public/index.html
secure: always
- url: /
static_dir: public
secure: always
Locally the requests to /api/whatever all return 404 errors.
When I deploy the app to GCP the requests succeed.
The reasoning for my setup is statically hosting Angular 7 app while also hosting an API the angular app calls.
Since the issue is only associated with the dev server, I think this is a bug. There is a similar python 2 example here: https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/appengine/standard/angular/app.yaml
Has anyone else run into this? Any workarounds?
Update: As requested in the comments here is an example main.py file.
# [START gae_python37_app]
import logging
from flask import request, url_for
from flask_api import FlaskAPI, status, exceptions
# Create flask app
app = FlaskAPI(__name__)
#app.route("/api/whatever", methods=["GET"])
def doSomething():
response = {"message":"placeholder"}
return response
if __name__ == "__main__":
# This is used when running locally only. When deploying to Google App
# Engine, a webserver process such as Gunicorn will serve the app.
app.run(host="127.0.0.1", port=8080, debug=True)
# [END gae_python37_app]
UPDATE: I attempted to recreate the issue and there indeed seems to be a discrepancy between the dev_appserver.py and the deployed version. I have created this issue on Google’s Issue Tracker to be properly followed up by the App Engine engineering team.
The answer below is only valid if your root directory doesn't contain a static_dir path
The issue is with the way that routing is implemented. I attempted to replicate the issue that you were having and I got the same results.
In my case, I was able to resolve it by changing the route from /api/.* to ('/api/<path:path>'), since the function associated with that handle will then be properly defined as a catch-all 1.
Please refer to the code[2] provided along with the links provided1.
1 https://flask.palletsprojects.com/en/1.1.x/quickstart/#routing
main.py
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
"""Return a friendly HTTP greeting."""
return 'Hello World!'
#app.route('/api/<path:path>')
def catch_all(path):
return 'Hello api page!'`
if __name__ == '__main__':
# This is used when running locally only. When deploying to Google App
# Engine, a webserver process such as Gunicorn will serve the app. This
# can be configured by adding an entrypoint to app.yaml.
app.run(host='127.0.0.1', port=8080, debug=True)`
app.yaml
runtime: python37
handlers:
- url: /
script: auto
- url: /api/.*
script: auto
Related
I have the following code which works successfully when I deploy it on App Engine. Except I want to run it with a cron.
import pandas as pd
import requests
from flask import Flask
app = Flask(__name__)
#app.route('/')
def sfLibraries():
return 'Success'
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
cron.yaml
cron:
- description: "SF Loader"
url: /task/loader
schedule: every 1 hours
app.yaml
runtime: python38
handlers:
- url: /task/loader
script: auto
I know that if I change #app.route('/') to #app.route('/task/loader') that the cron will work successfully, yet I lose the capability to go to the site when I first deploy it and see success.
gcloud app browse
Did not detect your browser. Go to this link to view your app:
https://<<<my_synapse>>>.uc.r.appspot.com
https://<<<my_synapse>>>.uc.r.appspot.com:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
How can I have it both ways -both able to go to the site via gcloud app browse and see success as well as run the cron successfully?
I mentioned WSGI Handler because I found this pertinent post but not sure how WSGI Handler might help or improve from what Flask is doing Setting up cron job in google app engine python
Create two different routes to accomplish what you need. The functions must have a different name in this case.
#app.route('/')
def sfLibraries():
return 'Success'
#app.route('/task/loader')
def sfLibrariesSecond():
return 'Success'
Assign different actions for each one.
They could also both do the exact same thing. If that was the case, then assign two routes to the same function.
#app.route('/')
#app.route('/task/loader')
def sfLibraries():
return 'Success'
I am currently working on a web app using Django. I used this app.yaml and it is deployed successfully on google app engine. However, I couldn't access /_hc, because Django checks whether the url is in url patterns and it just responds with 'Page not found'. How do I fix this?
runtime: python37
entrypoint: bash -c 'gunicorn -b :$PORT projectservice.wsgi'
liveness_check:
path: "/_hc"
check_interval_sec: 30
timeout_sec: 4
failure_threshold: 2
success_threshold: 2
handlers:
# This configures Google App Engine to serve the files in the app's
# static directory.
- url: /static
static_dir: static/
# This handler routes all requests not caught above to the main app.
# It is required when static routes are defined, but can be omitted
# (along with the entire handlers section) when there are no static
# files defined.
- url: /.*
script: auto
# [END django_app]
This is fine, but you need to have a url handler for /_hc that returns a 200 response:
from django.http import HttpResponse
def health_check_handler(request):
return HttpResponse("healthy", content_type='text/plain')
in urls.py:
(r'^_hc$', 'health_check_handler'),
I use Flask as Web Application, to redirect http to https, I added such codes in app.py:
#app.before_request
def before_request():
if request.url.startswith('http://'):
url = request.url.replace('http://', 'https://', 1)
return redirect(url, code=301)
if __name__ == '__main__':
app.run()
and the gunicorn command in supervisor.conf is:
command=/home/MyToDo/venv/bin/gunicorn --certfile=/home/MyToDo/SSL/mytodo.vip.cer --keyfile=/home/MyToDo/SSL/mytodo.vip.key -w3 -b0.0.0.0:443 -b0.0.0.0:80 app:app
but when I visit the website, I still need to add "https://" in front the url, it didn't redirect automaticly. So how to make gunicorn redirect http to https, does using Nginx is the only way?
I had the same problem running Flask on Google App Engine Flexible environment with gunicorn. Solved by using werkzeug.contrib.fixers.ProxyFix of Werkzeug version 0.14.1.
from werkzeug.contrib.fixers import ProxyFix
app = Flask(__name__)
app.wsgi_app = ProxyFix(app.wsgi_app)
I had the same issues using Google App Engine and have spend too much time on this since all the answers including werkzeug.contrib.fixers.ProxyFix and the official docu's custom HTTPMethodsOverrideMiddleware or other custom solutions did not work for me.
I finally managed to solve this by configuring gunicorn using a config which sets:
secure_scheme_headers = {'X-FORWARDED-PROTO': 'https'} and forwarded_allow_ips = '*'
Additionally, for the ones wanting to serve static files and who are struggling with unwanted http redirects, adding handlers for each static directory in the .yaml configuration and avoiding nested sub-directories solved this issue.
Flask==2.1.3
gunicorn==20.1.0
Consider the two following scenarios:
There are two url handlers in app.yaml
handlers:
- url: /main
script: main.app1
- url: /secondary
script: secondary.app2
and URI router in main.py
app1 = webapp2.WSGIApplication([('/main', MainHandler)])
and another in secondary.py
app2 = webapp2.WSGIApplication([('/secondary', SecondaryHandler)])
vs
There is one url handler in app.yaml
handlers:
- url: /.*
script: main.app
and the URI router decides the handler
app = webapp2.WSGIApplication([
('/main', MainHandler),
('/secondary', SecondaryHandler)
])
Is there any difference in how the App Engine imports the two scenarios? If all requests are for MainHandler, does the App Engine import the files associated with SecondaryHandler at all in the first scenario or does an instance always import each handler when it is first initialized?
Obviously these are different ways to partition the application logically, but I'm asking if there are any associated performance considerations.
You can use a lazy handler in webapp2 to optimize loading and use a single app.
See this link : https://webapp2.readthedocs.io/en/latest/guide/routing.html#lazy-handlers
I'm just now digging into GAE and I see two ways to make a particular URL pull up the right page.
The first way is using handlers:
handlers:
- url: /.*
script: helloworld.py
The other way is using the following:
application = webapp.WSGIApplication(
[('/', MainPage),
('/sign', Guestbook)],
debug=True)
Which is better or which is right? I don't fully understand what the second example is doing exactly.
You need to use both. The section in app.yaml tells App Engine where to look for your WSGI application. application = webapp.WSGIApplication(...) sets up your WSGI application using the webapp framework.
update:
app.yaml:
handlers:
- url: /city.*
script: cityhandler.py
cityhandler.py
application = webapp.WSGIApplication([('/city', ShowCityPage)],
debug=True)