16

当 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:警告:无法获取模块“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 查看了代理和后端之间的流量,但没有什么值得注意的。

4

7 回答 7

9

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

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

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

我为我找到了这个解决方案。我扩展了类 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;
      }
}
于 2010-05-01T16:40:04.590 回答
7

米歇尔,

感谢您提供用于处理此问题的示例 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 “mucking”委托给 apache 的代理设置,这是我一直采用的方法。

对此方法的评论表示赞赏

于 2010-09-22T16:22:19.167 回答
3

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

我更喜欢前者,但后者也应该工作。如果您真的需要将事物分离到多个上下文中,请使用不同的端口号?

于 2009-10-12T12:48:32.163 回答
2

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

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

于 2009-10-04T20:52:14.943 回答
2

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() 等来概括......

于 2012-05-27T21:54:29.893 回答
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 部分,删除不必要的斜杠

于 2014-07-29T21:17:47.700 回答
0

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

于 2012-08-03T14:16:06.040 回答