当序列化策略更改时,GWT客户端不会收到IncompatibleRemoteServiceException

部署新版本的应用程序时,会更改模型类(例如添加/删除字段)。 运行旧版本的客户端将获得com.google.gwt.user.client.rpc.SerializationException,并使用旧客户端代码生成RPC,这是预期的行为。 因此,我们希望在客户端看到IncompatibleRemoteServiceException。 但是我们得到StatusCodeException。

StatusCodeException是500错误,我们无法轻松自定义客户端行为(我们不希望假设每个StatusCodeException或500错误是新版本)。 我们在这里做错了什么?

注意:在服务器端(日志),我们显然得到SerializationExcepion,因为来自旧客户端的序列化策略对新服务器不再有效。 那么为什么不抛出IncompatibleRemoteServiceException呢?

谢谢。

这是我面临的问题的解决方案:

首先扩展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; } 

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

私有方法sendError()基本上是来自GWT 500错误代码的自定义副本(对于丑陋的谷歌缩进感到抱歉)

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

我使用了HttpServletResponse.SC_CONFLICT (409),但你可以使用一个聪明的错误代码。 写的信息并不重要。

然后在你的自定义RpcRequestBuilder (这是客户端代码)

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

我们使用多个弹出窗口,这是Navigator类的一个原因; 当然在那里使用你自己的风格来警告用户。

编辑 :这里发生了什么?

直到GWT 1.3.3 IsSerializable是唯一可用于将类标记为GWT RPC serializabled的接口。 下一版本为了相同的目的接受了Java标准的Serializable接口,但为实现此接口的对象添加了安全策略的要求。 默认情况下,GWT使用唯一的哈希名称为每个编译生成策略。 尝试传输标记为Serializable对象的旧客户端将在服务器端抛出将在客户端作为通用运行时错误接收的序列化策略exception。 只要签名保持不变, IsSerializable允许旧客户端仍然使用新服务。 这意味着此问题的另一种解决方案是将每个对象标记为GWT RPC为IsSerializable 。 但是如果由于某种原因你需要你的对象不被引用到GWT接口,这对于旧客户端连接来说是一个很好的解决方案。

有关1.3.3后备的更多详细信息,请查看GWT指南 。