Anuj
Anuj

Reputation: 9622

Static files not loaded in a Bottle application when the trailing slash is omitted

I am serving a test file through apache using Bottle.

Following are my apache config:

WSGIDaemonProcess temp user=www-data group=www-data processes=1 threads=5
WSGIScriptAlias /temp /opt/gridops/usage/temp/adapter.wsgi

<Directory /opt/gridops/usage/temp>
        WSGIProcessGroup temp
        WSGIApplicationGroup %{GLOBAL}
        Order deny,allow
        Allow from all
</Directory>

adapter.wsgi:

import os,sys
os.chdir(os.path.dirname(__file__))
sys.path = ['/opt/gridops/usage/temp'] + sys.path
os.chdir(os.path.dirname(__file__))
sys.stdout = sys.stderr
import bottle
print "++"*10
import index # This loads your application
application = bottle.default_app()

index.py:

from bottle import mount, run 
from routes import app
from bottle import default_app
default_app.push(app)
#run()
#run(app=app, host='192.168.1.3', port=8085) 

routes.py:

from bottle import Bottle , run,route,static_file,view,template,post,request

app = Bottle()
print str(dir(app))
@app.route('/static/<filename>')
def server_static(filename):
    return static_file(filename, root='static')

@app.route('/') 
def index(): 
        return template('template',text='This is index page!')

template.tpl:

<html>
<head>

<link rel="stylesheet" type="text/css" href="static/prettify.css" />
</head>
<body>
{{text}}
</body>

</html>

Directory Listing

temp/
  adapter.wsgi
  index.py
  routes.py
  static/
     prettify.css
  views/
     template.tpl

My issue is whenever I try to access the Bottle app using http://192.168.1.3/temp the webpage shows up without the static files, but whenever i access http://192.168.1.3/temp/ [Please note the extra /] the page loads properly. What modification should I do such that the result of both http://192.168.1.3/temp and http://192.168.1.3/temp/ become the same?

Any help would be highly helpful

Upvotes: 2

Views: 9586

Answers (3)

Nicolas AGIUS
Nicolas AGIUS

Reputation: 55

Another workaround is to add this redirection in your Apache configuration file :

RedirectMatch 301 ^/(temp)$ /$1/

This will add a / at the end of your index page, so you don't have to modify your code.

Upvotes: 1

user3381053
user3381053

Reputation: 1

One workaround is to add:

<base href="/temp/">

to the head in the template.

Upvotes: 0

Helgi
Helgi

Reputation: 5436

The Problem

The problematic line is this one:

<link rel="stylesheet" type="text/css" href="static/prettify.css" />

The address of the CSS file is a relative one, thus the full absolute address is computed from the loaded page location.

For http://192.168.1.3/temp/, it will be http://192.168.1.3/temp/static/prettify.css (correct).

For http://192.168.1.3/temp, it will be http://192.168.1.3/static/prettify.css. temp is considered to be a file in the root directory, not a subdirectory of its own right.

The Solution

There is no viable way to use a single relative address for referring to the static resources. Your application will likely to have “nested” paths like /article/some-name, or /view/content/566, or something like this, as well as paths as simple as /.

You can try to specify a root-based path, like /temp/static/prettify.css, in your template, but this means you will have to change the template if you relocate the app itself (say, to myapp.example.com/ from example.com/myapp/).

Instead, you need to tell the framework to make the correct path to the resource you need to use. Bottle has a function named get_url to facilitate this. Unfortunately, it's not mentioned in the Bottle tutorials.

The Code

Here's what you should do.

In template.tpl, call get_url referring to the static handler:

<link rel="stylesheet" type="text/css" 
      href="{{ get_url('static', filename='prettify.css') }}" />

In routes.py, import get_url:

from bottle import Bottle, run, route, static_file, view, template, 
                   post, request, get_url

Then, name your handler so you can pass its name to get_url:

@app.route('/static/<filename>', name='static')
def server_static(filename):
    return static_file(filename, root='static')

Finally, supply the actual get_url as the template argument when rendering the template:

@app.route('/') 
def index(): 
    return template('template', text='This is index page!', get_url=get_url)

Alternatively, instead of supplying get_url in every handler, set up a template default in index.py:

from Bottle import SimpleTemplate
SimpleTemplate.defaults["get_url"] = app.get_url

Caveat: The last method seems to be undocumented, but was explained by the Bottle's author on the mailing list.

Final Thought

As every page on a website should have a canonical address, you might want to choose one form (either with a trailing slash or without one) as canonical, and add some kind of redirect from the other one.

Upvotes: 15

Related Questions