代码之家  ›  专栏  ›  技术社区  ›  friedo

构建位于反向代理后面的应用程序时处理URI的策略

  •  4
  • friedo  · 技术社区  · 15 年前

    我正在用一个独立的HTTP服务器构建一个应用程序,该服务器可以直接访问,也可以放在反向代理(如Apache)后面 mod_proxy )

    所以,假设我的应用程序在端口8080上运行,而您的Apache设置如下:

    ProxyPass /myapp http://localhost:8080
    ProxyPassReverse /myapp http://localhost:8080
    

    这将导致HTTP请求进入主Apache服务器,然后转到 /myapp/* 代理到我的应用程序。如果一个请求像 GET /myapp/bar ,我的申请将看到 GET /bar . 这是应该的。

    出现的问题是生成必须从应用程序的URI空间转换的URI,以便通过代理正确工作(即预处理 /myapp/ )

    这个 ProxyPassReverse 指令负责处理HTTP头(重定向等)中的URI,但它不处理由我的应用程序生成的HTML或静态文件和模板中的URI。

    我知道像这样的过滤器 mod_proxy_html ,但这是一个非标准的Apache模块,在任何情况下,此类筛选器都不能用于其他能够充当反向代理的前端Web服务器。

    所以我想出了一些可能的策略:

    1. 需要在包含代理路径的某个位置设置一个环境变量,并将其预先发送给所有生成的URI。这看起来不雅;它破坏了反向代理提供的封装。

    2. 将代理路径放入应用程序的配置文件中。与上述反对意见相同。

    3. 在我的应用程序中只使用相对URI。这可能会变得有些棘手;我必须计算当前资源和链接所处位置之间的路径差异,并添加适当数量的 ../ 'es。似乎很乱。另一个问题是,有些东西必须生成绝对的URI,比如RSS提要和生成的电子邮件。

    4. 在前端使用一些hacky javascript在文档文本中咀嚼uris。从互操作性的角度来看,这似乎是一个非常可怕的想法。

    5. 在我的代码中使用一个单独的URI生成函数,并要求“静态”文件(如javascript、css等)通过我的模板系统运行。这就是我现在倾向的想法。

    这肯定是一个相当普遍的问题。你过去是怎么处理的?什么起作用了,什么使事情变得更困难了?

    1 回复  |  直到 15 年前
        1
  •  7
  •   Justin Grant    15 年前

    是的,常见问题。如何解决这一问题取决于您所拥有的应用程序类型以及您所使用的服务器平台和Web框架。但我有一种处理这些问题的一般方法,到目前为止效果相当好。

    我的首选是在应用程序代码中处理类似这样的问题,而不是依靠mod_proxy_html之类的Web服务器模块来处理,因为通常有太多的特殊情况(例如客户端javascript动态组装URL),而服务器模块无法捕获这些特殊情况。也就是说,在一些情况下,我采用了服务器模块的方法,但我决定自己修改模块代码来处理角落的情况。还要记住perormance;在代码生成时修复URL通常比将整个HTML推送到另一个服务器模块更快。

    以下是我在代码中如何处理此问题的建议:

    首先,您需要确定要生成哪种URL。我的首选是相对URL。上面的“添加适当数量的../”是混乱的,但至少这是您(程序员)的混乱。如果使用config file/environment变量方法,那么您将依赖于部署您的应用程序的人(例如,一个报酬低且脾气暴躁的IT运营工程师)来始终正确地设置这些内容。它还使代码的发布复杂化,即使您自己进行部署,因为您不能简单地将开发文件复制到生产环境中,而是需要添加每个部署环境的自定义步骤。我过去发现消除潜在的部署问题是值得的 许多 先发制人的编码。

    接下来,您需要将这些URL输入到代码中。根据内容/代码的类型,您如何执行此操作会有所不同:

    对于服务器端代码(如php、ror等),您需要确保在代码中尽可能少的地方生成服务器端URL(理想情况下,一个方法!).如果您正在使用任何主流的MVC Web框架(例如ror、django等),这应该是微不足道的,因为使用MVC框架的URL生成通常已经通过一个可以重写的代码路径。如果您不使用这些框架中的任何一个,那么您的代码中很可能会有大量的URL生成。但是,您希望采用的方法是通过代码生成所有URL,然后重写该方法以支持将非相对URL转换为相对URL。您通常可以在代码中搜索模式(例如 "/ , '/ , "http:// , 'http:// )然后手动搜索并替换(或者如果你真的很书呆子并且比我有更多的耐心,那么可以制作一个regex来替换源代码中的每个常见情况)。

    使这项工作可靠的关键是,您可以将绝对URL保留在适当的位置,并通过调用“relativizer”方法简单地将它们包装起来,而不是用服务器端代码中的相对URL手动替换所有绝对URL(即使每个URL都正确,但如果文件被移动,则很脆弱)。这是更可靠和不可抗拒的。

    对于JavaScript,我通常喜欢做与服务器代码相同的事情——将所有URL生成移动到一个方法中,并确保任何URL生成都调用此方法。在一个拥有大量现有javascript的应用程序上,这可能很难做到,但是上面的搜索和替换方法在JS中似乎也能很好地工作。

    对于CSS,CSS中的URL与CSS文件的位置(而不是调用的HTML页)相关,因此使用相对URL通常很容易。只需将你的CSS放在一个文件夹中,或者把图像放在它下面更深的文件夹中,或者把图像放在与CSS平行的文件夹中,然后使用一个../相对地获取图像。一般来说,这是一个很好的最佳实践——如果您还没有在CSS中做相对的URL,那么您应该考虑这样做,而不考虑反向代理。

    最后,您需要弄清楚如何处理其他奇怪的静态文件(比如传统的静态HTML文件有时会潜入其中)。一般来说,我建议使用与CSS和图像相同的做法——理想情况下,您可以将静态文件放在可预测的目录中,并依赖于相对的URL。或者(取决于您的服务器平台),重新映射这些静态文件的文件扩展名以便由Web框架处理,然后为所有URL运行服务器端URL生成器可能会更容易。或者,除此之外,您还可以将文件保留在适当的位置,并手动修复相对的URL,因为您知道这是脆弱的。

    在一个完整的循环中,有时会有太多的地方生成URL,使用mod_proxy_html这样的服务器模块更有效。但我认为这是最后的解决办法——特别是如果您不愿意在需要时编辑源代码的话。

    顺便说一句,我意识到我没有提到你上面的想法(javascript链接修正)。我不会这样做——如果用户关闭了javascript或者(更常见的)一些网络问题在页面加载后的一段时间内阻止了该javascript,那么您的链接将无法工作。太冒险了。