Google App Engine:使用高级路由正确配置静态站点

Google App Engine: Correctly configuring a static site with advanced routing

我花了几个可耻的时间试图解决这个问题,但无济于事...

问题:

我正在开发一个静态网站,该网站 100% 通过 Grunt 和 Assemble 进行了预处理(如果您熟悉 Jekyll,它本质上是相同的概念)。它还有一个简单的静态博客组件,其中包含各种名称的类别目录。因此,我需要 app.yaml 中的包罗万象来适当地路由它们。

但是,我也希望有一个自定义错误页面来代替标准的 GAE 页面状态。

app.yaml两个场景你好像无法单独完成统计,因为你只能使用一次catch-all target。

这是我目前的逻辑app.yaml

- url: (.*)/
  static_files: dist/index.html
  upload: dist/index.html
  expiration: "15m"

- url: /(.*)
  static_files: dist//index.html
  upload: dist/(.*)/index.html
  expiration: "15m"

这非常适合我的用例,因为它会将任何路径路由到索引文件(如果它存在于当前目录中)。但是,因为它使用了包罗万象,所以我不能再将它用于以下内容

- url: /(.*)
  static_files: custom_error.html

或取决于

error_handlers:
  - file: custom_error.html

因为它只呈现没有匹配 url 模式的路径...

想法:

我接下来的想法是,我可以通过外部 Python 脚本使用一些高级路由来完成此操作

- url: /.*
  script: main.app

但在尝试了无数种设置之后,我还没有找到实现这一目标的方法。

我使用的面包屑路径的一个例子是

import os
import webapp2
import jinja2

# vars
jinja_environment = jinja2.Environment(loader=jinja2.FileSystemLoader('jinja'), extensions=['jinja2.ext.autoescape'], autoescape=True)

class mainHandler(webapp2.RequestHandler):
    def get(self):
        if (an-index-file-exists-in-this-directory)
            # follow the same static file logic as the app.yaml
            # static_files: dist//index.html
            # upload: dist/(.*)/index.html
        else:
            template = jinja_environment.get_template('404/index.html')
            context =  {
                'page_title': '404',
            }
            self.response.out.write(template.render(context))
            self.error(404)

app = webapp2.WSGIApplication([
    ('/.*', mainHandler)
], debug=False)

我什至不确定将其放入外部 python 文件是否有助于解决问题,但这是我笨拙的尝试。

当你的包罗万象的模式被用于另一个重要目的时,有人知道如何实现自定义错误页面吗?

更新:已解决

好吧,我终于弄明白了,但是因为 Stack Overflow 认为我不够酷,无法回答我自己的问题(低点阈值?),我在这里发布了解决方案:

https://gist.github.com/dustintheweb/c5e6e4ee1a64d50d7f87

祝你好运!

我不认为你可以让你的第一个解决方案像那样工作,因为正如你所说它将匹配所有模式并且错误处理程序永远不会匹配。

并且除非您计划在每次要添加新博客 post 时部署您的应用程序,否则我不认为拥有静态处理程序是一个可行的解决方案,因为您无法直接在您的网站上上传内容静态应用程序文件夹。

但是您可以将内容上传到 GCS(例如)并从 WSGIApplication 检索它。

然后这个 WSGIApplication 可以有一个自定义错误处理程序:

def handle_404(request, response, exception):
   response.write("my custom error 404")

app.error_handlers[404] = handle_404

如果未找到模板,您只需引发 404 并调用此处理程序。

正如@Anthuin 的回答所指出的,您不能写入(也不能修改)磁盘上的那些index.html 文件(也不能动态创建新文件),所以它尝试从磁盘 read 它们毫无意义——GAE 应用程序可用的 "disk" 是只读的(并且在仅可用于静态服务的部分和应用程序代码本身可读的部分)。

相反,除非 index.html 文件很大(我怀疑不太可能),否则我会将它们保存在您的应用程序的 GAE 数据存储区 中。一个非常简单的模型:

from google.appengine.ext import ndb

class Indx(ndb.Model):
    body = ndb.TextProperty()

假设路径不超过 500 个字符,主体不超过 1 兆字节。然后,你的 MainHandler 变得非常简单:

class MainHandler(webapp2.RequestHandler):

    def get(self):
        key = ndb.Key('Indx', self.request.path)
        ent = key.get()
        if ent:
            self.response.write(ent.body)
        else:
            # your existing 404-returning code goes here

app.yaml 路由 /.* 到此脚本,您的 app = 代码不需要更改。

现在,唯一剩下的就是,您希望如何编写或修改这些 index.html 文件(现在是数据存储实体)?我不知道,因为只要它们 文件,您的应用就不可能写入或修改它们。不管怎么说,既然都在data store里了,写起来也变得很简单了:

def write_indx(path, body):
    ent = Indx(body=body, id=path)
    ent.put()

请注意,没有真正的 "modify" -- 可能 "modify" 一些 index.html 的 "body",你实际上会阅读上一个,做一个新建body字符串,并用上面的write_indx.

改写实体

潜在问题包括:正文超过 1 MB;以及超过 500 个字符的键(路径)。正如@Anhuin 建议的那样,可以通过使用 Google Cloud Storage 而不是 GAE 数据存储轻松解决前者问题;后者可能是一个问题,因为即使是 GCS 对象名称也有限制(不同于 GCS 对象长度)——具体来说,一旦名称转换为 utf-8,则为 1024 字节。这些问题中的任何一个对您来说可能是个问题吗?如果是这样,请告诉我们!