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)
Related
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
I just cannot seem to get multiple handlers working. I'm using Google App Engine with Python. The base URL returns "Hello world!", as expected, but I keep getting a 404 error when I try to visit "/girl".
As far as I can tell I'm doing exactly what is specified in the docs:
https://cloud.google.com/appengine/docs/python/config/appconfig
And in this similar question:
YAML file url and script in GAE python
and yet trying all variants I can think of on these models does not resolve my 404 problem. I am a beginner and don't really understand how the app.yaml file works, thus I'm pretty sure that I'm not specifying the handlers correctly. But I don't know how to fix it.
app.yaml
application: multiapp
version: 1
runtime: python27
api_version: 1
threadsafe: yes
handlers:
- url: ./girl/.*
script: girl.app
- url: .*
script: main.app
libraries:
- name: webapp2
version: "2.5.2"
main.py
import webapp2
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.write('Hello world!')
app = webapp2.WSGIApplication([
('/', MainHandler)
], debug=True)
girl.py
import webapp2
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.write('Hey girl!')
app = webapp2.WSGIApplication([
('/',MainHandler),
('/girl/', MainHandler)
], debug=True)
Added: Different organization of the project could definitely avoid having to solve this problem, but I would also like to know why setting these multiple handlers isn't working to begin with.
I know this is an old thread, and that you sort of got your answer, but I want to give a better explanation of what was happening, as I have just found the answer to your fundamental question: How to split handlers into different files?
The key problem is that you are working with regular expressions, both when you declare your WSGIApplication
app = webapp2.WSGIApplication([
('/', MainHandler),
('/girl/', GirlHandler)
], debug=True)
and in your yaml file
handlers:
- url: ./girl/.*
script: girl.app
- url: .*
script: main.app
The path in your yaml url: regex must be the full path from your webapp domain. So if your domain is localhost:8080 then in your yaml file yo should add the url from localhost:8080 onwards. If you type url: /girl/.*
you're asking the browser to match domain+regex: localhost:8080/girl/.*
This means:
match this part exactly: localhost:8080/girl/
match any characters that follow the first part (as that's what ".*"
means in a regex)
So the following yaml statement:
handlers:
- url: /girl/.*
script: girl.app
means that for any url of the form domain/girl/ anything (or nothing) use the the app variable in girl.py file app = webapp2.WSGIApplication(...)
The first implication of this, is there is no purpose in writing a handler for a url that girl.py will never handle, such as when you coded:
girl.py
import webapp2
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.write('Hey girl!')
app = webapp2.WSGIApplication([
('/',MainHandler),
], debug=True)
You will never use this main handler, as it will only 'activate' when you browse for domain/, but you said in your yaml file that you only wanted girl.py to handler urls of the form domain/girl/something. It's a contradiction
This means that for your setup to work, in girl.py you should only write handlers for urls that match the regex in your yaml. in this case, any regex that is also accepted by /girl/.*.
As a sidenote, if you wanted your girlHandler to work on either domain/girl and domain/girl/ you should use this regex in your yaml file:
handlers:
- url: ./girl(?:/.*)?
script: girl.app
- url: .*
script: main.app
as this makes the /.* part optional
Hope this helps anyone reaching this question, as an insight of how webapp2 calls each handler depending on the url given
You should bundle all your routes together in one file, and have different handlers for different routes.
main.py
import webapp2
class MainHandler(webapp2.RequestHandler):
def get(self):
self.response.write('Hello world!')
class GirlHandler(webapp2.RequestHandler):
def get(self):
self.response.write('Hey Girl!')
app = webapp2.WSGIApplication([
('/', MainHandler),
('/girl/', GirlHandler)
], debug=True)
then in your app.yaml you only have to link to main.app
application: multiapp
version: 1
runtime: python27
api_version: 1
threadsafe: yes
handlers:
- url: .*
script: main.app
libraries:
- name: webapp2
version: "2.5.2"
You don't need multiple webapp2.WSGIApplication instances.
You could have central routing while keeping handlers in separate files with something along these lines in main.py:
app = webapp2.WSGIApplication([
('/girl/.*', "girl.MainHandler")
('/.*', MainHandler),
], debug=True)
Another possibility of running largely independent "apps" while still be able to share some info across them (like authentication) would be to make them modules of the main app. While offering more flexibility in the long run they do have a non-neglijible learning curve (and docs aren't always up2date for them)
Organizational issues aside, the reason this wasn't working was that I didn't include a trailing slash.
"/girl" (no slash) returns 404;
"/girl/" (with slash) renders the page.
Also the lines in app.yaml should be:
handlers:
- url: /girl/.*
script: girl.app
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 approaching to Google App Engine.
I want to implement some handlers, but I get a "Oops! This link appears to be broken." error for each of them:
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
class MainPage(webapp.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write('Hello, webapp World!')
application = webapp.WSGIApplication([('/', MainPage)], debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
if I used a simple print function (i.e. print "2gf"), all would work perfectly.
This is my app.yaml file:
application: sample-app
version: 1
runtime: python
api_version: 1
handlers:
- url: /aaa/aaa
script: helloworld.py
- url: /bbb/bbb
script: helloworld2.py
Suggestions?
Your code is old, and the yaml file is pointing the python scripts/apps at the wrong urls. Try the code below:
import webapp2
class HomePageHandler(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write('Hello appengine!')
app = webapp2.WSGIApplication([('/', MainPage)],
debug=True)
And the app.yaml file should contain something like this:
application: helloworld
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: helloworld.py
Also, go through the following App Engine python tutorial. It explains the main concepts of coding for App Engine really well. It helped me greatly when I got started.
In your app.yaml you do not have any routing instructions for /. Therefore when you hit a URL that does not match one of those already in your YAML, your app doesn't know what to do and shows you that error. In order to fix that you need to provide a 'default' handler for anything else that isn't specified in your app.yaml. As mentioned by #Tkingovr (+1 to him), you want to point the default (/.*) to your script. Add that handler at the bottom of your app.yaml and point it to your main script. However I agree with #Tkingovr - switching over to 2.7 now (when you're first learning) will make things easier in the long run :)
I am trying to make a simple application using Google App Engine.
Below is my code
helloworld.py
print "hello"
class helloworld():
def myfunc(self):
st = "inside class"
return st
test.py
import helloworld
hw_object = helloworld.helloworld()
print hw_object.myfunc()
app.yaml
handlers:
- url: /.*
script: helloworld.py
- url: /.*
script: test.py
When I run my application via http://localhost:10000 it prints only hello whereas my expected output is hello and inside class.
My directory structure
E:\helloworld>dir
app.yaml helloworld.py test.py
I am pretty sure this has something to do with Script Handlers.So, what is the correct way to define handlers and what is wrong in my way of defining them.
When your first handler pattern /.* matches http://localhost:10000, the remaining handlers are all ignored.
You can updated your app.yaml
handlers:
- url: /hello
script: helloworld.py
- url: /test
script: test.py
And browse http://localhost:10000/test
Please walk through the Getting Started guide from the appengine documentation. It will help you get through the initial setup problems like this.
http://code.google.com/appengine/docs/python/gettingstarted/helloworld.html
Here is the sample Handler from that documentation.
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
class MainPage(webapp.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write('Hello, webapp World!')
application = webapp.WSGIApplication(
[('/', MainPage)],
debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
Note that the class extends webapp.RequestHandler, the method name is get (or post if you are responding to a http post request) Also the extra code at the bottom for setting up the application. You can add extra URL's to the application by adding arguments to the WSGIApplication. For example:
application = webapp.WSGIApplication(
[('/', MainPage)],
[('/help/', HelpPage)],
debug=True)
Also note that in your app.yaml as both scripts refer to the same url pattern, there is no way that any request will ever get to test.py. The normal pattern is to have specific url patterns at the top and a catch-all patter last.
Good Luck.
I had a similar problem too. Expanding on Hamish's answer, and correcting the last part where the square brackets are:
application = webapp.WSGIApplication([
('/', MainPage),
('/help/', HelpPage)],
debug=True)
Reference:
https://webapp-improved.appspot.com/guide/routing.html
** Edit I also had an extra closing bracket in my code above. Changed that now.