2016-10-10 14 views
2

私は、Springブートサービスが受け取るすべてのJSON要求ペイロードをデコードするために、Base64を要求しています。 HTTP POSTメソッドを使用して転記する前に、クライアントでJSONペイロードがBase64にエンコードされている可能性があります。さらに、私はBase64を呼び出すクライアントアプリケーションに提示する前にJSON応答をエンコードする必要があります。SpringブートでHttpServletResponseをインターセプトして操作できません

ハンドラインターセプタを使用して定型コードを減らす必要があります。 私はすでにインターセプタを使用して操作の要求/受信レッグを達成しましたが、レスポンスレッグのためにこれをまだ達成していません。 以下にコードスニペットを掲載しました。レスポンスを代行受信してbase64でエンコードするコードは、インターセプタクラスのpostHandleメソッドにあります。

私はここで間違っていますか?

インターセプタクラス:上記で使用

public class Base64ResponseEncodingInterceptor implements HandlerInterceptor { 
    private static final String DECODED_REQUEST_ATTRIB = "decodedRequest"; 
    private static final String POST = "POST"; 


    @Override 
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception { 
      try { 
       if (POST.equalsIgnoreCase(request.getMethod())) { 
        CharResponseWrapper res = new CharResponseWrapper(response); 
        res.getWriter(); 
        byte[] encoded = Base64.encodeBase64(res.toString().getBytes()); 
        byte[] encoded = Base64.encodeBase64(response.getHeader(ENCODED_RESPONSE_ATTRIB).getBytes()); 
        response.getWriter().write(new String(encoded)); 
       } 
      } catch (Exception e) { 
       throw new Exception(e.getMessage()); 
      } 
    } 


    // preHandle and afterCompletion methods 
    // Omitted 
} 

CharResponseWrapperクラス:インターセプタが使用され

@Configuration 
@EnableJpaRepositories(repositoryBaseClass = BaseRepositoryBean.class, basePackages = "") 
@EntityScan(basePackages = { "com.companyname", "com.companyname.productname"}) 
public class RestConfig extends WebMvcConfigurerAdapter { 


     @Override 
     public void addInterceptors(InterceptorRegistry registry) { 
     registry.addInterceptor(new Base64ResponseEncodingInterceptor()); 
     } 

} 

ザ・コントローラクラス、:インターセプタが登録されて

public class CharResponseWrapper extends HttpServletResponseWrapper { 

    protected CharArrayWriter charWriter; 

    protected PrintWriter writer; 

    protected boolean getOutputStreamCalled; 

    protected boolean getWriterCalled; 


    public CharResponseWrapper(HttpServletResponse response) { 
     super(response); 
     charWriter = new CharArrayWriter(); 
    } 


    @Override 
    public ServletOutputStream getOutputStream() throws IOException { 
     if (getWriterCalled) { 
      throw new IllegalStateException("getWriter already called"); 
     } 
     getOutputStreamCalled = true; 
     return super.getOutputStream(); 
    } 


    @Override 
    public PrintWriter getWriter() throws IOException { 
     if (writer != null) { 
      return writer; 
     } 
     if (getOutputStreamCalled) { 
      throw new IllegalStateException("getOutputStream already called"); 
     } 
     getWriterCalled = true; 
     writer = new PrintWriter(charWriter); 
     return writer; 
    } 


    @Override 
    public String toString() { 
     String s = null; 
     if (writer != null) { 
      s = charWriter.toString(); 
     } 
     return s; 
    } 
} 

JavaConfigクラス(ここでは作業要求レッグのみ表示しています):

@Autowired 
HttpServletRequest request; 

String decodedRequest = null; 

@ModelAttribute("decodedRequest") 
public void getDecodedParam(){ 
    decodedRequest = (String) request.getAttribute("decodedRequest"); 
} 

postHandleメソッドのコードは機能しません。それはどちらかHttpServletResponsenullであるか、私は例外メッセージを取得:

