how do I test the new router that Jonathan has made.
I renamed the routes.examples.py to routes.py and edited it. No mather
what I write in it gets used. I tried to find the answer in all posts on
the mailinglist but no success.
Kenneth
Start with router.example.py instead. The key to using the new router is to define a dict named 'routers'. There's more documentation in the file, and I'll repost some examples below.
Post your routes.py here, along with a description of what you intend that it does (if it's not obvious).
======
Suppose you've written an app, named it 'myapp', and want to make it the default, with its name always removed. Your default controller is still 'default', and you want to remove its name from user-visible URLs as well. Here's what you put in routes.py:
routers = dict(
BASE = dict( default_application='myapp' ),
)
That's it. And it's smart enough to know how to do the right thing with URLs like:
http://domain.com/myapp/default/myapp
or http://domain.com/myapp/myapp/index
...where normal shortening would be ambiguous.
If you have two applications, myapp and myapp2, you'll get the same effect, and additionally myapp2's default controller will be stripped from the URL whenever it's safe (which is mostly all the time).
Another case. Suppose you want to support URL-based languages, where your URLs look like this:
or (rewritten)
Here's how:
routers = dict(
BASE = dict( default_application='myapp' ),
myapp = dict( languages=['en', 'it', 'jp'], default_language='en' ),
)
Now an incoming URL like this:
http:/domain.com/it/some/path
will be routed to /myapp/some/path, and request.uri_language will be set to 'it', so you can force the translation. You can also have language-specific static files.
http://domain.com/it/static/filename
will be mapped to:
applications/myapp/static/it/filename
...if that file exists. If it doesn't, then URLs like:
http://domain.com/it/static/base.css
...will still map to:
applications/myapp/static/base.css
(because there is no static/it/base.css)
So you can now have language-specific static files, including images, if you need to.
Domain mapping is supported as well.
routers = dict(
BASE = dict(
domains = {
'domain1.com' : 'app1',
'domain2.com' : 'app2',
}
),
)
does what you'd expect.
routers = dict(
BASE = dict(
domains = {
'domain.com:80' : 'app/insecure',
'domain.com:443' : 'app/secure',
}
),
)
...maps http://domain.com accesses to app's controller named 'insecure', while https accesses go to the 'secure' controller. Or you can map different ports to different apps, in the obvious way.
There's more, but mostly everything happens automatically, and there's no need to dig into the details of configuration unless there's some non-standard thing you need. There's a bit more documentation in router.example.py.
> Post your routes.py here, along with a description of what you intend
that it does (if it's not obvious).
I just added:
routers = dict(
BASE = dict(
domains = {
'economy.nudata.fi' : 'economy',
'testing.nudata.fi' : 'testapplication',
}
),
)
but if if go to economy.nudata.fi I get to welcome.
Kenneth
Are you running the current trunk? There hasn't been a release with the new router yet. Try adding the abc line below (note the comma); you should get a complaint about an unknown key.
routers = dict(
BASE = dict(
abc = None,
One more question: does this work OK with the old routing logic?
I�m running the newest Nightly Built?
> One more question: does this work OK with the old routing logic?
I have not used any routing logic so far?
Kenneth
I'm not sure what's in the nightly build. The abc= line should tell us something, and I can give you a little more debugging depending on what it says.
What's your environment? Host system, server configuration, etc? There are some configurations (like mod_proxy) where you have to do domain-based routing at the server rather than web2py, I think.
abc gave nothing, it loaded welcome without any errors, or were should I
see any complaints about an unknown key?
I�m running on linux, CentOS, MySQL, mod_wsgi, Apache.
I guess I have to give trunk a go, never used Mercurial so it�ll be an
experiment.
Kenneth
Yes, you should get a syntax error:
raise SyntaxError, "unknown key '%s' in router '%s'" % (key, app)
In this case: "unknown key 'abc' in router 'BASE'"
I'll download the currently nightly and take a look.
The nightly is pretty old.
Massimo?
Where can you see if my upgrading to a trunk version worked? I did a hg
clone https://web..... and then copied everything to my web2py folder.
Earlier I edited the routes file, now I noticed a new file named
router.examples.py. I added those lines to it. But can�t any errors and
welcome app is always loaded,
Kenneth
So far so good. You need to copy your router file to routes.py, though.
If you cp router.example.py to routes.py and then replace the routers dict with yours, that should be a good start.
On Jan 16, 2011, at 5:11 PM, Matt wrote:
>
> Hi Jonathan,
>
> I'm also trying to use the new routing approach and I'm having a
> little trouble with static files.
>
> In my app's static directory I have sub folders (containing files)
> like this:
>
> /static/css/base.css
> /static/images/logo.png
> /static/js/jquery.js
>
> I'm using the most minimal routing:
>
> routers = dict(
> BASE = dict(
> default_application = 'app',
> ),
> )
>
> routes_onerror = [(r'*/*', r'/error')]
>
> Now when I try and request certain files I seem to get very unexpected
> results....
>
> When I try:
>
> http://localhost:8000/css/base.css
>
> it works.
OK, I wouldn't expect that to work.
Your app name is 'app', right?
>
> however when I request an invalid link like:
>
> http://localhost:8000/css/base2.css
>
> It doesn't raise a 404 as I'd expect.
>
> Also for some reason I can't seem to use:
>
> http://localhost:8000/static/css/base.css (gives a 403 error and
> doesn't redirect to error).
>
> But I have to use 'static' for javascript files:
>
> http://localhost:8000/static/js/jquery.js
>
> As
>
> http://localhost:8000/js/jquery.js gives an error and redirects to :
>
> http://localhost:8000/error?code=400&ticket=None&requested_uri=/js/jquery.js&request_url=/js/jquery.js
>
> Any suggestions?
I'll investigate, using your parameters. Can't promise to get to it tonight, though.
> If you cp router.example.py to routes.py and then replace the routers dict with yours, that should be a good start.
I copied the file, changed ownership, permissions, but nothing. Not until I restarted Apache was the routes.py noticed and used. Now my econopy.nudata.fi goes to economy application the way I wanted.
Thank you,
Kenneth
Ah, great.
Restarting Apache probably restarted web2py as well, which is necessary for a new routes.py.
I've been looking at this, and a couple of things aren't making sense to me. Your configuration sounds fine, at least what I see of it. I've been testing it like this:
routers = dict(
BASE = dict(
default_application = 'app',
),
app = dict(
controllers = ['default', 'error', 'appadmin'],
),
)
routes_onerror = [(r'*/*', r'/error')]
...because I don't have an application installed named 'app'; this tells the router logic to pretend there's such an app, with the indicated controllers (if the controller list is different, please let me know, but it shouldn't much affect things).
You wrote that this works:
http://localhost:8000/css/base.css
...but it really shouldn't, since there's no way for the router to recognize the URL as a static file. I'd expect it to be rewritten as:
http://localhost:8000/app/css/base.css
...that is, treating 'css' as a controller. I would expect this to work:
http://localhost:8000/static/css/base.css
So I'm wondering first if you're running the current trunk (last few days, anyway). If not, the 'routers=' entry will be ignored.
Second, it would be helpful to turn on router logging. To do so, copy logging.example.conf to logging.conf, and then edit the rewrite logger and console handler to log debug messages:
[logger_rewrite]
level=DEBUG
qualname=web2py.rewrite
handlers=consoleHandler
propagate=0
...
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
(If you're not able to log to the console, you'll have to do more configuration work to log elsewhere.)
Another thing to try, and I recommend this for anyone doing routing, is to customize the doctest in routes.py for your own routing policy. Try replacing the existing doctest with this (if this doesn't work, please send me your routes.py privately and I'll edit it myself and send it back). You run the doctest at the cli:
python routes.py
>>> import os
>>> import gluon.main
>>> from gluon.rewrite import load, filter_url, filter_err, get_effective_router, try_redirect_on_error
>>> load(routes=os.path.basename(__file__))
>>> filter_url('http://localhost:8000/css/base.css', router='app')
'app'
>>> os.path.relpath(filter_url('http://domain.com/favicon.ico'))
'applications/app/static/favicon.ico'
>>> filter_url('http://domain.com/abc')
'/app/default/abc'
>>> filter_url('http://localhost:8000/css/base.css')
"/app/default/css ['base.css']"
>>> os.path.relpath(filter_url('http://domain.com/static/css/base.css'))
'applications/app/static/css/base.css'
>>> from gluon.http import HTTP
>>> from gluon.storage import Storage
>>> http = HTTP(400, 'original http message', Location='some/location')
>>> request = Storage()
>>> request.env = Storage()
>>> request.application = 'app'
>>> request.env.request_uri = 'request_uri'
>>> request.url = 'request.url'
>>> http = try_redirect_on_error(http, request)
>>> (http.status, http.body, http.headers.get('Location'))
(303, 'You are being redirected <a href="/error?code=400&ticket=None&requested_uri=request_uri&request_url=request.url">here</a>', '/error?code=400&ticket=None&requested_uri=request_uri&request_url=request.url')
>>> filter_url('https://domain.com/app/ctr/fcn', out=True)
'/ctr/fcn'
>>> filter_url('https://domain.com/welcome/ctr/fcn', out=True)
'/welcome/ctr/fcn'
>>> filter_url('https://domain.com/app/default/fcn', out=True)
'/fcn'
>>> filter_url('https://domain.com/app/default/index', out=True)
'/'
>>> filter_url('https://domain.com/app/appadmin/index', out=True)
'/appadmin'
>>> filter_url('http://domain.com/app/default/fcn?query', out=True)
'/fcn?query'
>>> filter_url('http://domain.com/app/default/fcn#anchor', out=True)
'/fcn#anchor'
>>> filter_url('http://domain.com/app/default/fcn?query#anchor', out=True)
'/fcn?query#anchor'
>>> filter_err(200)
200
>>> filter_err(399)
399
>>> filter_err(404)
'/error?code=404&ticket=tkt'
Thanks, Martín. Is this the current trunk?
What's happening is that LOAD is calling URL in a peculiar way (Massimo, would you take a look, please?). It's making this call:
html.URL(request.application,c,f, args=args,vars=vars,extension=extension)
...which means that URL isn't able to pass request on to the URL rewriter.
As a temporary workaround, you can do the URL rewriting yourself by adding a url= argument to your LOAD call:
..., url=URL(r=request, c='plugin_comments', f='comments.load', args=[tablename, record_id, page])
in which case you don't have to pass those args to LOAD (but I think it does no harm).
Massimo, I think that LOAD's call to html.URL needs to include request, or else simply call URL, which will take care of it automatically. Do you see any reason not to do that? (This will be a non-problem once we have request-scope globals available in gluon.)
yes is trunk
If you'd like to try a likely fix to LOAD, you can make this change. In gluon.compileapp.LoadFactory, you'll see this line toward the end:
url = url or html.URL(request.application,c,
f, args=args,vars=vars,
extension=extension)
Just add r=request:
url = url or html.URL(request.application,c,
f, args=args,vars=vars, r=request,
extension=extension)
I just noticed that this pattern appears twice. Martín, I think you're using the second one, but it shouldn't hurt to patch both.
Hopefully this is now fixed in the trunk. I wasn't able to test it, but at the very least it should do no harm, and it should fix this particular problem.
Don't worry about it; I'm happy it's working for you.
If possible, it's better to do the redirection through .htaccess. It's more efficient, since web2py never has to run.
This should also work, if you don't really need the redirect, so it's also efficient:
routers = dict(
BASE = dict(
domains = {
'domain1.com' : 'app1',
'www.domain1.com' : 'app1',
'domain2.com' : 'app2',
}
),
)
My impression is that search engines (Google anyway) are fairly smart about encountering the same content at related domains, and IIRC Google has a protocol for telling them which address is preferred.
If you still want to do it through web2py, I think I'd do the rewrite as above (for both domain1 and www.domain1) and in models/0redirect.py (or at any rate, before any other application code runs), something like:
if request.env.http_host == 'www.domain1.com':
redirect("http://domain1.com%s" % request.env.request_uri, 301)
or more generally:
redirects = {
'www.domain1.com' : 'domain1.com',
...
}
if request.env.http_host in redirects:
scheme = request.env.get('wsgi_url_scheme', 'http').lower()
uri = "%s://%s%s" % (scheme, redirects[request.env.http_host], request.env.request_uri)
redirect(uri, 301)
None of that is tested, and you'd want to check it all, including appropriate escaping (if it's not done already). But you get the idea. It also doesn't take port overrides into consideration.
Thanks for the quick reply! Your suggestion works fine. Perfect for my low traffic server with lots of small sites. I'll look into getting Apache to manage redirects if (when!) it gets busier.The redirect is useful as if a user comes to the www. version of the site and then to the no www version they lose their session as the cookies don't match. With your suggestion the session is maintained regardless of the URL entered.
Initially I tried the short version. Just tried the long version and it works too - impressively no typos in your untested code! Both also work nicely for http and https.What are the pros and cons of the short v long version?
The long version seems useful if you had more than one or two domains that you needed to redirect
, and it retains the incoming http vs https scheme.