복붙노트

[SPRING] RestTemplate response.getBody는 put 및 post 요청에 대해 4 ** 및 5 ** 오류에 대한 예외를 throw하지만 get 요청에 대해서는 정상적으로 작동합니다.

SPRING

RestTemplate response.getBody는 put 및 post 요청에 대해 4 ** 및 5 ** 오류에 대한 예외를 throw하지만 get 요청에 대해서는 정상적으로 작동합니다.

모든 요청 - 응답을 가로 채고 기록하려고합니다. 요청을하려면 RestTemplate.exchange ()를 사용하고 있습니다.

내가 GET 요청을하고 4 ** 오류가 발생하면 ClientHttpResponse.getBody ()를 호출하고 응답 본문에 액세스 할 수 있지만 PUT 및 POST 요청의 경우 ClientHttpResponse.getBody () 메서드는 예외를 throw합니다.

무엇이 원인이 될 수 있으며 POST 및 PUT 요청에 대한 응답 본문을 어떻게 얻을 수 있습니까?

이것이 내가 요청하는 곳입니다.

apiResponse = restTemplate.exchange(url, vCloudRequest.getHttpMethod(), entity, responseType);

이것은 인터셉터가 예외를 얻는 부분입니다.

@Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        ClientHttpResponse response = execution.execute(request, body);

        String requestString = new String(body);

        String responseString = new 
// Below line throws exception
String(ByteStreams.toByteArray(response.getBody()), Charset.forName("UTF-8"));

이것이 스택입니다.

Caused by: java.io.IOException: Server returned HTTP response code: 403 for URL: https://176.235.57.11/api/admin/org/bd154aaf-2e7c-446d-91be-f0a45138476b/users
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1876)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
    at org.springframework.http.client.SimpleClientHttpResponse.getBody(SimpleClientHttpResponse.java:85)
    at org.springframework.http.client.BufferingClientHttpResponseWrapper.getBody(BufferingClientHttpResponseWrapper.java:69)
    at roma.api_utils.model.Interceptors.RequestLoggingInterceptor.intercept(RequestLoggingInterceptor.java:39)
    at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86)
    at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:70)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)

업데이트 :

response.getBody ()를 호출하기 전에 response.getStatusCode ()를 호출하면 IOException이 발생하지 않습니다.

