I use Java 8 application based on springboot and gradle and i try to suppress all null values into my JSON request body :
{"typePersonne":"Adherent","adressePrincipale":{"ligne1":null,"ligne2":null}
I have create interceptor to see my json query :
public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
private final org.slf4j.Logger log = LoggerFactory.getLogger(this.getClass());
private void logRequest(final HttpRequest request, final byte[] body) throws IOException {
if (log.isDebugEnabled()) {
log.debug("===========================request begin================================================");
log.debug("URI : {}", request.getURI());
log.debug("Method : {}", request.getMethod());
log.debug("Headers : {}", request.getHeaders());
log.debug("Request body: {}", new String(body, "UTF-8"));
log.debug("==========================request end================================================");
}
}
private void logResponse(final ClientHttpResponse response) throws IOException {
if (log.isDebugEnabled()) {
log.debug("============================response begin==========================================");
log.debug("Status code : {}", response.getStatusCode());
log.debug("Status text : {}", response.getStatusText());
log.debug("Headers : {}", response.getHeaders());
log.debug("Response body: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
log.debug("=======================response end=================================================");
}
}
#Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
logRequest(request, body);
final ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
}
How can i suppress all key corresponding to the null values when i call the API ?
final HttpEntity<?> httpEntity = new HttpEntity<>(adherent, headersCompleted);
responseEntity = restTemplate.exchange(urlComplete, HttpMethod.PUT, httpEntity,String.class);
Thanks :)
Related
In my spring-boot application, I have a GET end-point. When we call this GET endpoint, the application is sending a GET request to another service with RestTemplate and returns the same response file as the response of the GET request. With the below code I'm able to receive the response file. But I need to set the same headers that I have received to RestTempate request.
How to do that.
#GetMapping(value = URL_CONTENT_ID, produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE, APPLICATION_ZIP_VALUE,
MediaType.TEXT_HTML_VALUE})
#ControllerLogging
public ResponseEntity<StreamingResponseBody> getContentFile(#PathVariable String contentId) {
StreamingResponseBody responseBody = outputStream -> {
getContentFile(outputStream, contentId);
outputStream.close();
};
return ResponseEntity.ok()
.body(responseBody);
}
public void getContentFile(OutputStream outputStream, String nodeId) {
RequestCallback requestCallBack = request -> {
HttpHeaders headers = new HttpHeaders();
authenticationHelper.apply(headers::set);
request.getHeaders().addAll(headers);
};
ResponseExtractor<OutputStream> responseExtractor = clientHttpResponse -> {
InputStream inputStream = clientHttpResponse.getBody();
StreamUtils.copy(inputStream, outputStream);
return null;
};
restTemplate.execute(dcmUrl + nodeId, HttpMethod.GET, requestCallBack, responseExtractor);
}
Please find the solution to this issue, the change I did was adding "HttpServletResponse" to the parameters of the controller method.
#GetMapping(value = URL_CONTENT_ID, produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE, APPLICATION_ZIP_VALUE,
MediaType.TEXT_HTML_VALUE})
#ControllerLogging
public ResponseEntity<StreamingResponseBody> getContentFile(#PathVariable String contentId, HttpServletResponse response) {
StreamingResponseBody responseBody = outputStream -> {
getContentFile(outputStream, response, contentId);
outputStream.close();
};
return ResponseEntity.ok(responseBody);
}
public void getContentFile(OutputStream outputStream, HttpServletResponse response, String nodeId) {
RequestCallback requestCallBack = request -> {
HttpHeaders headers = new HttpHeaders();
authenticationHelper.apply(headers::set);
request.getHeaders().addAll(headers);
};
ResponseExtractor<OutputStream> responseExtractor = clientHttpResponse -> {
InputStream inputStream = clientHttpResponse.getBody();
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, modifyContentDispositionHeader(clientHttpResponse));
response.setHeader(HttpHeaders.CONTENT_TYPE, modifyContentTypeHeader(clientHttpResponse).toString());
StreamUtils.copy(inputStream, outputStream);
return null;
};
restTemplate.execute(dcmUrl + nodeId, HttpMethod.GET, requestCallBack, responseExtractor);
}
I want to log all HTTP requests.
Normally, I can get a request body for a successful request with the code below.
But I don't get the request body if a request fails.
When I try to get the body of the failed request I only get an empty string.
How can I get failed HTTP request body in java?
-Filter class-
#Component
#Slf4j
public class HttpLogFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
String requestId = UUID.randomUUID().toString();
response.addHeader(REQUEST_ID_PROPERTY_NAME, requestId);
ThreadContext.put(REQUEST_ID_PROPERTY_NAME, requestId);
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(requestWrapper, responseWrapper);
HttpLogDTO httpLog = collectHttpInfo(requestWrapper, responseWrapper);
responseWrapper.copyBodyToResponse();
long endTime = System.currentTimeMillis();
httpLog.setDuration(endTime - startTime);
log.info(httpLog.toString());
}
private HttpLogDTO collectHttpInfo(HttpServletRequest request, HttpServletResponse response) {
String scheme = request.getScheme();
String host = request.getServerName();
String port = String.valueOf(request.getLocalPort());
String path = request.getServletPath();
String parameters = request.getParameterMap().keySet().stream()
.map(key -> key + "=" + request.getParameterMap().get(key)[0]).collect(Collectors.joining("&"));
Map<String, List<String>> requestHeaders = Collections.list(request.getHeaderNames())
.stream()
.collect(Collectors.toMap(
Function.identity(),
h -> Collections.list(request.getHeaders(h))
));
Map<String, String> responseHeaders = response.getHeaderNames()
.stream()
.collect(Collectors.toMap(h -> h, response::getHeader));
return HttpLogDTO.builder()
.protocol(request.getProtocol())
.remote(request.getRemoteAddr())
.method(request.getMethod())
.uri(scheme + "://" + host + ":" + port + path + (StringUtils.isNotBlank(parameters) ? "?" + parameters : ""))
.host(host)
.path(path)
.scheme(scheme)
.port(port)
.requestHeaders(requestHeaders.toString())
.requestBody(new String(((ContentCachingRequestWrapper) request).getContentAsByteArray()))
.statusCode(String.valueOf(response.getStatus()))
.statusValue(Objects.requireNonNull(HttpStatus.resolve(response.getStatus())).getReasonPhrase())
.responseHeaders(responseHeaders.toString())
.responseBody(new String(((ContentCachingResponseWrapper) response).getContentAsByteArray()))
.build();
}
}
[SOLVED]
The problem is not in the above Filter class.
The problem is that I did write the code like I don't expect body in the controller class.
Problem solved after adding my POJO class with #RequestBody annotation in the method.
Old Code
#PostMapping("/post")
public ResponseEntity post() {
throw new BusinessValidationException(BusinessValidationRule.CITY_NOT_FOUND);
}
New Code
#PostMapping("/post")
public ResponseEntity post(#RequestBody Object body) {
throw new BusinessValidationException(BusinessValidationRule.CITY_NOT_FOUND);
}
I was trying out RestTemplate and Retrofit2. Both the libraries throw exception in case api returns 4XX/5XX. The api when hit from postman gives a JSON response body, along with 4XX/5XX.
How can I retrieve this JSON response using RestTemplate or Retrofit2.
Thanks.
Use the HttpClientErrorException, HttpStatusCodeException after try block as below.
try{
restTemplate.exchange("url", HttpMethod.GET, null, String.class);
}
catch (HttpClientErrorException errorException){
logger.info("Status code :: {}, Exception message :: {} , response body ::{}" , e.getStatusCode()
e.getMessage(), e.getResponseBodyAsString());
}
catch (HttpStatusCodeException e){
logger.info("Status code :: {}, Exception message :: {} , response body ::{}" , e.getStatusCode()
e.getMessage(), e.getResponseBodyAsString());
}
For that you have to create RestTemplateError handler and register that class while creating bean for RestTemplate.
#Bean
public RestTemplate getBasicRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
return restTemplate;
}
where your handler class has to implements ResponseErrorHandler. You can read the json response that is stored in the body.
#Component
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateResponseErrorHandler.class);
#Override
public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
return httpResponse.getStatusCode().series() == CLIENT_ERROR
|| httpResponse.getStatusCode().series() == SERVER_ERROR;
}
#Override
public void handleError(ClientHttpResponse httpResponse) throws IOException {
if (httpResponse.getStatusCode().series() == SERVER_ERROR) {
LOGGER.error("Handling server error response statusCode:{} ", httpResponse.getStatusCode());
} else if (httpResponse.getStatusCode().series() == CLIENT_ERROR) {
LOGGER.error("Handling Client error response statusCode:{} ", httpResponse.getStatusCode());
String body;
InputStreamReader inputStreamReader = new InputStreamReader(httpResponse.getBody(),
StandardCharsets.UTF_8);
body = new BufferedReader(inputStreamReader).lines().collect(Collectors.joining("\n"));
throw new CustomException(httpResponse.getStatusCode().toString(), httpResponse, body);
}
}
}
Restservice I am invoking using oauth2resttemplate returns a response with status code 403. I see that in my ClientHttpRequestInterceptor log lines injected to the resttemplate.
This is the interceptor to intercept request and response. traceResponse() logs response status code as 403 which I expected.
public class RequestInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
try
{
traceRequest(request, body);
} catch (Exception e)
{
ManagerLog.alertLog("Exception occurred", e);
}
ClientHttpResponse response = execution.execute(request, body);
traceResponse(response);
return response;
}
private void traceRequest(HttpRequest request, byte[] body) throws Exception {
ManagerLog.infoLog("===========================request begin================================================");
ManagerLog.infoLog("URI : "+request.getURI());
ManagerLog.infoLog("Method : "+request.getMethod());
ManagerLog.infoLog("Headers : "+request.getHeaders() );
ManagerLog.infoLog("Request body : "+new String(body, "UTF-8"));
ManagerLog.infoLog("==========================request end================================================");
}
private void traceResponse(ClientHttpResponse response) throws IOException {
ManagerLog.infoLog("============================response begin==========================================");
ManagerLog.infoLog("Response Status code : "+ response.getStatusCode());
ManagerLog.infoLog("Response Status text : "+ response.getStatusText());
ManagerLog.infoLog("Response Headers : "+ response.getHeaders());
// ManagerLog.infoLog("Response body : "+ StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
ManagerLog.infoLog("=======================response end=================================================");
}
}
But down here I see 400 instead of 403. But I see in the spring source code it is actually hardcoded to 400 in parent class(OAuth2Exception.java) of InvalidGrantException.
try{
objectResponseEntity = oauth2RestTemplate.exchange(url, httpMethod, httpEntity, pojo) -- throws an InvalidGrantException
} catch (HttpStatusCodeException e)
{
...
} catch(InvalidGrantException e)
{
errorCode = e.getHttpErrorCode(); -- was expecting 403 but returning 400
...
}
Any idea on how to get the actual http response code from the exception?
I have a webservice which gets data from other webservice and return back to the browser.
I want to hide internal client errors
Want to throw 404, 400 etc which
are returned from the webservice in the below method.
How to resolve this problem in a neat way?
Option 1 or Option 2 is clean way?
Option 1
public <T> Optional<T> get(String url, Class<T> responseType) {
String fullUrl = url;
LOG.info("Retrieving data from url: "+fullUrl);
try {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(ImmutableList.of(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + httpAuthCredentials);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<T> exchange = restTemplate.exchange(fullUrl, HttpMethod.GET, request, responseType);
if(exchange !=null)
return Optional.of(exchange.getBody());
} catch (HttpClientErrorException e) {
LOG.error("Client Exception ", e);
throw new HttpClientError("Client Exception: "+e.getStatusCode());
}
return Optional.empty();
}
(or)
Option 2
public <T> Optional<T> get(String url, Class<T> responseType) {
String fullUrl = url;
LOG.info("Retrieving data from url: "+fullUrl);
try {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(ImmutableList.of(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + httpAuthCredentials);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<T> exchange = restTemplate.exchange(fullUrl, HttpMethod.GET, request, responseType);
if(exchange !=null)
return Optional.of(exchange.getBody());
throw new RestClientResponseException("", 400, "", null, null, null);
} catch (HttpStatusCodeException e) {
LOG.error("HttpStatusCodeException ", e);
throw new RestClientResponseException(e.getMessage(), e.getStatusCode().value(), e.getStatusText(), e.getResponseHeaders(), e.getResponseBodyAsByteArray(), Charset.defaultCharset());
}
return Optional.empty();
}
I have written a sample ResponseErrorHandler for you,
public class RestTemplateClientErrorHandler implements ResponseErrorHandler {
private static final Logger logger = LoggerFactory.getLogger(RestTemplateClientErrorHandler.class);
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
return RestUtil.isError(clientHttpResponse.getStatusCode());
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
String responseBody = "";
if(clientHttpResponse != null && clientHttpResponse.getBody() != null){
responseBody = IOUtils.toString(clientHttpResponse.getBody());
}
switch(clientHttpResponse.getRawStatusCode()){
case 404:
logger.error("Entity not found. Message: {}. Status: {} ",responseBody,clientHttpResponse.getStatusCode());
throw new RestClientResponseException(responseBody);
case 400:
logger.error("Bad request for entity. Message: {}. Status: {}",responseBody, clientHttpResponse.getStatusCode());
throw new RestClientResponseException(StringUtils.EMPTY, 400,StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY);
default:
logger.error("Unexpected HTTP status: {} received when trying to delete entity in device repository.", clientHttpResponse.getStatusCode());
throw new RestClientResponseException(responseBody);
}
}
public static class RestUtil {
private RestUtil() {
throw new IllegalAccessError("Utility class");
}
public static boolean isError(HttpStatus status) {
HttpStatus.Series series = status.series();
return HttpStatus.Series.CLIENT_ERROR.equals(series)
|| HttpStatus.Series.SERVER_ERROR.equals(series);
}
}
}
Note : This is common ResponseErrorHandler for your restTemplate and it will catch all the exceptions thrown by restTemplate you don't require try,catch block in each method and you don't need to catch "HttpStatusCodeException" or any other exception.
Please use the below code to register this ErrorHandler.
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new RestTemplateClientErrorHandler());
You can also find examples here.
You can refactor your client class like this,
public <T> Optional<T> get(String url, Class<T> responseType) {
String fullUrl = url;
LOG.info("Retrieving data from url: "+fullUrl);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(ImmutableList.of(MediaType.APPLICATION_JSON));
headers.add("Authorization", "Basic " + httpAuthCredentials);
HttpEntity<String> request = new HttpEntity<>(headers);
ResponseEntity<T> exchange = restTemplate.exchange(fullUrl, HttpMethod.GET, request, responseType);
if(exchange !=null)
return Optional.of(exchange.getBody());
return Optional.empty();
}
So your method not looking beautiful now ? Suggestions welcome.