のgetOutputStreamすでに

アップデートと呼ばれる:コントローラクラスでResponseBodyAdvice に直接応答を読み込むにソリューション回避策、私は以下を追加しました:

@RestController 
@RequestMapping("/api/ipmanager") 
public class IPProfileRestController extends AbstractRestController { 

    @Autowired 
    HttpServletResponse response; 

    String encodedResponse = null; 

    @ModelAttribute("encodedResponse") 
    public void getEncodedResponse(){ 
     response.setHeader("encodedResponse", StringUtils.EMPTY); 
    } 

    @RequestMapping(value = "/time", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE }, consumes = { 
      MediaType.APPLICATION_JSON_VALUE }) 
    public @ResponseBody String saveAccessClientTime(@RequestBody String ecodedRequest) { 

     // Some code here 

     String controllerResponse = prettyJson(iPProfileResponse); 
     response.setHeader("encodedResponse", controllerResponse); 
     return controllerResponse; 
    } 
} 

私はResponseBodyAdvice Spring MVC documentation状態として

@ControllerAdvice 
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> { 

    @Override 
    public boolean supports(MethodParameter returnType, 
          Class<? extends HttpMessageConverter<?>> converterType) { 
     return true; 
    } 

    @Override 
    public Object beforeBodyWrite(Object body, 
            MethodParameter returnType, 
            MediaType selectedContentType, 
            Class<? extends HttpMessageConverter<?>> converterType, 
            ServerHttpRequest request, 
            ServerHttpResponse response) { 

     String body1 = StringUtils.EMPTY; 
     // Encode the response and return 

     List<String> listOfHeaderValues = response.getHeaders().get("encodedResponse"); 

     body1=new String(Base64.encodeBase64(listOfHeaderValues.get(0).getBytes())); 

     return body1; 
    } 

} 

答えて

0

HandlerInterceptorpostHandleメソッドは常に@ResponseBodyResponseEntity方法で使用するための理想的 適していません。 の場合HttpMessageConverterは に応答して書き込みを行い、postHandleが呼び出される前に応答を変更することが不可能になります。たとえば、ヘッダーを追加します。代わりに、アプリケーションは ResponseBodyAdviceを実装し、それを @ControllerAdvice beanとして宣言するか、直接 RequestMappingHandlerAdapterに設定することができます。そのされた状態で

は言った:

は、私がここで間違って何をしているのですか?

応答はすでにコミットされているため、変更することはできません。応答を変更するには、ResponseBodyAdvice<T>を登録し、応答エンコーディングロジックをそこに配置する必要があります。

@ControllerAdvice 
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> { 
    @Override 
    public boolean supports(MethodParameter returnType, 
          Class<? extends HttpMessageConverter<?>> converterType) { 
     return true; 
    } 

    @Override 
    public Object beforeBodyWrite(Object body, 
            MethodParameter returnType, 
            MediaType selectedContentType, 
            Class<? extends HttpMessageConverter<?>> converterType, 
            ServerHttpRequest request, 
            ServerHttpResponse response) { 

     // Encode the response and return 
    } 
} 
+0

ありがとう、アリ。 @ControllerAdviceで注釈を付けると、リクエストレグのインターセプタのようにJavaConfigクラスに登録する必要がありますか?できるだけ早くこれをテストし、結果を元に戻します。ありがとう。 – sage

+0

登録は必要ありません。スキャン可能な限り –

+0

こんにちはアリですが、これを入れてしまえばうまくいきます。郵便配達員にプリントアウトされたStringオブジェクトをレスポンスとして送信することで、期待されるhttpレスポンスを無効にすることができました。私の挑戦は今、文字列として自分のレスポンスペイロードにアクセスする最善の方法です。そのため、クライアントに提示する前にbase64でエンコードすることができます。 response.getBody()は、OutputStreamのみを返します。 ServerHttpResponseからペイロード本文を取得するためのスニペットはありますか? – sage

関連する問題