I've setup a static website on GAE using hints found elsewhere, but can't figure out how to return a 404 error. My app.yaml file looks like

- url: (.*)/
  static_files: static\1/index.html
  upload: static/index.html

- url: /
  static_dir: static

with all the static html/jpg files stored under the static directory. The above works for files that exist, but returns a null length file if they don't. The answer is probably to write a python script to return a 404 error, but how do you set things up to serve the static files that exist but run the script for files that don't?

Here is the log from fetching a non-existent file (nosuch.html) on the development application server:

ERROR    2008-11-25 20:08:34,084 dev_appserver.py] Error encountered reading file "/usr/home/ctuffli/www/tufflinet/static/nosuch.html":
[Errno 2] No such file or directory: '/usr/home/ctuffli/www/tufflinet/static/nosuch.html'
INFO     2008-11-25 20:08:34,088 dev_appserver.py] "GET /nosuch.html HTTP/1.1" 404 -

Solution 1:

You need to register a catch-all script handler. Append this at the end of your app.yaml:

- url: /.*
  script: main.py

In main.py you will need to put this code:

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class NotFoundPageHandler(webapp.RequestHandler):
    def get(self):
        self.error(404)
        self.response.out.write('<Your 404 error html page>')

application = webapp.WSGIApplication([('/.*', NotFoundPageHandler)],
                                     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()

Replace <Your 404 error html page> with something meaningful. Or better use a template, you can read how to do that here.

Please let me know if you have problems setting this up.

Solution 2:

google app engine now has Custom Error Responses

so you can now add an error_handlers section to your app.yaml, as in this example:

error_handlers:

- file: default_error.html

- error_code: over_quota
    file: over_quota.html

Solution 3:

A significantly simpler way to do this without requiring any CPU cycles is to place this handler at the bottom of your app.yaml

- url: /.*
    static_files: views/404.html
    upload: views/404.html

This then allows you to place a static 404.html file in your views directory. No need for a python handler. Anything that isn't handled in your app.yaml already will hit that.

Solution 4:

You can create a function to handle your errors for any of the status codes. You're case being 404, define a function like this:

def Handle404(request, response, exception):
     response.out.write("Your error message") 
     response.set_status(404)`

You can pass anything - HTML / plain-text / templates in the response.out.write function. Now, add the following declaration after your app declaration.

app.error_handlers[404] = Handle404

This worked for me.

Solution 5:

webapp2 provides the error_handlers dictionary that you can use to serve custom error pages. Example below:

def handle_404(request, response, exception):
    logging.warn(str(exception))
    response.set_status(404)
    h = YourAppBaseHandler(request, response)
    h.render_template('notfound')

def handle_500(request, response, exception):
    logging.error(str(exception))
    response.set_status(500)
    h = YourAppBaseHandler(request, response)
    h.render_template('servererror')

app = webapp2.WSGIApplication([
    webapp2.Route('/', MainHandler, name='home')
    ], debug=True)
app.error_handlers[404] = handle_404
app.error_handlers[500] = handle_500

More details are available on webapp2's documentation pages: http://webapp-improved.appspot.com/guide/app.html#error-handlers