-->

App Engine throws 404 Not Found for any path but r

2020-05-08 05:58发布

问题:

I want to split my App Engine implementation into several files. So I wrote in my app.yaml file:

runtime: python27
api_version: 1
threadsafe: true

handlers:

- url: /imageuploader
  script: imageuploader.app

- url: /search
  script: main.app

- url: /
  static_files: index.html
  upload: index.html

libraries:
- name: MySQLdb
  version: "latest"

Basically I search and image upload to be on different paths and root path to have index file.

Here are my handlers:

main.py:

class MainPage(webapp2.RequestHandler):

    def get(self):
        #Implementation

app = webapp2.WSGIApplication([
    ('/', MainPage),
], debug=True)

imageuploader.py:

class UserPhoto(ndb.Model):
    blob_key = ndb.BlobKeyProperty()


class PhotoUploadFormHandler(webapp2.RequestHandler):
    def get(self):
        # [START upload_url]
        upload_url = blobstore.create_upload_url('/upload_photo')
        # [END upload_url]
        # [START upload_form]
        # To upload files to the blobstore, the request method must be "POST"
        # and enctype must be set to "multipart/form-data".
        self.response.out.write("""
<html><body>
<form action="{0}" method="POST" enctype="multipart/form-data">
  Upload File: <input type="file" name="file"><br>
  <input type="submit" name="submit" value="Submit">
</form>
</body></html>""".format(upload_url))
        # [END upload_form]


# [START upload_handler]
class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        try:
            upload = self.get_uploads()[0]
            user_photo = UserPhoto(
                user=users.get_current_user().user_id(),
                blob_key=upload.key())
            user_photo.put()

            self.redirect('/view_photo/%s' % upload.key())

        except:
            self.error(500)
# [END upload_handler]


# [START download_handler]
class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)
# [END download_handler]


app = webapp2.WSGIApplication([
    ('/', PhotoUploadFormHandler),
    ('/upload_photo', PhotoUploadHandler),
    ('/view_photo/([^/]+)?', ViewPhotoHandler),
], debug=True)
# [END all]

Now, the weird thing is that if I set

-url: /.*
    script: main.app

It all works. I also tried /search/.+ and /imageuploader/.+. I must have forgotten something completely obvious but I cant figure out what is it.

回答1:

Each app.yaml route script config (like script: imageuploader.app) requires a python file with a matching name (imageuploader.py in this case) which is defining an app called app (just like the main.py does) with app routes being, of course, a subset of (or equal to) the corresponding path from the app.yaml file (/imageuploader in this case).

So, problems with your implementation:

  • the main.py app path is / - doesn't match the corresponding /search path in app.yaml
  • none of the 3 app path patterns in imageuploader.py matches the corresponding /imageuploader path in app.yaml
  • the / path in app.yaml is actually mapped to the static resource index.html, so it'll be served by GAE directly - no point adding a handler for it as that handler should never be invoked (you may want to revisit the static resources mapping).

Your attempt using /.* mapped to main.app could only have worked before you removed the handler code now in imageuploader.py, but should not be working with the main.py content from your question.

Assuming your patterns in the app.yaml are the ones you desire, these would be the app configs you'd need (to continue with your approach):

main.py:

app = webapp2.WSGIApplication([
    ('/search', MainPage),
], debug=True)

imageuploader.py:

app = webapp2.WSGIApplication([
    ('/imageuploader', ...),  # TODO: revisit mappings
], debug=True)

But I'd suggest a simpler to manage approach, using webapp2's lazy loading of the handler code.

You keep a single, generic mapping in the app.yaml, which would match any url pattern in the main.py file:

- url: /.*
  script: main.app

You have a single app route mapping definition in main.py, but which can lazy-load handlers from both main.py as well as other modules/files. For example something like this:

app = webapp2.WSGIApplication([    
    ('/upload', 'imageuploader.PhotoUploadFormHandler'),
    ('/upload_photo', 'imageuploader.PhotoUploadHandler'),
    ('/view_photo/([^/]+)?', 'imageuploader.ViewPhotoHandler'),
    ('/main_page', MainPage),
], debug=True)

A middle-ground approach is also possible, allowing you to completely de-couple imageuploader.py from main.py but with a slightly more careful url pattern choice.

You could have in app.yaml:

- url: /photo.*
  script: imageuploader.app

And in imageuploader.py have url patterns matching that /photo.* one:

app = webapp2.WSGIApplication([
    ('/photo_upload_form', PhotoUploadFormHandler),
    ('/photo_upload', PhotoUploadHandler),
    ('/photo_view/([^/]+)?', ViewPhotoHandler),
], debug=True)