Python 3.x 在不了解应用程序的情况下,如何处理与应用程序根相关的URL引用';谁的根URL?

Python 3.x 在不了解应用程序的情况下,如何处理与应用程序根相关的URL引用';谁的根URL?,python-3.x,cherrypy,mako,Python 3.x,Cherrypy,Mako,我正在使用CherryPy、Mako HTML模板和JavaScript编写一个应用程序。我想允许它被安装到任何网址-也就是说,我希望有人能够安装到,或,或。我还希望它能像Apache后面的WSGI应用程序或使用CherryPy的内置Web服务器一样工作。然而,我在处理模板和JavaScript必须使用的URL时遇到了问题 也就是说,如果我想访问与我的应用程序根相关的资源,比如api/something或static/application.js,我如何确保无论我的应用程序的基本URL是什么,引

我正在使用CherryPy、Mako HTML模板和JavaScript编写一个应用程序。我想允许它被安装到任何网址-也就是说,我希望有人能够安装到,或,或。我还希望它能像Apache后面的WSGI应用程序或使用CherryPy的内置Web服务器一样工作。然而,我在处理模板和JavaScript必须使用的URL时遇到了问题

也就是说,如果我想访问与我的应用程序根相关的资源,比如
api/something
static/application.js
,我如何确保无论我的应用程序的基本URL是什么,引用都能正常工作

我最初的天真解决方案是使用相对URL,但当我想在多个端点返回相同的Mako模板时,这就停止了工作。也就是说,如果我有一个用于显示项目的模板,那么我可能希望在页面的根目录(
/
)的某个地方显示该项目,也可能在其他地方(比如
/Item
,也可能
/username/items
)。如果模板中有一个相对URL,则该URL是相对于该位置的-这意味着,由于我的静态CSS/JS/图像资源仅与根相关,因此将在
/item
/username/items
位置断开模板的链接

Mako模板 我发现我可以在Mako模板中用
cherrypy.url()
包装我的url,这就解决了这个问题。例如,此示例模板将正确引用
/link
URL,无论:

<%!
    import cherrypy
%>
<a href="${cherrypy.url('/link')}">Click here!</a>

这很好,但有点麻烦,在我的模板中导入cherrypy似乎很奇怪。这样做对吗?有更好的办法吗?如果它是自动的,那就太好了,所以我不必记得自己包装URL

JavaScript 我使用CherryPy的
tools.staticdir
选项提供static.js文件。这很好,但是我进行了几个AJAX调用,就像我的Mako模板中的静态资源一样,URL是相对于我的应用程序根的。例如,如果CherryPy公开了一个
/api/something
URL,并且我想从JavaScript访问它,那么我如何编写JS,以便无论我的CherryPy应用程序安装在何处都可以访问它

我的第一个想法是,我可以向模板中添加某种隐藏的HTML元素或注释,这些元素或注释包含
cherrypy.url()
的值,在没有参数的情况下调用,这将生成我的应用程序的根url,并且我可以遍历DOM,获取该值,在我尝试发出HTTP请求之前,将其预先添加到我想要的任何URL。好处是,这在JavaScript中非常透明;缺点是,当我向应用程序添加越来越多的模板时,很容易忘记包含magic hidden HTML元素。我想我可以通过使所有模板都依赖于根模板来解决这个问题,但它看起来仍然像是一个黑客

我的第二个想法是将我的JavaScript文件本身转化为Mako模板,而不是使用
tools.staticdir
来服务它们,而是使用我在现有Mako HTML模板中使用的相同
cherrypy.url()
方法。这具有一致性的吸引力,并且在我现有的模板中不需要一个神奇的HTML元素,但这意味着文件必须经过一个完整的模板呈现过程,然后才能以一定的速度以理论上的损失提供,而且感觉有点不对

有更好的选择吗

其他关注事项 虽然我目前没有这个问题,但我想将来我可能也希望在静态CSS文件中使用与应用程序相关的URL

代码气味问题?
我在Google和CherryPy文档中花了一些时间试图找到这个问题的解决方案,但我没有找到任何结果。这是否表明我正在做一些奇怪的事情,并且有某种模式或最佳实践可以避免我没有遵循的问题?

我最终将我的裸JavaScript文件转换为JavaScript文件的Mako模板,并传入一个
baseurl
变量,设置为
cherrypy.url('/')
。由于应用程序的现有结构,我能够为每个渲染模板自动执行此操作,这基本上满足了我的需要

我如何使用Mako 首先,请注意,我使用的是来自的
MakoHandler
MakoLoader
类。使用这些类如下所示(为简洁起见,稍加编辑):

这样,您就可以在CherryPy中引用以下模板:

@cherrypy.expose
@cherrypy.tools.mako(filename="index.html")
def index(name=None):
    return {'username': name}
class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
    def __init__(self, template, next_handler):
        self.template = template
        self.next_handler = next_handler
    def __call__(self):
        env = globals().copy()
        env.update(self.next_handler())
        env.update({'baseurl': cherrypy.url('/')})
        try:
            return self.template.render(**env)
        except:
            cherrypy.response.status = "500"
            return exceptions.html_error_template().render()
添加一组默认的变量替换 现在,有了这段代码,您可以通过修改
MakoHandler.\uu call\uu()
传递给
self.template.render的
env
变量,向所有模板添加变量替换。考虑到这一点,我们可以将
MakoHandler
类更改为如下所示:

@cherrypy.expose
@cherrypy.tools.mako(filename="index.html")
def index(name=None):
    return {'username': name}
class MakoHandler(cherrypy.dispatch.LateParamPageHandler):
    def __init__(self, template, next_handler):
        self.template = template
        self.next_handler = next_handler
    def __call__(self):
        env = globals().copy()
        env.update(self.next_handler())
        env.update({'baseurl': cherrypy.url('/')})
        try:
            return self.template.render(**env)
        except:
            cherrypy.response.status = "500"
            return exceptions.html_error_template().render()
这样,
baseurl
变量就被设置为所有模板中应用程序的根,这正是我想要的。它甚至可以与另一个模板中的模板一起工作(见下文)

附带福利 以前,在我的HTML中,我有一个
标记,指向CherryPy提供的一个静态JS文件,浏览器发出一个单独的HTTP请求去获取该文件。但是当我开始使用Mako模板我的JavaScript以添加
baseurl
变量时,我意识到我可以直接在HTML中
它,省去了一次往返