Java 序列化策略更改时GWT客户端未接收到不兼容的RemoteServiceException
部署新版本的应用程序时,对模型进行了更改 类(例如添加/删除字段)。 运行旧版本的客户端获取 com.google.gwt.user.client.rpc.SerializationException与rpc 使用旧的客户端代码创建,这是预期的行为。 因此,我们希望在客户端看到不兼容的EmoteServiceException。 然而,我们得到了StatusCodeException StatusCodeException为500错误,我们无法自定义客户端 侧行为容易(我们不想假设每个 StatusCodeException或500 error是新版本)。我们能做什么 你在这里做错了什么 注意:在服务器端(日志),我们得到SerializationExcepion,因为旧客户端的序列化策略对新服务器不再有效。那么为什么不抛出不兼容的emoteServiceException呢Java 序列化策略更改时GWT客户端未接收到不兼容的RemoteServiceException,java,google-app-engine,gwt,exception-handling,Java,Google App Engine,Gwt,Exception Handling,部署新版本的应用程序时,对模型进行了更改 类(例如添加/删除字段)。 运行旧版本的客户端获取 com.google.gwt.user.client.rpc.SerializationException与rpc 使用旧的客户端代码创建,这是预期的行为。 因此,我们希望在客户端看到不兼容的EmoteServiceException。 然而,我们得到了StatusCodeException StatusCodeException为500错误,我们无法自定义客户端 侧行为容易(我们不想假设每个 Stat
谢谢。以下是我面对这个问题的解决方案: 首先扩展
RemoteServiceServlet
(您的所有服务servlet都将从此新类扩展,请记住这是服务器代码)。这是其中的相关代码:
@Override
protected SerializationPolicy doGetSerializationPolicy(
HttpServletRequest request, String moduleBaseURL, String strongName) {
SerializationPolicy sp = super.doGetSerializationPolicy(request,
moduleBaseURL, strongName);
if(sp == null) { //no policy found, probably wrong client version
throw new InvalidPolicyException();
}
return sp;
}
及
私有方法sendError()
基本上是来自GWT 500错误代码的定制副本(很抱歉谷歌缩进太难看了)
我使用了HttpServletResponse.SC_CONFLICT
(409),但您可能可以使用一个巧妙的错误代码。写下的信息并不重要
然后在自定义的RpcRequestBuilder
(这是客户端代码)
我们使用多个弹出窗口,这是Navigator类的一个原因;当然,在那里使用您自己的样式来警告用户
编辑:这里发生了什么
直到GWT1.3.3IsSerializable
是唯一一个可以将类标记为GWT RPC序列化的接口。下一个版本出于同样的目的接受了Java标准Serializable
接口,但增加了实现此接口的对象的安全策略要求。默认情况下,GWT为每个编译生成一个具有唯一哈希名称的策略。尝试传输标记为可序列化的对象的旧客户端将在服务器端引发序列化策略异常,该异常将作为一般运行时错误在客户端接收IsSerializable
允许旧客户端在签名保持不变的情况下仍使用新服务。这意味着这个问题的另一个解决方案是将通过GWT RPC的每个对象标记为可序列化的
。但是,如果出于某种原因,您需要不将对象引用到GWT接口,那么对于旧客户端连接来说,这是一个很好的解决方案
查看有关1.3.3回退的更多详细信息。虽然这在较新版本的GWT(即2.5.1)中可能不是问题,但需要注意的是,您不能仅通过从服务方法中抛出不兼容的RemoteServiceException来测试功能,因为GWT会将其包装在StatusCodeException中。但是,如果您通过实际运行与您的客户机不兼容的服务来测试它,您将看到不兼容的EmoteServiceException出现。
@Override
protected void doUnexpectedFailure(Throwable e) {
if(e instanceof InvalidPolicyException) {
sendError(); //send message to reload client (wrong client version)
return; //that's it
}
super.doUnexpectedFailure(e);
}
private void sendError() {
ServletContext servletContext = getServletContext();
HttpServletResponse response = getThreadLocalResponse();
try {
response.setContentType("text/plain");
response.setStatus(HttpServletResponse.SC_CONFLICT);
try {
response.getOutputStream().write("wrong client version".getBytes("UTF-8"));
} catch (IllegalStateException e) {
// Handle the (unexpected) case where getWriter() was previously used
response.getWriter().write("wrong client version");
}
} catch (IOException ex) {
servletContext.log(
"sendError failed while sending the previous custom failure to the client", ex);
}
}
public class CustomRequestBuilder extends RpcRequestBuilder {
private class RequestCallbackWrapper implements RequestCallback {
private RequestCallback callback;
RequestCallbackWrapper(RequestCallback aCallback) {
this.callback = aCallback;
}
@Override
public void onResponseReceived(Request request, Response response) {
if(response.getStatusCode() == 409) { //wrong client version
Navigator.closeEveryPopUp();
Navigator.showUncloseablePopUp("Login again!",
new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
//reload your $#%^ client!!!
Window.Location.reload();
}
});
} else {
//(...)irrelevant security code here(...)
callback.onResponseReceived(request, response);
}
}
@Override
public void onError(Request request, Throwable exception) {
callback.onError(request, exception);
}
}
@Override
protected void doFinish(RequestBuilder rb) {
//(...)more irrelevant security code here(...)
super.doFinish(rb);
rb.setCallback(new RequestCallbackWrapper(rb.getCallback()));
}
}