反向代理后面的 GWT 问题 - nginx 或 apache

发布于 2024-08-07 13:34:39 字数 1855 浏览 4 评论 0原文

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

当我直接点击时,GWT 应用程序工作正常:

http://host:8080/context/

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

upstream backend {
    server 127.0.0.1:8080;
}

...

location / {
   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; did you forget to include it in this deployment?
2009-10-04 14:05:41.140:/:WARN:  Login: WARNING: Failed to get the SerializationPolicy 'C7F5ECA5E3C10B453290DE47D3BE0F0E' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used.  You may experience SerializationExceptions as a result.
2009-10-04 14:05:41.292:/:WARN:  StoryService: ERROR: The serialization policy file '/0445C2D48AEF2FB8CB70C4D4A7849D88.gwt.rpc' was not found; did you forget to include it in this deployment?
2009-10-04 14:05:41.292:/:WARN:  StoryService: WARNING: Failed to get the SerializationPolicy '0445C2D48AEF2FB8CB70C4D4A7849D88' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used.  You may experience SerializationExceptions as a result.

换句话说,GWT 没有得到需要在 /context/ 前面添加的单词,然后寻找 C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc,但是仅当请求通过代理时。解决方法是将上下文添加到网站的 url:

location /context/ {
    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,但那里没有什么值得注意的。

I'm having this problem with GWT when it's behind a reverse proxy. The backend app is deployed within a context - let's call it /context.

The GWT app works fine when I hit it directly:

http://host:8080/context/

I can configure a reverse proxy in front it it. Here's my nginx example:

upstream backend {
    server 127.0.0.1:8080;
}

...

location / {
   proxy_pass        http://backend/context/;
}

But, when I run through the reverse proxy, GWT gets confused, saying:

2009-10-04 14:05:41.140:/:WARN:  Login: ERROR: The serialization policy file '/C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc' was not found; did you forget to include it in this deployment?
2009-10-04 14:05:41.140:/:WARN:  Login: WARNING: Failed to get the SerializationPolicy 'C7F5ECA5E3C10B453290DE47D3BE0F0E' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used.  You may experience SerializationExceptions as a result.
2009-10-04 14:05:41.292:/:WARN:  StoryService: ERROR: The serialization policy file '/0445C2D48AEF2FB8CB70C4D4A7849D88.gwt.rpc' was not found; did you forget to include it in this deployment?
2009-10-04 14:05:41.292:/:WARN:  StoryService: WARNING: Failed to get the SerializationPolicy '0445C2D48AEF2FB8CB70C4D4A7849D88' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used.  You may experience SerializationExceptions as a result.

In other words, GWT isn't getting the word that it needs to prepend /context/ hen look for C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc, but only when the request comes throught proxy. A workaround is to add the context to the url for the web site:

location /context/ {
    proxy_pass        http://backend/context/;
}

but that means the context is now part of the url that the user sees, and that's ugly.

Anybody know how to make GWT happy in this case?

Software versions:
GWT - 1.7.0 (same problem with 1.7.1)
Jetty - 6.1.21 (but the same problem existed under tomcat)
nginx - 0.7.62 (same problem under apache 2.x)

I've looked at the traffic between the proxy and the backend using DonsProxy, but there's nothing noteworthy there.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

短叹 2024-08-14 13:34:39

我遇到了同样的问题,我打开了错误报告:

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;
      }
}

I have the same problem, and I opened a bug report:

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

The problem is that it was marked "As Design", so I don't think it will be fixed.

I found this solution for me. I extended the class RemoteServiceServlet and I forced GWT to load serialization policy file starting from ContextName instead of URL.
Then I extended my service my class instead of RemoteServiceServlet class.
In this way the application will be unlinked from the url from where it will be called.

Here there is my custom class:

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;
      }
}
巷子口的你 2024-08-14 13:34:39

Michele,

谢谢您提供的示例 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 的代理设置,这是我一直采用的方法。

感谢对这种方法的评论

Michele,

Thank you for the example servlet to handle this problem. However when I tried to use your approach it worked in the reverse proxy environment but not in my dev mode eclipse environment.

I took an approach that would let me seamlessly move between my dev and prod environments.

As you did I overwrote RemoteServiceServlet but I only replaced following...

@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);
}

In my apache config I added...

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

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

This approach works in all scenarios and delegates the url "mucking" to apache's proxy settings which is the approach I've always taken.

Comments on this approach are appreciated

雪落纷纷 2024-08-14 13:34:39

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

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

I'm fairly sure the correct answer here is to patch the source and submit a bug report. Another option would be to run the GWT app at / on your backend.

I'd prefer the former, but the latter should work too. If you really needed things separated out into multiple contexts, use a different port number?

空袭的梦i 2024-08-14 13:34:39

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

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

I've run into a similar problem, a successful workaround was to make all serialized objects implement GWT's IsSerializable interface (in addition to the standard Serializable interface). If you read the message, it states that 'a legacy, 1.3.3 compatible, serialization policy will be used' - the 1.3.3 compatible policy requires all of your serialized objects implement the IsSerializable interface, so by adding it, everything worked.

I do have concerns that the legacy policy will be desupported in future versions of GWT, so i am also in search for a better workaround myself.

ˇ宁静的妩媚 2024-08-14 13:34:39

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

KC's answer is good. For those that do not want to muck around with apache configs, or need a quick and dirty way to test, here is a code only solution.

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);
}

The application is on http://server/bar, the proxy is serving it at http://proxy/foo/bar
Hence moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar"); makes GWT happy.
Likewise if the application is at http://server/bar and the proxy is serving at http://proxy/, you need to add bar to the moduleBaseURL (right before the package name).
This can be generalized through the use of getServletContext().getContextPath() etc...

原谅过去的我 2024-08-14 13:34:39

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

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

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

My goal was to avoid additional header(s) which would make deployment and configuration harder. I solved this problem by overriding RemoteServiceServlet.doGetSerializationPolicy():

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

In above code:
extractGwtModuleName() extracts last string prefixed and/or followed by slash
joinPaths() joins multiple url parts, removes unnecessary slashes

半寸时光 2024-08-14 13:34:39

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

Use restful JSON for your RPC calls instead of GWT-RPC.
This solves the reverse-proxy problem since no serialization files are required.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文