简体   繁体   English

RestTemplate response.getBody对put和post请求的4 **和5 **错误抛出异常但对get请求工作正常

[英]RestTemplate response.getBody throws exception on 4** and 5** errors for put and post request but works fine for get requests

I am trying to intercept and log all the request-responses. 我试图拦截并记录所有请求 - 响应。 To make requests i am using RestTemplate.exchange() . 要发出请求我正在使用RestTemplate.exchange()

When i make a GET request and get an 4** error i can call the ClientHttpResponse.getBody( ) and can access the response body but for PUT and POST requests ClientHttpResponse.getBody() method throws an exception. 当我发出GET请求并获得4**错误时,我可以调用ClientHttpResponse.getBody( )并可以访问响应主体但是对于PUTPOST请求, ClientHttpResponse.getBody()方法会抛出异常。

What might be causing this and how can i get the response body for POST and PUT requests as well? 可能导致这种情况的原因以及如何获得POSTPUT请求的响应体?

This is where i make the request: 这是我提出请求的地方:

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

This is the part of the interceptor that gets the exception: 这是获取异常的拦截器的一部分:

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

This is the stack. 这是堆栈。

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)

Update : 更新:

When i call response.getStatusCode() before calling response.getBody() it doesn't throw IOException . 当我在调用response.getStatusCode()之前调用response.getBody()它不会抛出IOException

Basic knowledge: 基础知识:

HttpURLConnection has two similar fields, errorStream and inputStream . HttpURLConnection有两个相似的字段, errorStreaminputStream When we invoke its getInputSteam method, it checks whether the response has an error code. 当我们调用其getInputSteam方法时,它会检查响应是否有错误代码。 If so, it throws an IOException and records it- that's why you got the exception. 如果是这样,它会抛出IOException并记录它 - 这就是你得到异常的原因。 Furthermore, it also copies the contents in inputStream to errorStream , thus we can get its response body by invoking its getErrorStream method. 此外,它还将inputStream的内容复制到errorStream ,因此我们可以通过调用其getErrorStream方法来获取其响应主体。 This is exactly what SimpleClientHttpResponse does with its getBody method: 这正是SimpleClientHttpResponse对其getBody方法的作用:

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

It first checks if errorStream is not null. 它首先检查errorStream是否为null。 If true, it returns it. 如果为true,则返回它。 If false, it calls connection.getInputStream() and returns that. 如果为false,则调用connection.getInputStream()并返回该值。

Now here are the answers 现在这里是答案

  1. Why does calling response.getBody() not throw an IOException after you called response.getStatusCode() ? 为什么叫response.getBody()不抛出IOException你叫后response.getStatusCode() It is because getStatusCode calls getInputStream internally. 这是因为getStatusCode内部调用getInputStream Thus, errorStream will be not null when getBody is called. 因此,调用getBody时, errorStream不为null。
  2. Why does it not throw an exception when the http method is GET? 为什么http方法是GET时不会抛出异常? See method org.springframework.http.client.SimpleBufferingClientHttpRequest#executeInternal . 请参阅方法org.springframework.http.client.SimpleBufferingClientHttpRequest#executeInternal

.

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

It eagerly executes this.connection.getResponseCode(); 它急切地执行this.connection.getResponseCode(); when the http method is GET. 当http方法是GET时。

I had a similar requirement of logging every request and response. 我有一个类似的记录每个请求和响应的要求。 I wrote a filter and hooked into the filter chain. 我写了一个过滤器并挂钩过滤器链。

The code looks like something below: 代码如下所示:

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

  }
}

For PUT and POST , it depends on your target resource. 对于PUTPOST ,它取决于您的目标资源。 If your target resource do not add anything in the response's body after a PUT or POST request, it's normal to get an exception. 如果您的目标资源在PUTPOST请求后没有在响应的正文中添加任何内容,则获取异常是正常的。 In general, you know the resource that you send with PUT or POST so you can just check the response's status to know if your resource had been created or modified. 通常,您知道使用PUTPOST发送的资源,因此您只需检查响应的状态即可知道您的资源是否已创建或修改。 You do not need to check the response body again. 您无需再次检查响应正文。

You can use the following to accesss the response body in the interceptor. 您可以使用以下内容访问拦截器中的响应主体。 I did a quick unit test to confirm it works even on a POST with a 403 response. 我做了一个快速的单元测试,以确认它甚至在具有403响应的POST上也能正常工作。

However be careful, getBody returns an InputStream. 但是要小心,getBody返回一个InputStream。 Which means you can only read it once. 这意味着您只能阅读一次。 You won't be able to read the same stream again outside the interceptor unless you provide a new response with a new body. 除非您使用新主体提供新响应,否则您将无法在拦截器外再次读取相同的流。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM