Log Response content in spring HandlerInterceptorAdapter postHandle - java

I have implemented the below method to log response , but it throws an exception at IOUtils.toString(responseWrapper.getContentInputStream(), "UTF-8"); saying response has zero bytes . I am able to access headers and status. how can I get the response body?
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("after Request");
ObjectMapper objectMapper = new ObjectMapper();
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
HttpStatus responseStatus = HttpStatus.valueOf(responseWrapper.getStatusCode());
System.out.println(responseWrapper.getStatusCode());
HttpHeaders responseHeaders = new HttpHeaders();
for (String headerName : responseWrapper.getHeaderNames()) {
System.out.println(headerName);
responseHeaders.add(headerName, responseWrapper.getHeader(headerName));
}
String responseBody = IOUtils.toString(responseWrapper.getContentInputStream(), "UTF-8");
System.out.println(responseBody);
log.info("Response body {} ",responseBody);
responseWrapper.copyBodyToResponse();
}

You never use response.getOutputStream() or response.getWriter() so no data is added to the response. That's why it is empty.
Your created entity
ResponseEntity<JsonNode> responseEntity = new ResponseEntity<>(responseJson,responseHeaders,responseStatus);
is never used and never sent to response. Try e.g.
responseWrapper.getWriter().write("test");
and check the body after.

How to read and copy the HTTP servlet response output stream content for logging
has your answer. The reason why getting your response output stream does not work is because the output stream gets written to and flushed regularly as you generate your output. This means that when you attempt to get it at the end, you just have an empty output. Alternatively, if you capture it at the beginning, that just means that your consumption of the outputstream denies the same consumption to your client. This is why you have to copy it as in the answer above.

Related

Retrieving the name of the method from which HTTP call was made using an Interceptor

I am using the Spring ClientHttpRequestInterceptor to capture all outgoing HTTP calls from my applications in order to log the data. In addition to the data that I am already collecting in the interceptor, I want to somehow fetch the name of the function from which the HTTP call originated. So, as an example, if a method called getStuffFromUrl is making the HTTP call using the Spring RestTemplate as follows,
public String getStuffFromUrl() {
...
return restTemplate.exchange(url, HttpMethod.GET,entity, String.class).getBody();
}
when I capture this outbound HTTP call in my interceptor, I want to retrieve the name of the method getStuffFromUrl as well. How could I go about doing this?
If you are allowed to modify your HTTP request, one way would be to add a ad-hoc HTTP header for the method name :
public String getStuffFromUrl() {
HttpHeaders headers = new HttpHeaders();
headers.add("JavaMethod", "getStuffFromUrl");
entity = new Entity(headers)
...
return restTemplate.exchange(url, HttpMethod.GET,entity, String.class).getBody();
}
You could then get back the method name and remove the header from within the ClientHttpRequestInterceptor prior the HTTP request is actualy sent out.
ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution)
throws IOException {
String javaMethodName="Unknown";
List<String> javaMethodHeader = request.getHeaders().remove("JavaMethod");
if(javaMethodHeader!=null && javaMethodHeader.size()>0) {
javaMethodName = javaMethodHeader.get(0);
}
log.info("Calling method = "+ javaMethodName);
execution.execute(request, body);
}
(provided code not tested)

RestController - Forward POST request to external URL

I'm looking for a way how to forward POST request which has been made to endpoint in #RestController class and forward it to external URL with body and headers untouched (and return response from this API of course), is it possible to do it by using some spring features? The only solution which I have found is extracting a body from #RequestBody and headers from HttpServletRequest and use RestTemplate to perform a request. Is there any easier way?
#RequestMapping("/**")
public ResponseEntity mirrorRest(#RequestBody(required = false) String body,
HttpMethod method, HttpServletRequest request, HttpServletResponse response)
throws URISyntaxException {
String requestUrl = request.getRequestURI();
URI uri = new URI("http", null, server, port, null, null, null);
uri = UriComponentsBuilder.fromUri(uri)
.path(requestUrl)
.query(request.getQueryString())
.build(true).toUri();
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.set(headerName, request.getHeader(headerName));
}
HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();
try {
return restTemplate.exchange(uri, method, httpEntity, String.class);
} catch(HttpStatusCodeException e) {
return ResponseEntity.status(e.getRawStatusCode())
.headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
}
The above code is taken from this answer.
This is more a matter of the HTTP spec than Spring where the server would be expected to return a 307 redirect status, indicating the client should follow the redirect using the same method and post data.
This is generally avoided in the wild as there's a lot of potential for misuse, and friction if you align with the W3.org spec that states the client should be prompted before re-executing the request at the new location.
One alternative is to have your Spring endpoint act as a proxy instead, making the POST call to the target location instead of issuing any form of redirect.
307 Temporary Redirect (since HTTP/1.1) In this occasion, the request should be repeated with another URI, but future requests can still use the original URI.2 In contrast to 303, the request method should not be changed when reissuing the original request. For instance, a POST request must be repeated using another POST request.

Java spring file download proxy with rest calls

I need to create a rest service in java which will in turn connect to another rest service for file download. For now, I just need to transfer the file from the other backend to client but in future some processing/transformations would be done.
For all the web services in my project, we are using spring rest (for providing as well as consuming the services).
My question is what would be the appropriate way of doing it considering that the files would be large and I don't want to run into OutOfMemory errors.
People in some other posts have suggested to use streams on both the ends but is that really possible? For this, do I need to write the file on disk first?
My current code for file download (consumer) -
public BackendResponse<byte[]> callBackendForFile(BackendRequest request) {
String body = null;
ResponseEntity<byte[]> responseEntity = null;
URI uri = createURI(request);
MultiValueMap<String, String> requestHeaders = getHeadersInfo(request.getHttpRequest());
if (HttpMethod.GET.equals(request.getMethod())) {
responseEntity = restTemplate.exchange(uri, request.getMethod(),
new HttpEntity<String>(body, requestHeaders), byte[].class);
} else {
LOG.error("Method:{} not supported yet", request.getMethod());
}
BackendResponse<byte[]> response = new BackendResponse<>();
response.setResponse(responseEntity);
return response;
}
My client code (provider):
#RequestMapping(value = "/file", method = RequestMethod.GET, produces = "application/xml")
#ResponseBody
public void downloadFileWithoutSpring(HttpMethod method, HttpServletRequest httpRequest,
HttpServletResponse httpResponse) {
BackendRequest request = new BackendRequest(method,
httpRequest.getRequestURI(), httpRequest.getQueryString(), httpRequest);
BackendResponse<byte[]> backendResponse = dutyplanService.getFile(request);
ResponseEntity<byte[]> response = backendResponse.getResponse();
httpResponse.addHeader("Content-Disposition", "attachment; filename=\"" + "attachment.zip" + "\"");
httpResponse.getOutputStream().write(response.getBody());
httpResponse.flushBuffer();
}
Note: The code above doesn't work somehow as the attachment downloaded is a corrupt file
I don't think you will need to create that file on server as long as you are having the bytearray content of it received from another server.
You can try changing value of produces annotation to the value application/zip (or application/octet-stream, depending on the target browser) instead of 'application/xml'
you can pass HttpServletResponse#getOutputStream() directly in restTemplate and write it without save file in server.
public void getFile(HttpServletResponse response) throws IOException {
restTemplate.execute(
"http://ip:port/temp.csv",
HttpMethod.GET,
null,
clientHttpResponse -> {
StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
return null;
}
);
}
note that after call getFile(), you should close outputStream like this
response.getOutputStream().close()

Get size of serialized Request using Spring RestTemplate

I am using Spring and RestTemplate to send requests to a REST service. Is there a way to get the (byte)size of the actual request? Optimally it includes the size of the HTTP request including the size of the serialized myObject object for both GET and POST requests.
template.postForObject(restUrl, myObject, MyObject.class);
template.getForObject(restUrl, MyObject.class);
Basically I want to know how much data is actually transmitted.
Thanks!
[edit]:
Just to complete the answer, this is how you add the Interceptor to the RestTemplate. I also edited the LengthInterceptor to show the content length of the request instead of the response.
final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add( new LengthInterceptor() );
template.setInterceptors( interceptors );
You can use interceptor to intercept the request/response similarly to filter in java servlet. You have to read the response and using getHeaders().getContentLength() you'll get the body length:
public class LengthInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept( HttpRequest request, byte[] body, ClientHttpRequestExecution execution ) throws IOException {
ClientHttpResponse response = execution.execute( request, body );
long length = response.getHeaders().getContentLength();
// do something with length
return response;
}
}

Trying to use bing translator API with Robospice in Android

I need to implement robospice for doing the networking part in my Translator app. I previously used async task class and it was working fine, but now i want to improve my application with implementing robospice. I'am trying to execute the following code but it doesn't't throw any exception it just never executes....
#Override
public TranslatedText loadDataFromNetwork() throws Exception {
String jsonString = getJsonString();
String headerValue = getHeaderValue(jsonString);
String text = pair.getWordPairs().getWordFrom();
String languageFrom = pair.getLanguagePairs().getLanguageFrom().getCode();
String languageTo = pair.getLanguagePairs().getLangougateTo().getCode();
String uri = String
.format("http://api.microsofttranslator.com/v2/Http.svc/Translate?text=%s&from=%s&to=%s&contentType=text/html",
URLEncoder.encode(text, "UTF-8"),
URLEncoder.encode(languageFrom, "UTF-8"),
URLEncoder.encode(languageTo, "UTF-8"));
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", headerValue);
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the Simple XML message converter
getRestTemplate().getMessageConverters().add(new SimpleXmlHttpMessageConverter());
//set the headerValue in the Entity
org.springframework.http.HttpEntity<?> request = new org.springframework.http.HttpEntity<Object>(headerValue);
// Make the HTTP GET request, marshaling the response from XML to an
// EventList
Log.v("request","Making request!");
//This line never finish execuitng, doesen't throw exception or anything in logCat
ResponseEntity<Object> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, null);
Log.v("request", responseEntity.getBody().toString());
Log.d("Load Data From Network", request.getBody().toString());
return null;
}
The last thing it shows in log cat is Request First!! And nothing after that. It never even gets to The Request Listener onRequestFailure.
Can any 1 tell me what i do wrong ?
This is what look weird to me in your code:
ResponseEntity<Object> and null as 4th parameter of the exchange method are not correct. You need to provide a type which represents the response you get from the server.
The object returned by loadDataFromNetwork() is what you will get in the onRequestSuccess() method. Returning null is not a good idea, in my opinion.
I fixed the problem. So if you need to handle stream you will have to provide the following code
ResponseEntity<byte[]> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, byte[]);

Categories