I want to create random urls. i mean , let first url be myapp.appspot.com/868DF7.html
it ll be connected to test.py in app.yaml. when user opens this url, test.py ll generate new url. I ll save the next url at datastore. and the previous url will not be accessible. I guess the word is "disposable".
Is that possible to create dynamic url like this?
my current test.py : it gets previous string from datastore and creates next url and saves it.
import webapp2
from google.appengine.ext import db
from google.appengine.api import users
import jinja2
import os
import uuid
class Saved(db.Model):
urls=db.StringProperty()
date = db.DateTimeProperty(auto_now_add=True)
def _rKey(r_name=None):
return db.Key.from_path("r", r_name or 'default_r')
class MainPage(webapp2.RequestHandler):
def get(self):
r_name="none"
saveds_query = Saved.all().ancestor(
_rKey(r_name)).order('-date')
urls = saveds_query.fetch(10)
q=db.Query(Saved).order('-date')
print "previous url:", q[0].urls
print "<br>"
save = Saved(parent=_rKey(r_name))
save.urls=str(uuid.uuid4().get_hex().upper()[0:6])+".html"
print "next url:",save.urls
save.put()
APP = webapp2.WSGIApplication([
('/give/', MainPage),
], debug=True)
and app.yaml
handlers:
- url: /favicon\.ico
static_files: favicon.ico
upload: favicon\.ico
- url: /.*
script: helloworld.APP
libraries:
- name: webapp2
version: "2.5.2"
- name: jinja2
version: "2.6"
rest of the files are pretty much the same guestbook example of google.
https://github.com/GoogleCloudPlatform/appengine-guestbook-python/blob/master/guestbook.py
app.yaml lists "rules" that a server uses to match handlers. These rules may use wildcards and regular expressions. For example, you can add the following rule:
- url: /movie/.*
A server would interpret this rule to send all matching URLs to a file or a script that you specify. For example, the following URLs would match this rule:
myApp.appspot.com/movie/1234
myApp.appspot.com/movie/1234.mp4
You can use regular expressions to be as specific as you need in your matching rules.
You should define a handler that is active on a wildcard or a regular expression that matches the format you chose for the random strings.
When you get a request there, have the handler check the specific route that was used, and validate it against (as you correctly noted) the Datastore, Cloud SQL, your own Redis server, etc., which stores resources or references to resources that should be accessible from that special route.
You would then have the handler serve the resource, or at that point, if you want/need, validate authentication somehow (hopefully OAuth of their google account)
Related
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
I've been trying to do a simple webapp on appengine, but I got an issue with handlers.
This is my code:
My app.yaml file:
application: test-app
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /web/.*
script: AppWebInterface.application
My AppWebInterface.py file:
import webapp2
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write('Hello, World!')
application = webapp2.WSGIApplication([
('/', MainPage),
], debug=True)
When I run the dev_server and visit localhost:8080/web , I get this error:
The url "/web" does not match any handlers.
I see two problems, one masking the other.
First, /web/.* will match /web/, /web/foo, but not /web. There are a few approaches. One is to use
- url: /web.*
But this will also match /webelos. Many that's not a problem for you, because the next step will catch it.
The next problem is that, having passed /web/foo through to the application, there's nothing mapping that url pattern to a class. The WSGIAppliction is set up to handle /, though no such URL will make it to the app given the handler in app.yaml.
If you're stuck getting started, try changing the handler in app.yaml to - url: /. Get something simple working, then add handlers from there.
It is because your pattern only match url which contains /web/ (need to have a '/' after /weg)
modify the pattern and see the result.
handlers:
- url: /web.*
script: AppWebInterface.application
I'm building an app on the webapp2 framework in Google App Engine in Python. I've been using Jinja2 as my template engine and Twitter Bootstrap for styling purposes. After building a nice "layout.html" and having all other templates inherit from the "layout.html", I deployed. All pages render property, except one, the one whose url is dynamic.
Here is what the WSGI handler looks like:
webapp2.WSGIApplication = ([('/login', LoginPage),
('/updates', Update),
('/updates/.*', Individual)],
debug = True)
# as you can see I'm using the catchall regex for Individual
Functionally, each dynamically generated url handled by Individual operates properly. Here is the handler, again, everything within this handler is getting executed.
class Individual(Handler):
def get(self):
url_u = str(self.request.url)
posit = url_u.find('updates')
bus1 = url_u[posit+8:]
bus = bus1.replace('%20', chr(32))
b = BusUpdates.all()
this_bus = b.order('-timestamp').filter('bus = ', bus).fetch(limit=10)
name = users.get_current_user()
user = None
if name:
user = name.nickname()
logout = users.create_logout_url(self.request.uri)
self.render("individual.html", bus=bus, user=user, this_bus=this_bus, logout=logout)
A typical url will look like:
http://www.megabusfinder.appspot.com/updates/St%20Louis,%20MO-Chicago,%20IL-4-12-930AM-310PM
Here is my app.yaml file
application: megabusfinder
version: 1
runtime: python27
api_version: 1
threadsafe: no
handlers:
- url: /favicon\.ico
static_files: favicon.ico
upload: favicon\.ico
- url: /static/stylesheets
static_dir: static/stylesheets
- url: /twitter-bootstrap-37d0a30
static_dir: twitter-bootstrap-37d0a30
- url: /static/xml
static_dir: static/xml
- url: .*
script: main.app
builtins:
- remote_api: on
libraries:
- name: webapp2
version: "2.5.1"
- name: jinja2
version: latest
Now, I previously had the "individual.html" inheriting from my "layout.html". As of about an hour ago, I no longer am doing that and I've manually added all the necessary bootstrap that I use in "layout.html" to "individual.html". Even with this, no styling is in effect.
Thanks in advance.
The issue is you are using relative URL paths for your stylesheets, rather than absolute paths. You are doing this:
<link href="styles/bootstrap.css" rel="stylesheet">
When you should be doing this:
<link href="/styles/bootstrap.css" rel="stylesheet">
The issue is that the browser will make a request for a relative URL like that one by combining the existing URL with the relative URL provided in your href (or src for a JavaScript file).
On your root pages the browser makes a request for megabusfinder.appspot.com/styles/bootstrap.css. On your non-root pages it makes a request for megabusfinder.appspot.com/some/sub/path + styles/bootstrap.css ... which doesn't exist, resulting in a 404 (and an unstyled page).
Providing a leading slash ensures that the browser will replace the current path with the href path, rather than combining the paths.
See RFC 3986 for more information on how URIs are merged.
I want to restrict login to a python application running on Google App Engine to members of a particular Google Apps Domain using OpenID.
According to the thread How limit Google Federated Login to specific Apps domain?
this could be accomplished by simply substitution the ordinary google openid autentication url
https://www.google.com/accounts/o8/id
with
https://google.com/accounts/o8/site-xrds?hd=example.com
This does however not seem to work using users.create_login_url() in GAE for Python. It throws a 500 server error that is not shown in the google app engine log (the log only shows the redirect and the "OpenID" from logging.debug).
Does anyone have any suggestions on how to fix this?
app.yaml
application: example
version: 1
runtime: python27
api_version: 1
threadsafe: yes
handlers:
- url: /_ah/login_required
script: main.app
- url: .*
script: main.app
login: required
main.py:
import webapp2, logging
from google.appengine.api import users
# Any google account, works like a charm
#federated_identity='https://www.google.com/accounts/o8/id'
# only accounts under spefific domain, does not work
federated_identity='https://google.com/accounts/o8/site-xrds?hd=example.com'
dest_url = 'http://example.appspot.com/'
class Main(webapp2.RequestHandler):
def get(self):
logging.debug('Main')
user = users.get_current_user()
if user:
self.response.out.write('Hello %s<p>[log out]' % (user.email(),
users.create_logout_url(self.request.uri)))
else:
self.response.out.write('Not logged in')
class OpenID(webapp2.RequestHandler):
def get(self):
logging.debug('OpenID')
login_url = users.create_login_url(dest_url=dest_url,
federated_identity=federated_identity)
self.redirect(login_url)
app = webapp2.WSGIApplication([
('/_ah/login_required', OpenID),
('/', Main)
], debug=True)
Update
Sebastian suggests that a solution might be to url encode the federated identity. I tried url encoding the whole url or only the question mark as suggested. Unfortunately this does not change anything.
The redirect urls as shown in the browser address bar or if written to log:
No url encoding:
http://example.appspot.com/_ah/login_redir?claimid=https://google.com/accounts/o8/site-xrds?hd=example.com&continue=http://example.appspot.com/
With url encoding:
http://example.appspot.com/_ah/login_redir?claimid=https%3A%2F%2Fgoogle.com%2Faccounts%2Fo8%2Fsite-xrds%3Fhd%3Dexample.com&continue=http://example.appspot.com/
I think (I haven't tested this myself) that the issue is because the federated_identity is not encoded. Try replacing the question mark with %3F. Also make sure the url
https://google.com/accounts/o8/site-xrds?hd=example.com
works.
The test I did was to go to the url
http://testsk2012.appspot.com/_ah/login_redir?claimid=https://www.google.com/accounts/o8/site-xrds%3Fhd=somesite.com&continue=http://testsk2012.appspot.com/
and it succeeded.
I am new at this and am planning to move my stuff from a paid web service to GAE (nothing dynamic just static pages). Believe me I have spent countless hours trying to make this work but am at an impasse whereby I achieve one result at the exclusion of another and visa versa.
I am sure it is a simple answer and that I am violating some basic principles. What I want is that the app engine page (mygaeid.appspot.com) delivers a static landing page such that other pages are available with the addition of a suffix e.g. mygaeid.appspot.com/oranges.html mygaeid.appspot.com/grapes.html etc.
I am unable to achieve this such that I either am able to get the other pages when I add the suffix e.g. mygaeid.appspot.com/apples.html; mygaeid.appspot.com/oranges.html but not the landing page OR with a slightly different yaml the landing page (mygaeid.appspot.com) works but there is no access to the other pages (mygaeid.appspot.com/oranges.html etc) that have a suffix.
The py file (abcdefg.py) is below and is common to the two different yamls that follow:
import os
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp import template
class MainHandler(webapp.RequestHandler):
def get (self, q):
if q is None:
q = 'htmdir/apples.html'
path = os.path.join (os.path.dirname (__file__), q)
self.response.headers ['Content-Type'] = 'text/html'
self.response.out.write (template.render (path, {}))
def main ():
application = webapp.WSGIApplication ([('/(.*html)?', MainHandler)], debug=True)
util.run_wsgi_app (application)
if __name__ == '__main__':
main ()
Using the following yaml the landing page (mygaeid.appspot.com) works perfectly (delivering the content of apples.html), but I cannot access the page if I add a suffix e.g. mygaeid.appspot.com/apples.html or mygaeid.appspot.com/static/htmdir/apples.html etc, as soon as I add the suffix it does not work. In the directory (htmdir) I have placed apples.html along with other html pages e.g. oranges.html etc and I cannot access any of them with this yaml.
application: mygaeid
version: 1
runtime: python
api_version: 1
handlers:
- url: /(.*\.(html))
static_files: static/htmdir/\1
upload: static/htmdir/(.*\.(html))
- url: /css
static_dir: css
- url: /js
static_dir: js
- url: /images
static_dir: images
- url: .*
script: abcdefg.py
If I modify the yaml as follows then the landing page (mygaeid.appspot.com) does not work but when I add the suffixes then it works perfectly e.g. mygaeid.appspot.com/apples.html; mygaeid.appspot.com/oranges.html etc deliver the appropriate pages:
- url: /(.*\.(html))
static_files: htmdir/\1
upload: htmdir/(.*\.(html))
Finally if I dispense with the directories altogether and using the same abcdefg.py (without the directory) the following very simple yaml actually delivers the results I want but is very unruly as all the files are stuffed in the root directory.
application: mygaeid
version: 1
runtime: python
api_version: 1
handlers:
- url: /(.*\.(png|js|css))
static_files: \1
upload: (.*\.(png|js|css))
- url: .*
script: abcedfg.py
any help would be much appreciated on figuring this out.
thanks
thanks wooble and thanks dave I went back yet again and carefully read the logs Wooble's solution works but I needed to put the htmdir (that contains the html) inside a directory called static. GAE is a great (and free) solution for static websites
your help and feedback is very much appreciated
SiteDirectory
-mygaeid
-static
-htmdir
-js
-css
-images
app.yaml
index.yaml
(py file was removed)
If you declare files as static in app.yaml, they are not available to your application's handlers.
However, if they're really static, using the django template engine to "render" them is kind of silly; just add mappings in app.yaml to display the static files, including one to display apples.html for /:
application: mygaeid
version: 1
runtime: python
api_version: 1
handlers:
- url: /(.*\.html)
static_files: static/htmdir/\1
upload: static/htmdir/.*\.html
- url: /css
static_dir: css
- url: /js
static_dir: js
- url: /images
static_dir: images
- url: /
static_files: static/htmdir/apples.html
upload: static/htmdir/apples\.html
(no python files needed)
Woobie has the right answer. Let me phrase it differently.
When you put .html in static_files, they're served by separate services that are dedicated to serving static content. Your app will not be able to see those files. The app can only see files that are resources.
Django templates must be resources, not static files. Otherwise, the application can't see them, and the template.render(path, ... will fail.
A next step to getting your problem solved is (if you haven't done so aleady) is to update your post to show your current app.yaml, and also to show us what's being reported up in the application's logs.
There are technical reasons why it works this way
The app.yaml config functions in a very simple top->bottom procedural manner.
Matching happens in the following order:
1. - url: /(.*\.(html))
static_files: static/htmdir/\1
upload: static/htmdir/(.*\.(html))
2. - url: /css
static_dir: css
3. - url: /js
static_dir: js
4. - url: /images
static_dir: images
5. - url: .*
script: abcdefg.py
To put it simply, if the file has a .html suffix it gets matched in step 1 and reroutes that request from mygaeid.appspot.com/.html to mygaeid.appspot.com/htmdir/.html. The *.html handler in step 5 never gets hit because all *.html routes are already spoken for.
In addition -- as the answers have already covered -- directories marked as static will not be available locally to your app handlers. I'll try to address the technical reasons why.
app.yaml acts as a configuration file for GAE's reverse proxy. Static files only change when they're uploaded so they're ideal for caching. By immediately pushing the static files to a cache server when they're deployed, GAE increases loading performance and removes unnecessary load from the app servers.
There's a good reason that static requests are counted separately and cost less than regular app requests. Every time you request a static file you're essentially pulling fetching the file from GAE's CDN. If you were to only fetch static files, then your server would have no reason to spool up in the first place.
The obvious downside to that approach is that those files don't physically exist on the same server as your app so you can't fetch or upload them directly in your app.
Note: Also following what other answers have covered. Don't mark your template folder as static. When your app goes to load the template -- instead of grabbing it from an adjacent directory -- it'll have to send out a web request and fetch the file from a remote location. Obviously, fetching a remote file instead of a local file is going to increase load time and latency.