Проблема с GWT за обратным прокси - либо nginx, либо apache
У меня проблема с GWT, когда она находится за обратным прокси. Бэкэнд-приложение развернуто в контексте - позвоните ему/контексту.
Приложение 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/hen искать 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, но там ничего не примечательно.
Ответы
Ответ 1
Я уверен, что правильный ответ здесь - исправить источник и отправить отчет об ошибке. Другой вариант - запустить приложение GWT с /
на вашем сервере.
Я бы предпочел первый, но последний тоже должен работать. Если вам действительно нужны вещи, разделенные на несколько контекстов, используйте другой номер порта?
Ответ 2
У меня такая же проблема, и я открыл отчет об ошибке:
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;
}
}
Ответ 3
Мишель,
Благодарим вас за пример сервлета для решения этой проблемы. Однако, когда я пытался использовать ваш подход, он работал в обратной прокси-среде, но не в моей среде eclipse в режиме dev.
Я сделал подход, который позволил бы мне легко перемещаться между средами dev и prod.
Как и я, я перезаписал 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, который является тем подходом, который я всегда делал.
Комментарии к этому подходу оценены
Ответ 4
У меня возникла аналогичная проблема, успешным решением было сделать все сериализованные объекты реализованным интерфейсом GWT IsSerializable (в дополнение к стандартному интерфейсу Serializable). Если вы прочтете сообщение, в нем говорится, что "будет использоваться политик последовательной версии 1.3.3, совместимый с сериализацией". 1.3.3 совместимая политика требует, чтобы все ваши сериализованные объекты реализовали интерфейс IsSerializable, поэтому, добавив его, все сработало.
У меня есть опасения, что прежняя политика будет описана в будущих версиях GWT, поэтому я также ищу лучший способ обхода.
Ответ 5
Ответ 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
Следовательно, модульBaseURL = moduleBaseURLHdr.replace( "foo/bar", "bar" ); делает GWT счастливым.
Аналогично, если приложение находится в http://server/bar
, а прокси-сервер работает в http://proxy/
, вам нужно добавить bar к модулюBaseURL (прямо перед именем пакета).
Это можно обобщить с помощью getServletContext(). GetContextPath() и т.д.
Ответ 6
Моя цель состояла в том, чтобы избежать дополнительных заголовков, которые затрудняли бы развертывание и настройку. Я решил эту проблему, переопределив 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, удаляет ненужные косые черты
Ответ 7
Используйте надежный JSON для вызовов RPC вместо GWT-RPC.
Это решает проблему обратного прокси-сервера, поскольку файлы сериализации не требуются.