2016-10-10 4 views
2

У меня есть требование: Base64 декодировать каждую полезную нагрузку запроса JSON, которую получает моя служба загрузки Spring. Полезная нагрузка JSON была бы Base64, закодированная на клиенте перед публикацией с использованием метода HTTP POST. Кроме того, мне также необходимо, чтобы Base64 кодировал ответ JSON перед представлением вызывающему клиентскому приложению.Невозможно перехватить и обработать 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 Класс, использованный выше:

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 Класс, где зарегистрирован Перехватчик:

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

} 

Контроллер класса, где используется перехватчик (Здесь отображается только рабочая область запроса):

@Autowired 
HttpServletRequest request; 

String decodedRequest = null; 

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

Код в коде postHandle не работает. Это либо HttpServletResponse является null или я получаю сообщение об исключении:

getOutputStream уже под названием

Update: Работа вокруг решения читать ответ непосредственно в 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

@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

В качестве Spring MVC documentation состояний:

postHandle метод HandlerInterceptor не всегда идеально подходит для использования с @ResponseBody и ResponseEntity методами. В таких случаях в 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, например, для Interceptor для этапа запроса? Я быстро проверю это, как только смогу, и вернусь к результатам. Благодарю. – sage

+0

Нет необходимости в регистрации, если это сканируется –

+0

Привет, Али, я сделал это, и он будет работать. Я смог переопределить ожидаемый ответ HTTP, отправив объект String, который был напечатан в почтальоне в качестве ответа. Моя задача теперь - лучший способ получить доступ к моей полезной нагрузке ответа как строку, так что я могу кодировать base64, прежде чем представлять ее клиенту. response.getBody() возвращает только OutputStream. Есть ли у вас фрагмент для извлечения тела полезной нагрузки из ServerHttpResponse? – sage

Смежные вопросы