해결법

  1. ==============================

    1.기본 지식:

    기본 지식:

    HttpURLConnection에는 두 개의 유사한 필드 인 errorStream과 inputStream이 있습니다. getInputSteam 메소드를 호출하면 응답에 오류 코드가 있는지 확인합니다. 따라서 IOException을 throw하고 기록하므로 예외가 발생했습니다. inputStream에있는 내용을 errorStream에 복사하십시오. 따라서 getClientHttpResponse가 getBody 메소드로 수행하는 getErrorStream 메소드를 호출하여 응답 본문을 가져올 수 있습니다.

        @Override
        public InputStream getBody() throws IOException {
            InputStream errorStream = this.connection.getErrorStream();
            this.responseStream = 
    (errorStream != null ? errorStream : this.connection.getInputStream());
            return this.responseStream;
        }
    

    먼저 errorStream이 null인지 확인하고 false이면 반환하고 true이면 connection.getInputStream ()을 호출하여 반환합니다.

    이제 대답은 여기에 있습니다.

    .

    @Override
    protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) 
    throws IOException {
        addHeaders(this.connection, headers);
        // JDK <1.8 doesn't support getOutputStream with HTTP DELETE
        if (HttpMethod.DELETE == getMethod() && bufferedOutput.length == 0) {
            this.connection.setDoOutput(false);
        }
        if (this.connection.getDoOutput() && this.outputStreaming) {
            this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
        }
        this.connection.connect();
        if (this.connection.getDoOutput()) {
            FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
        }
        else {
            // Immediately trigger the request in a no-output scenario as well
            this.connection.getResponseCode();
        }
        return new SimpleClientHttpResponse(this.connection);
    }
    

    열심히 this.connection.getResponseCode (); http 메서드가 GET 인 경우

  2. ==============================

    2.나는 모든 요구와 응답을 로깅하는 비슷한 요구 사항을 가지고 있었다. 필자는 필터를 작성하고 필터 체인에 연결했습니다.

    나는 모든 요구와 응답을 로깅하는 비슷한 요구 사항을 가지고 있었다. 필자는 필터를 작성하고 필터 체인에 연결했습니다.

    코드는 다음과 같습니다.

    public class CustomRequestFilter implements Filter {
    
    
      @Override
      public void init(FilterConfig filterConfig) throws ServletException {
        //No custom initialisation required
      }
    
      @Override
      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
          FilterChain filterChain) throws IOException, ServletException {
        try {
    
          HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
          HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
    
            Map<String, String> requestMap = this
                .getTypesafeRequestMap(httpServletRequest);
            BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(
                httpServletRequest);
            BufferedResponseWrapper bufferedResponse = new BufferedResponseWrapper(
                httpServletResponse);
    
            final StringBuilder logMessage = new StringBuilder(
                "REST Request - ").append("[HTTP METHOD:")
                .append(httpServletRequest.getMethod())
                .append("] [PATH INFO:")
                .append(httpServletRequest.getServletPath())
                .append("] [REQUEST PARAMETERS:").append(requestMap)
                .append("] [REQUEST BODY:")
                .append(bufferedRequest.getRequestBody())
                .append("] [REMOTE ADDRESS:")
                .append(httpServletRequest.getRemoteAddr()).append("]");
            log.info("=======================REQUEST PAYLOAD=================================");
            log.info(bufferedRequest.getRequestBody());
            log.info("========================================================");
            filterChain.doFilter(bufferedRequest, bufferedResponse);
            logMessage.append(" [RESPONSE:")
                .append(bufferedResponse.getContent()).append("]");
            log.info("=======================REST RESPONSE=================================");
            log.info(bufferedResponse.getContent());
            log.info("========================================================");
                 } catch (Exception a) {
          log.error("Error while filtering ", a);
        }
      }
    
      private Map<String, String> getTypesafeRequestMap(HttpServletRequest request) {
        Map<String, String> typesafeRequestMap = new HashMap<>();
        Enumeration<?> requestParamNames = request.getParameterNames();
        while (requestParamNames.hasMoreElements()) {
          String requestParamName = (String) requestParamNames.nextElement();
          String requestParamValue;
          if ("password".equalsIgnoreCase(requestParamName)) {
            requestParamValue = "********";
          } else {
            requestParamValue = request.getParameter(requestParamName);
          }
          typesafeRequestMap.put(requestParamName, requestParamValue);
        }
        return typesafeRequestMap;
      }
    
      @Override
      public void destroy() {
        //not yet implemented
      }
    
      private static final class BufferedRequestWrapper extends
          HttpServletRequestWrapper {
    
        private ByteArrayInputStream bais = null;
        private ByteArrayOutputStream baos = null;
        private BufferedServletInputStream bsis = null;
        private byte[] buffer = null;
    
        public BufferedRequestWrapper(HttpServletRequest req)
            throws IOException {
          super(req);
          // Read InputStream and store its content in a buffer.
          InputStream is = req.getInputStream();
          this.baos = new ByteArrayOutputStream();
          byte[] buf = new byte[1024];
          int read;
          while ((read = is.read(buf)) > 0) {
            this.baos.write(buf, 0, read);
          }
          this.buffer = this.baos.toByteArray();
        }
    
        @Override
        public ServletInputStream getInputStream() {
          this.bais = new ByteArrayInputStream(this.buffer);
          this.bsis = new BufferedServletInputStream(this.bais);
          return this.bsis;
        }
    
        String getRequestBody() throws IOException {
          BufferedReader reader = new BufferedReader(new InputStreamReader(
              this.getInputStream()));
          String line;
          StringBuilder inputBuffer = new StringBuilder();
          do {
            line = reader.readLine();
            if (null != line) {
              inputBuffer.append(line.trim());
            }
          } while (line != null);
          reader.close();
          return inputBuffer.toString().trim();
        }
    
      }
    
      private static final class BufferedServletInputStream extends
          ServletInputStream {
    
        private ByteArrayInputStream bais;
    
        public BufferedServletInputStream(ByteArrayInputStream bais) {
          this.bais = bais;
        }
    
        @Override
        public int available() {
          return this.bais.available();
        }
    
        @Override
        public int read() {
          return this.bais.read();
        }
    
        @Override
        public int read(byte[] buf, int off, int len) {
          return this.bais.read(buf, off, len);
        }
    
        @Override
        public boolean isFinished() {
          return false;
        }
    
        @Override
        public boolean isReady() {
          return true;
        }
    
        @Override
        public void setReadListener(ReadListener readListener) {
            //No specific readListener changes required
        }
      }
    
      public class TeeServletOutputStream extends ServletOutputStream {
    
        private final TeeOutputStream targetStream;
    
        public TeeServletOutputStream(OutputStream one, OutputStream two) {
          targetStream = new TeeOutputStream(one, two);
        }
    
        @Override
        public void write(int arg0) throws IOException {
          this.targetStream.write(arg0);
        }
    
        @Override
        public void flush() throws IOException {
          super.flush();
          this.targetStream.flush();
        }
    
        @Override
        public void close() throws IOException {
          super.close();
          this.targetStream.close();
        }
    
        @Override
        public boolean isReady() {
          return false;
        }
    
        @Override
        public void setWriteListener(WriteListener writeListener) {
          //not yet implemented
        }
      }
    
      public class BufferedResponseWrapper implements HttpServletResponse {
    
        HttpServletResponse original;
        TeeServletOutputStream tee;
        ByteArrayOutputStream bos;
    
        public BufferedResponseWrapper(HttpServletResponse response) {
          original = response;
        }
    
        public String getContent() {
          return bos.toString();
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
          return original.getWriter();
        }
    
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
          if (tee == null) {
            bos = new ByteArrayOutputStream();
            tee = new TeeServletOutputStream(original.getOutputStream(),
                bos);
          }
          return tee;
    
        }
    
        @Override
        public String getCharacterEncoding() {
          return original.getCharacterEncoding();
        }
    
        @Override
        public String getContentType() {
          return original.getContentType();
        }
    
        @Override
        public void setCharacterEncoding(String charset) {
          original.setCharacterEncoding(charset);
        }
    
        @Override
        public void setContentLength(int len) {
          original.setContentLength(len);
        }
    
        @Override
        public void setContentLengthLong(long l) {
          original.setContentLengthLong(l);
        }
    
        @Override
        public void setContentType(String type) {
          original.setContentType(type);
        }
    
        @Override
        public void setBufferSize(int size) {
          original.setBufferSize(size);
        }
    
        @Override
        public int getBufferSize() {
          return original.getBufferSize();
        }
    
        @Override
        public void flushBuffer() throws IOException {
          tee.flush();
        }
    
        @Override
        public void resetBuffer() {
          original.resetBuffer();
        }
    
        @Override
        public boolean isCommitted() {
          return original.isCommitted();
        }
    
        @Override
        public void reset() {
          original.reset();
        }
    
        @Override
        public void setLocale(Locale loc) {
          original.setLocale(loc);
        }
    
        @Override
        public Locale getLocale() {
          return original.getLocale();
        }
    
        @Override
        public void addCookie(Cookie cookie) {
          original.addCookie(cookie);
        }
    
        @Override
        public boolean containsHeader(String name) {
          return original.containsHeader(name);
        }
    
        @Override
        public String encodeURL(String url) {
          return original.encodeURL(url);
        }
    
        @Override
        public String encodeRedirectURL(String url) {
          return original.encodeRedirectURL(url);
        }
    
        @SuppressWarnings("deprecation")
        @Override
        public String encodeUrl(String url) {
          return original.encodeUrl(url);
        }
    
        @SuppressWarnings("deprecation")
        @Override
        public String encodeRedirectUrl(String url) {
          return original.encodeRedirectUrl(url);
        }
    
        @Override
        public void sendError(int sc, String msg) throws IOException {
          original.sendError(sc, msg);
        }
    
        @Override
        public void sendError(int sc) throws IOException {
          original.sendError(sc);
        }
    
        @Override
        public void sendRedirect(String location) throws IOException {
          original.sendRedirect(location);
        }
    
        @Override
        public void setDateHeader(String name, long date) {
          original.setDateHeader(name, date);
        }
    
        @Override
        public void addDateHeader(String name, long date) {
          original.addDateHeader(name, date);
        }
    
        @Override
        public void setHeader(String name, String value) {
          original.setHeader(name, value);
        }
    
        @Override
        public void addHeader(String name, String value) {
          original.addHeader(name, value);
        }
    
        @Override
        public void setIntHeader(String name, int value) {
          original.setIntHeader(name, value);
        }
    
        @Override
        public void addIntHeader(String name, int value) {
          original.addIntHeader(name, value);
        }
    
        @Override
        public void setStatus(int sc) {
          original.setStatus(sc);
        }
    
        @SuppressWarnings("deprecation")
        @Override
        public void setStatus(int sc, String sm) {
          original.setStatus(sc, sm);
        }
    
        @Override
        public String getHeader(String arg0) {
          return original.getHeader(arg0);
        }
    
        @Override
        public Collection<String> getHeaderNames() {
          return original.getHeaderNames();
        }
    
        @Override
        public Collection<String> getHeaders(String arg0) {
          return original.getHeaders(arg0);
        }
    
        @Override
        public int getStatus() {
          return original.getStatus();
        }
    
      }
    }
    
  3. ==============================

    3.PUT 및 POST의 경우 대상 자원에 따라 다릅니다. 타겟 리소스가 PUT 또는 POST 요청 후에 응답 본문에 아무 것도 추가하지 않으면 예외가 발생하는 것이 정상입니다. 일반적으로 PUT 또는 POST를 사용하여 보내는 리소스를 알고 있으므로 응답 상태를 확인하여 리소스가 생성되거나 수정되었는지 여부를 확인할 수 있습니다. 응답 본문을 다시 확인할 필요가 없습니다.

    PUT 및 POST의 경우 대상 자원에 따라 다릅니다. 타겟 리소스가 PUT 또는 POST 요청 후에 응답 본문에 아무 것도 추가하지 않으면 예외가 발생하는 것이 정상입니다. 일반적으로 PUT 또는 POST를 사용하여 보내는 리소스를 알고 있으므로 응답 상태를 확인하여 리소스가 생성되거나 수정되었는지 여부를 확인할 수 있습니다. 응답 본문을 다시 확인할 필요가 없습니다.

  4. ==============================

    4.다음을 사용하여 인터셉터의 응답 본문에 액세스 할 수 있습니다. 나는 POST 응답이 403 일 때조차도 작동하는지 확인하기 위해 빠른 단위 테스트를 수행했습니다.

    다음을 사용하여 인터셉터의 응답 본문에 액세스 할 수 있습니다. 나는 POST 응답이 403 일 때조차도 작동하는지 확인하기 위해 빠른 단위 테스트를 수행했습니다.

    그러나주의해서 getBody가 InputStream을 반환합니다. 어느 한 번만 읽을 수 있다는 뜻입니다. 새 본문에 대한 새로운 응답을 제공하지 않으면 인터셉터 외부에서 동일한 스트림을 다시 읽을 수 없습니다.

    ...
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        final ClientHttpResponse response = execution.execute(request, body);
        final InputStream body = response.getBody();
        return response;
    }
    ...
    
  5. from https://stackoverflow.com/questions/47429978/resttemplate-response-getbody-throws-exception-on-4-and-5-errors-for-put-and by cc-by-sa and MIT license