【问题标题】:Problem with GWT behind a reverse proxy - either nginx or apache反向代理后面的 GWT 问题 - nginx 或 apache
【发布时间】:2010-12-03 18:56:27
【问题描述】:

当 GWT 在反向代理后面时,我遇到了这个问题。后端应用程序部署在上下文中 - 我们称之为 /context。

当我直接点击 GWT 应用程序时,它运行良好:

http://host:8080/context/

我可以在它前面配置一个反向代理。这是我的 nginx 示例:

上游后端{ 服务器 127.0.0.1:8080; } ... 地点 / { proxy_pass http://backend/context/; }

但是,当我运行反向代理时,GWT 会感到困惑,说:

2009-10-04 14:05:41.140:/:WARN: Login: ERROR: the serialization policy file '/C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc' was not found;您是否忘记将其包含在此部署中? 2009-10-04 14:05:41.140:/:WARN:登录:警告:无法获取模块“https://hostname:444/”的序列化策略“C7F5ECA5E3C10B453290DE47D3BE0F0E”;将使用与 1.3.3 兼容的旧版序列化策略。因此,您可能会遇到 SerializationExceptions。 2009-10-04 14:05:41.292:/:WARN: StoryService: 错误: 未找到序列化策略文件“/0445C2D48AEF2FB8CB70C4D4A7849D88.gwt.rpc”;您是否忘记将其包含在此部署中? 2009-10-04 14:05:41.292:/:WARN: StoryService: WARNING: 无法获取模块“https://hostname:444/”的序列化策略“0445C2D48AEF2FB8CB70C4D4A7849D88”;将使用与 1.3.3 兼容的旧版序列化策略。因此,您可能会遇到 SerializationExceptions。

换句话说,GWT 没有得到它需要在 /context/ 之前查找 C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc 的词,但只有在请求通过代理到达时。一种解决方法是将上下文添加到网站的 url:

位置/上下文/ { proxy_pass http://backend/context/; }

但这意味着上下文现在是用户看到的 url 的一部分,这很丑。

有人知道在这种情况下如何让 GWT 开心吗?

软件版本:
GWT - 1.7.0(与 1.7.1 相同的问题)
Jetty - 6.1.21(但在tomcat下也存在同样的问题)
nginx - 0.7.62(在 apache 2.x 下同样的问题)

我使用DonsProxy查看了代理和后端之间的流量,但没有什么值得注意的。

【问题讨论】:

    标签: gwt reverse-proxy


    【解决方案1】:

    我的目标是避免额外的标头,这会使部署和配置更加困难。我通过覆盖RemoteServiceServlet.doGetSerializationPolicy()解决了这个问题:

    @覆盖 protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName) { 字符串 localServerAddress = "http://127.0.0.1:" + getThreadLocalRequest().getLocalPort(); String localContextPath = getServletConfig().getServletContext().getContextPath(); 字符串模块名 = extractGwtModuleName(moduleBaseURL); String localModuleBaseURL = joinPaths(localServerAddress, localContextPath, moduleName, "/"); return super.doGetSerializationPolicy(request, localModuleBaseURL, strongName); }

    在上面的代码中:
    extractGwtModuleName() 提取最后一个前缀和/或后跟斜杠的字符串
    joinPaths() 加入多个 url 部分,删除不必要的斜杠

    【讨论】:

    • 我也在尝试让 gwt 与 https 一起工作。但没有运气。谁能帮帮我吗?我使用 Spring Boot 作为带有 h​​ttps 的服务器,但是 gwt 向 http 发出请求,我得到:“无法从超级开发模式加载应用程序”
    【解决方案2】:

    为您的 RPC 调用使用 restful JSON 而不是 GWT-RPC。 这解决了反向代理问题,因为不需要序列化文件。

    【讨论】:

      【解决方案3】:

      KC 的回答很好。对于那些不想乱用 apache 配置,或者需要一种快速而肮脏的测试方式的人,这里是一个纯代码解决方案。

      protected SerializationPolicy doGetSerializationPolicy(final HttpServletRequest request, String moduleBaseURL, final String strongName) {
          final String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
          if (moduleBaseURLHdr != null) {
              moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar");
          }
          return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
      }
      

      应用程序位于http://server/bar,代理正在为http://proxy/foo/bar 提供服务 因此 moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar");让 GWT 高兴。 同样,如果应用程序位于 http://server/bar 并且代理在 http://proxy/ 提供服务,则需要将 bar 添加到 moduleBaseURL(就在包名称之前)。 这可以通过使用 getServletContext().getContextPath() 等来概括...

      【讨论】:

      • 不错。你还记得这适用于哪个 GWT 版本吗? (2009 年的问题现在可能仍然适用,也可能不适用,但我假设您使用的是更新的版本?)
      【解决方案4】:

      米歇尔,

      感谢您提供用于处理此问题的示例 servlet。但是,当我尝试使用您的方法时,它在反向代理环境中有效,但在我的开发模式 eclipse 环境中无效。

      我采用了一种方法,可以让我在开发环境和生产环境之间无缝切换。

      和你一样,我覆盖了 RemoteServiceServlet 但我只替换了以下...

      @Override
      protected SerializationPolicy doGetSerializationPolicy(
              HttpServletRequest request, String moduleBaseURL, String strongName) {
          //get the base url from the header instead of the body this way 
          //apache reverse proxy with rewrite on the header can work
          String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
      
          if(moduleBaseURLHdr != null){
              moduleBaseURL = moduleBaseURLHdr;
          }
      
          return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
      }
      

      在我的 apache 配置中我添加了...

      ProxyPass /app/ ajp://localhost:8009/App-0.0.1-SNAPSHOT/
      

      RequestHeader edit X-GWT-Module-Base ^(.*)/app/(.*)$ $1/App-0.0.1-SNAPSHOT/$2
      

      这种方法适用于所有场景,并将“弄脏”的 url 委托给 apache 的代理设置,这是我一直采用的方法。

      感谢您对此方法的评论

      【讨论】:

      • 拜托,你能发布整个 Apache 配置吗?我正在尝试这个,但没有工作,也许我错过了一些东西。
      • 非常优雅和不错的解决方案,我确认这种方法也适用于 gwt 2.6.0,来自我的 +1。
      【解决方案5】:

      我有同样的问题,我打开了一个错误报告:

      http://code.google.com/p/google-web-toolkit/issues/detail?id=4817

      问题是它被标记为“As Design”,所以我认为它不会被修复。

      我为我找到了这个解决方案。我扩展了类 RemoteServiceServlet 并强制 GWT 从 ContextName 而不是 URL 开始加载序列化策略文件。 然后我将我的服务扩展到我的类而不是 RemoteServiceServlet 类。 这样,应用程序将与调用它的 url 断开链接。

      这是我的自定义类:

      import java.io.IOException;
      import java.io.InputStream;
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.text.ParseException;
      
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      
      import com.google.gwt.user.server.rpc.RemoteServiceServlet;
      import com.google.gwt.user.server.rpc.SerializationPolicy;
      import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
      
      public class MyRemoteServiceServlet extends RemoteServiceServlet
      {
          @Override
          protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName)
          {
              return MyRemoteServiceServlet.loadSerializationPolicy(this, request, moduleBaseURL, strongName);
          }
      
      
          /**
            * Used by HybridServiceServlet.
            */
            static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
            HttpServletRequest request, String moduleBaseURL, String strongName) {
          // The serialization policy path depends only by contraxt path
          String contextPath = request.getContextPath();
      
          SerializationPolicy serializationPolicy = null;
      
      
          String contextRelativePath = contextPath + "/";
      
      
      
            String serializationPolicyFilePath = SerializationPolicyLoader.getSerializationPolicyFileName(contextRelativePath
                + strongName);
      
            // Open the RPC resource file and read its contents.
            InputStream is = servlet.getServletContext().getResourceAsStream(
                serializationPolicyFilePath);
            try {
              if (is != null) {
                try {
              serializationPolicy = SerializationPolicyLoader.loadFromStream(is,
                  null);
                } catch (ParseException e) {
              servlet.log("ERROR: Failed to parse the policy file '"
                  + serializationPolicyFilePath + "'", e);
                } catch (IOException e) {
              servlet.log("ERROR: Could not read the policy file '"
                  + serializationPolicyFilePath + "'", e);
                }
              } else {
                String message = "ERROR: The serialization policy file '"
                + serializationPolicyFilePath
                + "' was not found; did you forget to include it in this deployment?";
                servlet.log(message);
              }
            } finally {
              if (is != null) {
                try {
              is.close();
                } catch (IOException e) {
              // Ignore this error
                }
              }
            }
      
          return serializationPolicy;
            }
      }
      

      【讨论】:

      • 我喜欢这个解决方案,但和 KC Berg 一样,它只适用于生产环境。我的最终解决方案位于gist.github.com/476175,使用此代码但在调用loadSerializationPolicy() 之前尝试正常加载文件。
      【解决方案6】:

      我很确定这里的正确答案是修补源并提交错误报告。另一种选择是在您的后端以/ 运行 GWT 应用程序。

      我更喜欢前者,但后者也应该可以。如果您确实需要将内容分离到多个上下文中,请使用不同的端口号?

      【讨论】:

      • 我不一定需要在短期内分离出一些东西——但应用程序构建器默认会使用上下文设置模块,我可能希望将某些部分分离到其他模块中。修补源(到 GWT)听起来是正确的答案,因为似乎一切都配置正确。
      • 在我看来,你有一个混乱的问题,其他人可能会从你的修复中受益,所以补丁将非常有价值。如果您确实走这条路,请务必将您的补丁文件放在 Gist (gist.github.com) 或类似网站上,并将此问题链接到它,以防补丁未被立即接受。
      【解决方案7】:

      我遇到了类似的问题,一个成功的解决方法是让所有序列化对象实现 GWT 的 IsSerializable 接口(除了标准的 Serializable 接口)。如果您阅读该消息,它会声明“将使用与 1.3.3 兼容的遗留序列化策略” - 1.3.3 兼容策略要求您的所有序列化对象都实现 IsSerializable 接口,因此通过添加它,一切正常。

      我确实担心 GWT 的未来版本将不再支持旧版政策,因此我自己也在寻找更好的解决方法。

      【讨论】:

      • 有趣。 IsSerializable接口和反向代理有什么联系?
      • 在您的示例中,反向代理导致找不到序列化策略文件 (C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc)。正因为如此,它正在恢复到1.3.3兼容的序列化策略,它不需要这样的策略文件,而是需要序列化对象来实现IsSerializable接口
      • 我同意代理正在做某事。我认为这与使用的 java 接口无关。问题是,代理在做什么让 GWT 感到困惑?标头看起来相同,URL 被正确翻译,等等。对 C7F%... 文件的请求实际上从未通过代理,因为它都是在服务器端处理的,基于我在有和没有代理的情况下在线上看到的内容。
      猜你喜欢
      • 1970-01-01
      • 2021-05-21
      • 1970-01-01
      • 2020-05-20
      • 2016-02-12
      • 1970-01-01
      • 2014-10-27
      • 1970-01-01
      相关资源
      最近更新 更多