2017-05-02 8 views
2

GWTのRPCでは、パラメータはClientSerializationStreamWriterを使用してクライアント上でシリアル化され、ServerSerializationStreamReaderを使用してサーバ上で逆シリアル化され、一方で戻り値はServerSerializationStreamWriterを使用してサーバ上でシリアル化され、 ClientserializationStreamReader。GWTのRPCシリアル化メカニズムを拡張するには?

私は、シリアル化されたパラメータ(client-> server)または戻り値(server-> client)に基づいて、シリアル化ストリームにチェックサムまたは同様のものを送信側で計算しようとしています。受信側では、シリアル化されて受信されたパラメータまたは戻り値からチェックサムが再計算され(受信チェックサムなしで)、受信されたチェックサムと比較されます。

具体的なパラメータ/戻り値の型からチェックサムの計算を独立させるために、元の型ではなく、シリアル化された値に基づいて計算します。これにより、オブジェクトグラフの追加トラバーサルが不要になり、JavaのリフレクションAPI(クライアントでは使用できません)に頼ることなくオブジェクトグラフのすべての値を考慮することで、別の問題を解決できます。

最初にパラメータオブジェクト(特定のRPCの実際のパラメータで構成されています)と戻り値ラッパーを導入し、それらにカスタムフィールドシリアライザを導入することを考えました。シリアル化では、最初にパラメータ/戻り値をシリアル化し、* SerializationStreamWriterからシリアル化された値にアクセスし、チェックサムを計算し、同じ* SerializationStreamReaderを使用してチェックサムを書き込むことができます。しかし、受信側では、最初のカスタムフィールドシリアライザが呼び出されたときに、シリアライズストリームが既にトークン化されているため、* SerializationStreamReaderを実際に読み取らずにシリアライズされた値にアクセスできないようです。

そこで質問です:

  1. は、私が見逃しているフックはありますか?または、
  2. クライアントやサーバーの両方でオブジェクトグラフを同じ方法(同じ形式)でシリアル化し、具体的なパラメータや戻り値の型に固有のものを実装する必要はありません。
+0

どのようにサーバー側でサーブレットフィルタを使用して、カスタム[ 'RpcRequestBuilder'](約http://www.gwtproject.org/javadoc/latest/com:GWTのRPCServletUtilsは、いくつかの有用な方法のhereforeを持っています/google/gwt/user/client/rpc/RpcRequestBuilder.html)をクライアントサイドに追加しますか? –

答えて

0

実際、Thomas(質問に追加されたコメントを参照)は質問に答えました。 @トーマス:ありがとう、それは私の前の研究で逃したものでした。

いくつかの詳細を追加できます。

非同期サービス・インターフェース上で設定する必要がRpcRequestBuilderカスタムクライアント側では、トーマスが提案したよう:

IMyServiceAsync service = GWT.create(IMyService.class); 
((ServiceDefTarget)service).setRpcRequestBuilder(new MyCustomRpcRequestBuilder()); 

送信されようとしている要求を操作するためdoSetRequestDataをオーバーライドする必要がRpcRequestBuilderカスタムそしてdoSetCallback、それが処理される前に、応答にその手を得るために:サーバー側で

private final class MyCustomRpcRequestBuilder extends RpcRequestBuilder { 
    @Override 
    protected void doSetRequestData(RequestBuilder rb, String payload) { 
    // do whatever you need to do on the payload, request header, ... 
    String checksum = calculateChecksum(payload); 
    rb.setHeader(MY_CUSTOM_HEADER_NAME, checksum); 
    super.doSetRequestData(rb, payload); 
    } 

    @Override 
    protected void doSetCallback(RequestBuilder rb, final RequestCallback callback) { 
    super.doSetCallback(rb, new RequestCallback() { 
     @Override 
     public void onResponseReceived(Request request, Response response) { 
     // do whatever you need to do on the response header, ... 
     String receivedChecksum = response.getHeader(MY_CUSTOM_HEADER_NAME); 
     if (receivedChecksum.equals(calculateChecksum(response.getText())) { 
      callback.onResponseReceived(request, response); 
     } 
     else { 
      callback.onError(request, new InvalidChecksumException(...)); 
     } 
     } 

     @Override 
     public void onError(Request request, Throwable exception) { 
     callback.onError(request, exception); 
     } 
    }); 
    } 
} 

をサーブレットフィルタを使用することができます。

public class MyFilter implements Filter { 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
    } 

    @Override 
    public void destroy() { 
    } 

    @Override 
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException { 
    HttpServletRequest httpReq = (HttpServletRequest)req; 
    HttpServletResponse httpResp = (HttpServletResponse)resp; 

    String receivedChecksum = httpReq.getHeader(MY_CUSTOM_HEADER_NAME); 
    String reqPayload = RPCServletUtils.readContentAsGwtRpc(httpReq); 
    if (receivedChecksum.equals(calculateChecksum(reqPayload)) { 
     final ServletInputStream inputStream = new ByteArrayServletInputStream(reqPayload.getBytes(RPCServletUtils.CHARSET_UTF8)); 
     HttpServletRequestWrapper reqWrapper = new HttpServletRequestWrapper(httpReq) { 
     @Override 
     public ServletInputStream getInputStream() throws IOException { 
      return inputStream; 
     } 
     }; 
     final CachingServletOutputStream outputStream = new CachingServletOutputStream(); 
     HttpServletResponseWrapper respWrapper = new HttpServletResponseWrapper(httpResp) { 
     @Override 
     public ServletOutputStream getOutputStream() throws IOException { 
      return outputStream; 
     } 
     }; 

     filterChain.doFilter(reqWrapper, respWrapper); 

     // possibly you also need to handle gzipped payload 
     String respPayload = new String(outputStream.getCachedDataAsBytes(), RPCServletUtils.CHARSET_UTF8); 
     String checksum = calculateChecksum(respPayload); 
     httpResp.setHeader(MY_CUSTOM_HEADER_NAME, checksum); 
     httpResp.getOutputStream().write(outputStream.getCachedDataAsBytes()); 
    } else { 
     httpResp.setContentType("text/plain"); 
     httpResp.setStatus(HttpServletResponse.SC_BAD_REQUEST); 
     httpResp.getOutputStream().write("Invalid checksum".getBytes(RPCServletUtils.CHARSET_UTF8)); 
    } 
    } 

    static class ByteArrayServletInputStream extends ServletInputStream { 
    private final InputStream delegate; 

    ByteArrayServletInputStream(byte[] data) throws IOException, ServletException { 
     delegate = new ByteArrayInputStream(data); 
    } 

    @Override 
    public int read() throws IOException { 
     return delegate.read(); 
    } 
    } 

    static class CachingServletOutputStream extends ServletOutputStream { 
    private final ByteArrayOutputStream cache; 

    CachingServletOutputStream() { 
     this.cache = new ByteArrayOutputStream(4096); 
    } 

    @Override 
    public void write(int b) throws IOException { 
     cache.write(b); 
    } 

    @Override 
    public void flush() throws IOException { 
     cache.flush(); 
    } 

    byte[] getCachedDataAsBytes() { 
     return cache.toByteArray(); 
    } 
    } 
} 
関連する問題