4

So I have successfully deployed an app using webapp2/jinja2 and a Paste server, but am having trouble serving static stylesheets.

I have had luck accessing static files via this method, as well as implementing a StaticFileHandler I found with some google-fu:

import os
import mimetypes
import webapp2
import logging

class StaticFileHandler(webapp2.RequestHandler):
    def get(self, path):
        abs_path = os.path.abspath(os.path.join(self.app.config.get('webapp2_static.static_file_path', 'static'), path))
        if os.path.isdir(abs_path) or abs_path.find(os.getcwd()) != 0:
            self.response.set_status(403)
            return
        try:
            f = open(abs_path, 'r')
            self.response.headers.add_header('Content-Type', mimetypes.guess_type(abs_path)[0])
            self.response.out.write(f.read())
            f.close()
        except:
            self.response.set_status(404)

where my main app routing looks like:

app = webapp2.WSGIApplication([('/', HelloWorld),
                               (r'/display', DisplayHandler),
                               (r'/static/(.+)', StaticFileHandler)
                              ], debug=True)

My css files are in a folder under the app root: /static/css/main.css

I can access the file via direct url, and even link it as a stylesheet, but the styles won't apply. Any ideas? Is there another way to serve stylesheets? Some way to implement an app.yaml similar to GAE?

4

3 回答 3

0

you dont need a static file handler.
upload the app with the static file folder by adding this to your app.yaml

- url: /static/
  static_dir: static

docs are here: https://developers.google.com/appengine/docs/python/config/appconfig#Static_Directory_Handlers

edit: see answer below in comments

于 2012-06-07T15:45:20.213 回答
0

@Mnemon, hats off to you for solving my problem. I would upvote you but I'm not allowed to do that. You convinced me that if it's not the only webapp2 way without GAE, it's at least a way that will work.

But also I can contribute that your solution is now installable as "pip install webapp2_static", from pipi--- by an author who seems to be using his real name... you I'm sure. Other webapp2 docs that I found helpful are available here.

I'm implementing your code on a Linux desktop development server, using paste, which you also used:

def main():
    from paste import httpserver
    httpserver.serve(app, host='127.0.0.1', port='8080')

But with the code as you have it above (which appears to be utterly identical to that of webapp2_static.py file), I don't find that putting my css files in a folder named static in the app root works as you said.

For example, I have /home/user/proj/public_html/app/app.py, where the py file contains your code plus other "views" for my ultra-simple site. (I don't know how paste really works, so maybe for now the public_html is just in there for reference so that I don't become confused when I'm uploading stuff onto the production server.)

So if I put the css stylesheets into a folder named /static, then, if I put /static in as either a subdirectory of /app or of /public_html I find that neither location works; I must instead make it a subdirectory of /proj.

I wasn't expecting that, but the cure for me is to change the default 'static' in your app.configure.get(..., 'static') call, to 'public_html/app/static'. Then it works, with the /static folder inside /app.

Similarly using the pipi code with './app/static/ in place of the default 'static' doesn't work; I found that I need ./public_html/app/static instead (or maybe it was just /public_html/app/static or even public_html/app/static... I forgot... one of those worked).

I tested how your computation of abs_path works and have reworked it in the code below, in which I have junked your approach in favor of something more Djangoesque. To wit, in my one app py file I put at the top the following:

STATIC_DIR = os.sep + 'tostatic' + os.path.abspath(os.path.dirname(__file__)) + os.sep + 'static'

Then in the page to which I want to add css, my Home page in my case, I put a very readable:

<link href="{{STATIC_DIR}}/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css">

For the "view" that generates my Home page I have (env is a jinja2 Environment object that takes a template loader as an argument):

class Home(webapp2.RequestHandler):
    def get(self):
        template = env.get_template('index.html')
        template_values = {'STATIC_DIR': STATIC_DIR }
        self.response.write(template.render(template_values))

And finally the URL routing is as in:

app = webapp2.WSGIApplication(
[
(r'/', Home),
(r'/tostatic/(.+)', StaticView),
], debug=True)

The view for the static file serving is now:

class StaticView(webapp2.RequestHandler):
    def get(self, path):
        path = os.sep + path
        try:
            f = open(path, 'r')
            self.response.headers.add_header('Content-Type', mimetypes.guess_type(path)[0])
            self.response.out.write(f.read())
            f.close()
        except Exception, e:
            print 'Problem in StaticView:', e
            self.response.set_status(404)

To finally close, the problem that I had with your approach is the one that I and other near noobs have with the departure of URLs from the legacy association with the file system. In your approach "static" is both a sub-directory and a string between slashes at the front of the URL that tells the interpreter which view (which webapp2.RequestHandler subclass) to run. You take the /static from the rest of the URL and then later hard-code it back on. And when it comes time to decide what to put in for href in the tag the HTML page coder has to remember that duplicity. With the {{STATIC_DIR}} template variable approach it's clear what to do. And it's easy to redefine the location of the static files--- only the STATIC_DIR declaration has to be changed.

I found that self.response.set_status(404) shows up in Firebug, but not Firefox. Evidently with webapp2 you must provide and serve your own HTTP status code pages.

于 2014-06-04T13:03:32.647 回答
0
self.response.headers.add_header('Content-Type', mimetypes.guess_type(abs_path)[0])
self.response.headers['Content-Type'] = mimetypes.guess_type(abs_path)[0]
于 2016-10-10T11:28:49.887 回答