[英]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(
)并可以访问响应主体但是对于PUT
和POST
请求, ClientHttpResponse.getBody()
方法会抛出异常。
What might be causing this and how can i get the response body for POST
and PUT
requests as well? 可能导致这种情况的原因以及如何获得
POST
和PUT
请求的响应体?
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
有两个相似的字段, errorStream
和inputStream
。 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 现在这里是答案
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。 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. 对于
PUT
和POST
,它取决于您的目标资源。 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. 如果您的目标资源在
PUT
或POST
请求后没有在响应的正文中添加任何内容,则获取异常是正常的。 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. 通常,您知道使用
PUT
或POST
发送的资源,因此您只需检查响应的状态即可知道您的资源是否已创建或修改。 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.