Trying to add Headers Authorization on restTemplate.exchange, the body returns null. but the request is valid and the body always contains something.
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", this.getToken("public","public") );
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<String> response = restTemplate.exchange(uriFinal, HttpMethod.GET, request, String.class);
log.debug( response.getBody() );
profiling with debug, I got the results:
===========================request begin================================================
URI : http://localhost:8080/api/department?pageNumber=0&pageSize=20
Method : GET
Headers : [Accept:"text/plain, application/json, application/*+json, */*", Content-Type:"application/json", Authorization:"Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJwdWJsaWMiLCJleHAiOjE1Njk1NjQ5MTl9.o0N-y6dxXDPjvviRA66AR1gppwN31XEAjT3mttSdhTbUCuBlBFTdSIjnPyLqPyvyEu1sKIBax1QoKglejJ0cgw", Content-Length:"0"]
Request body 1:
==========================request end================================================
============================response begin==========================================
Status code : 200 OK
Status text :
Headers : [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Set-Cookie:"JSESSIONID=B0F5DFA38D26AD57C489F1366D291780; Path=/; HttpOnly", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Thu, 26 Sep 2019 14:03:07 GMT"]
Response body 2: {"content":[],"pageable":{"sort":{"sorted":false,"unsorted":true,"empty":true},"offset":0,"pageSize":20,"pageNumber":0,"paged":true,"unpaged":false},"totalPages":0,"totalElements":0,"last":true,"number":0,"sort":{"sorted":false,"unsorted":true,"empty":true},"size":20,"first":true,"numberOfElements":0,"empty":true}
=======================response end=================================================
When I disabled the debug interceptor it worked!
#Bean
public RestTemplate getRestTemplate(){
RestTemplate restTemplate = new RestTemplate();
if(apiRestDebugEnable){
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseLoggingInterceptor()));
}
return restTemplate;
}
public class RequestResponseLoggingInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, 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 1: {}", new String(body, "UTF-8"));
log.debug("==========================request end================================================");
}
}
private void logResponse(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 2: {}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
log.debug("=======================response end=================================================");
}
}
}
Related
The service provider supplies me with the header data: grant_type, Content-Type. And the body data: grant_type, username and password. Which I use in Postman where it generates OK token. But in the Spring application it generates an error HttpClientErrorException $ BadRequest: 400 Bad Request.
I have the class to set the body data:
public class BodyToken {
private String grant_type = "password";//set body data
private String username = "User";//set body data
private String password = "123";//set body data
private String access_token;
#JsonGetter("access_token")
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
#JsonGetter("grant_type")
public String getGrant_type() {
return grant_type;
}
#JsonGetter("username")
public String getUsername() {
return username;
}
#JsonGetter("password")
public String getPassword() {
return password;
}
}
This is the controller where the header data is set:
#PostMapping("/TokenGeneration")
#ResponseBody
public BodyToken TokenGeneration() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("grant_type", "password");//set header data
headers.set("Content-Type", "application/x-www-form-urlencoded");//set header data
HttpEntity request = new HttpEntity(headers);
headers.add("User-Agent", "Spring's RestTemplate" );
ResponseEntity<BodyToken> response = restTemplate.exchange(
"https://sw/token",
HttpMethod.POST,
request,
BodyToken.class
);
try {
return response.getBody();
} catch (Exception e) {
BodyToken body = new BodyToken();
log.info(e.getMessage());
return body;
}
}
OK was solved with using the Class MultiValueMap and LinkedMultiValueMap. The credentials are added to this new object and it is sent together with the request:
#PostMapping("/TokenGeneration")
#ResponseBody
public BodyToken TokenGeneration() throws IOException {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.set("grant_type", "password");//set header data
headers.set("Content-Type", "application/x-www-form-urlencoded");//set header data
MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();//line solution
body.add("grant_type", "password");//line solution
body.add("username", "user");//line solution
body.add("password", "123");//line solution
HttpEntity request = new HttpEntity(body, headers);//and I add this body to HttpEntity
headers.add("User-Agent", "Spring's RestTemplate" );
ResponseEntity<BodyToken> response = restTemplate.exchange(
"https://sw/token",
HttpMethod.POST,
request,
BodyToken.class
);
try {
return response.getBody();
} catch (Exception e) {
BodyToken body = new BodyToken();
log.info(e.getMessage());
return body;
}
}
I have to write a procedure which will accept body in Hashmap format.
I have created HttpEntity with Hashmap values & headers.
public <T> void doPOSTRequest(String url, T body, HttpHeaders headers) throws JsonProcessingException {
HttpEntity<T> request = new HttpEntity<T>(body,headers);
System.out.println("Printing Request :" + request);
ResponseEntity<String> response = null;
//Calling POST Method
//response=restTemplate.postForObject(url,request,String.class);
response=restTemplate.exchange(url, HttpMethod.POST,request,String.class);
System.out.println(response);
}
I am facing below exception :
Exception in thread "main"
org.springframework.web.client.RestClientException: No
HttpMessageConverter for [java.util.HashMap] at
org.springframework.web.client.RestTemplate$HttpEntityRequestCallback.doWithRequest(RestTemplate.java:957)
at
org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:733)
at
org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)
at
org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:579)
at Com.RESTRequest.doPOSTRequest(RESTRequest.java:39) at
Com.GenericREST.main(GenericREST.java:30)
Try using MultiValueMap instead of Generic like this
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<MultiValueMap<String, Object>>(parameters, headers);
Full block:
public <T> void doPOSTRequest(String url, T body, HttpHeaders headers) throws JsonProcessingException {
HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(body,headers);
System.out.println("Printing Request :" + request);
ResponseEntity<String> response = null;
//Calling POST Method
//response=restTemplate.postForObject(url,request,String.class);
response=restTemplate.exchange(url, HttpMethod.POST,request,String.class);
System.out.println(response);
}
I have done the following for my custom logging interceptor
public class HttpLoggingInterceptor implements ClientHttpRequestInterceptor {
private final static Logger log = LoggerFactory.getLogger(HttpLoggingInterceptor.class);
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws IOException {
log.info("Request URI : {}, Method : {}, Headers : {}, Request body : {}", request.getURI(), request.getMethod(), request.getHeaders(), new String(body, "UTF-8"));
}
private void logResponse(ClientHttpResponse response) throws IOException {
log.info("Response Status code : {}, Status text : {}, Headers : {}, Response body: {}", response.getStatusCode(), response.getStatusText(), response.getHeaders(), StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
}
}
And I am setting the intercepter to the restTemplate
#Autowired
public RestTemplate restTemplate;
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
List<ClientHttpRequestInterceptor> clientHttpRequestInterceptors = new ArrayList<>();
clientHttpRequestInterceptors.add(new HttpLoggingInterceptor());
// clientHttpRequestInterceptors.addAll(restTemplate.getInterceptors());
restTemplate.setInterceptors(clientHttpRequestInterceptors);
// restTemplate.setInterceptors(Collections.singletonList(new HttpLoggingInterceptor()));
}
The logger is printing the response properly to the console, But at the end the response is returned as empty to the caller. I am not able to debug and figure it out.
I have figured it out that the StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()) is reading the input stream once and it is no more holding the response body in it (which is empty now)
Anyone else also facing the same issue and has any idea of duplicating the InputStream without reading it from the original InputStream?
Since the input stream can be consumed only once and there is no reset() or mark(***) function available for sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.
There is only one way to read the response multiple time by creating the restTemplate in the following way.
#Bean
public RestTemplate getfxoWsClientRestTemplate(){
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
restTemplate.setInterceptors(Collections.singletonList(new HttpLoggingInterceptor()));
return restTemplate;
}
And the LoggingIntercepter can be written like this
public class HttpLoggingInterceptor implements ClientHttpRequestInterceptor {
private final static Logger logger = LoggerFactory.getLogger(HttpLoggingInterceptor.class);
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
logger.info("request method: {}, request URI: {}, request headers: {}, request body: {}",
request.getMethod(), request.getURI(), request.getHeaders(), new String(body, Charset.forName("UTF-8")));
ClientHttpResponse response = execution.execute(request, body);
logger.info("response status code: {}, response headers: {}, response body: {}",
response.getStatusCode(), response.getHeaders(), new String(ByteStreams.toByteArray(response.getBody()), Charset.forName("UTF-8")));
return response;
}
}
With Spring-boot 1.5.10.RELEASE, I am getting response.body as null.
Here is how I am using RestTemplate
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new LoggingRequestInterceptor());
restTemplate.setInterceptors(interceptors);
String url = "http://someurl/Commands";
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("cmd", "{\"operation\":\"getSomeDetails\"}}");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
System.out.println("This is always null: " + response.getBody());
While above program always prints null,
following interceptor prints valid response body
public class LoggingRequestInterceptor implements ClientHttpRequestInterceptor {
final static Logger log = LoggerFactory.getLogger(LoggingRequestInterceptor.class);
#Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
traceResponse(response);
return response;
}
private void traceResponse(ClientHttpResponse response) throws IOException {
StringBuilder inputStringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
inputStringBuilder.append('\n');
line = bufferedReader.readLine();
}
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: {}", inputStringBuilder.toString());
log.debug("=======================response end=================================================");
}
}
Although the accepted answer has the reason, I believe the solution is also necessary.
Spring has a BufferingClientHttpRequestFactory that acts as a wrapper to Rest Template's default SimpleClientHttpRequestFactory.
It can be passed to a Rest Template during creation. This forces the Rest Template to make interceptors use a copy of the response rather than destroying it.
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
RestTemplate restTemplate = new RestTemplate(factory);
Source :
http://objectpartners.com/2018/03/01/log-your-resttemplate-request-and-response-without-destroying-the-body/
You're consuming the response body in traceResponse; that's your problem. Also, please update your question to be specific; "all latest" means nothing. What's latest today isn't so tomorrow.
Below code will resolve issue.
#Bean public RestTemplate restTemplate() {
final RestTemplate restTempate = new RestTemplate(new BufferingClientHttpRequestFactory(new
SimpleClientHttpRequestFactory()));
final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
interceptors.add(new LogHttpInterceptor());
restTempate.setInterceptors(interceptors);
return restTemplate;}
While log interceptor will be like below
public class LogHttpInterceptor implements ClientHttpRequestInterceptor {
final static Logger log = LoggerFactory.getLogger(LogHttpInterceptor.class);
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
traceRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
traceResponse(response);
return response;
}
private void traceRequest(HttpRequest request, byte[] body) throws IOException {
log.info("===========================================================================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.info("=============================================================================request end");
}
private void traceResponse(ClientHttpResponse response) throws IOException {
StringBuilder inputStringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), "UTF-8"));
String line = bufferedReader.readLine();
while (line != null) {
inputStringBuilder.append(line);
inputStringBuilder.append('\n');
line = bufferedReader.readLine();
}
log.info("==========================================================================response begin");
log.debug("Status code : {}", response.getStatusCode());
log.debug("Status text : {}", response.getStatusText());
log.debug("Headers : {}", response.getHeaders());
log.debug("Response body: {}", inputStringBuilder.toString());
log.info("===========================================================================response end");
}
Let me know if doesn't work
After some time of searching I tried to use HttpComponentsClientHttpRequestFactory() instead of SimpleClientHttpRequestFactory()
ClientHttpRequestFactory factory = new BufferingClientHttpRequestFactory(new HttpComponentsClientHttpRequestFactory());
That solved the issue for me.
Create your RestTemplate like this
#Bean
public RestTemplate interceptedRestTemplate() {
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(
new SimpleClientHttpRequestFactory()
));
restTemplate.setInterceptors(List.of(<i>your interceptor</i>));
return restTemplate;
}
worked for me.
I am writing a rest proxy (it exposes the API and delegates call to other server) and it works fine for the normal case and also for 500 http status code, we get the response from the rest client.
But when we get 404 status code, the Rest API server returns the message but we get junk values from the RestTemplate. We need to pass the same response to other API user but cannot get the same response.
Message returned from REST API Server:
{
"status_code":"0",
"error":{
"code":"404",
"description":"Source not found"
}
}
Getting the below response by RestTemplate client:
Not able to paste the content, attaching the screen shot of the response.
Please see the code below.
#RequestMapping(value = "/api/**")
public #ResponseBody String apiProxy(#RequestBody String body, HttpMethod method, HttpServletRequest request,
HttpServletResponse response) throws URISyntaxException {
RestTemplate restTemplate = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
restTemplate.setInterceptors(Collections.singletonList(new RestClientLoggingInterceptor()));
restTemplate.setErrorHandler(new CustomResponseErrorHandler());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
HttpHeaders httpHeaders = new HttpHeaders();
Enumeration<String> headers = request.getHeaderNames();
String headerName = null;
String headerValue = null;
while (headers.hasMoreElements()) {
headerName = headers.nextElement();
headerValue = request.getHeader(headerName);
httpHeaders.set(headerName, headerValue);
}
HttpEntity<String> httpEntity = new HttpEntity<String>(body, httpHeaders);
URI uri = new URI(ServerProtocol, null, ServerDomain, Integer.valueOf(ServerPort),
request.getRequestURI(), request.getQueryString(), null);
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(uri, method, httpEntity, String.class);
} catch (RestClientResponseException e) {
response.setStatus(e.getRawStatusCode());
return e.getResponseBodyAsString();
}
response.setStatus(responseEntity.getStatusCode().value());
return responseEntity.getBody();
}
ResponseErrorHandler Class
public class CustomResponseErrorHandler extends DefaultResponseErrorHandler {
private static final Logger logger = LogManager.getLogger(CustomResponseErrorHandler.class);
#Override
public void handleError(ClientHttpResponse response) throws IOException {
logger.error("Response error: {} {}", response.getStatusCode(), response.getStatusText());
}
}
RestClientLoggingInterceptor Class
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
ClientHttpResponse response = execution.execute(request, body);
logger.debug("request method:" + request.getMethod());
logger.debug("request URI:" + request.getURI());
logger.debug("request headers:" + request.getHeaders());
logger.debug("request body:" + new String(body, Charset.forName("UTF-8")));
logger.debug("response status code:" + response.getStatusCode());
logger.debug("response headers:" + response.getHeaders());
logger.debug("response body:" + IOUtils.toString(response.getBody(), "UTF-8"));
return response;
}
Thanks
Cannot parse gzip encoded response with RestTemplate from Spring-Web
This was helpful to me for this same issue. You can try this out.