I am trying to get the actual json i recieve as response from rest service.
My problem is that i have multiple message converters configured for my RestTemplate like this.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
restTemplate.setMessageConverters(getMessageConverters());
return restTemplate;
}
private List<HttpMessageConverter<?>> getMessageConverters() {
List<HttpMessageConverter<?>> converters = new LinkedList<>();
StringHttpMessageConverter textConverter = new StringHttpMessageConverter();
converters.add(textConverter);
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
converters.add(jsonConverter);
HttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
converters.add(formHttpMessageConverter);
return converters;
}
Here is the how i try to retrive the string from the response.
ResponseEntity<String> message = null;
try {
message = restTemplate.postForEntity(
url,
new HttpEntity<>(payload, getHeaders()),
String.class);
return message.getBody();
} catch (HttpClientErrorException e) {
log.error(e.getResponseBodyAsString(), e);
}
My guess is that because i have a Mapping2Jackson2HttpMessageConverter configured, each time i recieve message with content type application/json, it tries to convert it using json converter even if response type class is specified as String.
Related
I am trying to POST a payload with almost 4-5 MB of data through RestTemplate. Some of the request are passing but some of the requests are not even going to API gateway with this error
org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting
for connection from pool
Here is the rest template implementation I am using.
#Component
public class RestTemplateHelper {
#Autowired
private RestTemplate restTemplate;
public RestTemplateHelper() {
}
private <X, Y> Y post(final String url, final String accessToken, final MediaType mediaType, final X requestBody, final Class<Y> responseType, final Map<String, String> extraHeaders, HttpMessageConverter httpMessageConverter, RestTemplate restTemplateFromParam) {
HttpHeaders headers = this.getHeaders(accessToken, mediaType, extraHeaders);
HttpEntity<X> httpEntity = new HttpEntity(requestBody, headers);
ResponseEntity responseEntity;
NonRetriableException nonRetriableException;
try {
RestTemplate restTemplateToUse = this.restTemplate;
if (Objects.nonNull(httpMessageConverter)) {
restTemplateToUse = Objects.nonNull(restTemplateFromParam) ? restTemplateFromParam : restTemplateToUse;
restTemplateToUse.getMessageConverters().add(0, httpMessageConverter);
}
responseEntity = restTemplateToUse.exchange(url, HttpMethod.POST, httpEntity, responseType, new Object[0]);
} catch (ResourceAccessException var14) {
nonRetriableException = new NonRetriableException(var14);
nonRetriableException.setRootException(var14);
throw nonRetriableException;
} catch (RestClientResponseException var15) {
nonRetriableException = new NonRetriableException(var15);
nonRetriableException.setRawStatusCode(var15.getRawStatusCode());
nonRetriableException.setStatusText(var15.getStatusText());
nonRetriableException.setResponseBody(var15.getResponseBodyAsString());
nonRetriableException.setResponseHeaders(var15.getResponseHeaders());
nonRetriableException.setRootException(var15);
throw nonRetriableException;
} catch (Exception var16) {
throw new NonRetriableException(var16);
}
return responseEntity.getBody();
}
}
So I have an API request where I am copying the details directly from postman where it works. I am however getting a bad request error.
#Service
public class GraphApiService {
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
#Autowired
RestTemplate restTemplate;
#Autowired
Constants constants;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public ResponseEntity<String> getAccessTokenUsingRefreshToken(Credential cred) throws IOException{
try {
//https://learn.microsoft.com/en-us/graph/auth-v2-user
// section 5. Use the refresh token to get a new access token
String url = "url";
JSONObject body = new JSONObject();
body.put("grant_type", "refresh_token");
body.put("client_id", "clientid");
body.put("scope","User.Read offline_access Files.Read Mail.Read Sites.Read.All");
body.put("redirect_uri", "http://localhost");
body.put("client_secret","secret");
body.put("refresh_token", "token");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<String> request = new HttpEntity<String>(body.toString(), headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, request,String.class);
return response;
}
catch(HttpClientErrorException e){
logger.error(e.getResponseBodyAsString());
logger.error(e.getMessage());
return null;
}
}
I would appreciate any help. The bad request error message from microsoft graph isn't a descriptive one that will help
You're sending JSON payload with FORM_URLENCODED header.
Either you need to check if API accepts json payload, if so you need to change content-type to application/json or you can post form data as follows.
public ResponseEntity<String> getAccessTokenUsingRefreshToken(Credential cred) throws IOException{
try {
//https://learn.microsoft.com/en-us/graph/auth-v2-user
// section 5. Use the refresh token to get a new access token
String url = "url";
MultiValueMap<String, String> multiValueMap= new LinkedMultiValueMap<String, String>();
multiValueMap.add("grant_type", "refresh_token");
multiValueMap.add("client_id", "clientid");
//.....
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(multiValueMap, headers);
ResponseEntity<String> response= restTemplate.postForEntity(url, request, String.class);
return response;
}catch(HttpClientErrorException e){
logger.error(e.getResponseBodyAsString());
logger.error(e.getMessage());
return null;
}
}
I'm trying to integrate my app with ebay Finding API, after getting familiar with api, I tried making some requests to see if it's working as expected, but one thing got me stuck. The ebay service even though I explicitly set RESPONSE-DATA-FORMAT to JSON, returns response in json format but the content-type is text/plain. I set my restTemplate message converters as follows:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper)
{
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(objectMapper);
messageConverter.setSupportedMediaTypes(
ImmutableList
.of(
new MediaType("application", "json", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET),
new MediaType("text", "plain", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET)
));
return messageConverter;
}
#Bean
RestTemplate restTemplate(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter)
{
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(Collections.singletonList(mappingJackson2HttpMessageConverter));
return new RestTemplate();
}
Even though when I try to deserialize response which looks like this:
json data
And object:
public class Response
{
private String version;
public Response()
{
}
public String getVersion()
{
return version;
}
public void setVersion(String version)
{
this.version = version;
}
}
My Api call:
String url = uriBuilder.formEndpoint("iphone").toString();
Response response = restTemplate.getForObject(url, Response.class);
Is finishing with exception like this:
Could not extract response: no suitable HttpMessageConverter found for
response type [class com.domain.Response]
and content type [text/plain;charset=UTF-8]
I'm getting this json response with some timeout message in the beginning and the class throwing Not valid jSON exception.
Json Response
failed to connect: timeout I should not be here {Valid json}
but when I test through postman , I don't see that frustrating message.
I'm using spring RestTemplate` to call the service
final HttpEntity<String> entity = new HttpEntity<String>(headers);
final ResponseEntity<String> exchange = restTemplate.exchange(uri.toUriString(),
HttpMethod.GET, entity, String.class);
Even though I increase timeout values, it's the same
#PostConstruct
public void init() {
restTemplate = new RestTemplate(clientHttpRequestFactory());
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
protected boolean hasError(HttpStatus statusCode) {
return false;
}
});
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(10000);
factory.setConnectTimeout(10000);
return factory;
}
This is my camel route
from("direct:processRequest").process(new RecipientListBean()).recipientList(header("recipients"))
.aggregationStrategy(aggregationStrategy)
.parallelProcessing()
.to("bean:transformerBean");
When attempting to post to a Spring-Data-Rest web service via RestTemplate, the JSON representation of my domain object is being converted to a full blown JSON object that isn't in HAL representation. My assumption here is that I need to register the Jackson2HalModule as a deserializer though am not sure how to do that considering I register it to the objectMapper. The serialization works correctly when calling GET on the webservice, just not for POST/PUT:
Request outputBuffer field:
{
"id" : 1,
"name" : "Name",
"description" : "",
"childObject" : {
"id" : 1,
"name" : "test"
}
}
Rest Template configuration:
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JodaModule());
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
objectMapper.setDateFormat(new ISO8601DateFormat());
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.registerModule(new Jackson2HalModule());
return objectMapper;
}
public void configureMessageConverters(
List<HttpMessageConverter<?>> messageConverters) {
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(objectMapper());
jsonMessageConverter.setSupportedMediaTypes(MediaType
.parseMediaTypes("application/hal+json,application/json"));
messageConverters.add(jsonMessageConverter);
}
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
configureMessageConverters(messageConverters);
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}
Request Headers:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
Calling method:
ResponseEntity<DomainObject> responseEntity =
restTemplate.exchange(this.getBaseUri() + resolveResource(), HttpMethod.POST, new HttpEntity(domainObject,createHttpHeaders(tenantId)), DomainObject.class);
I think you should not register your own ObjectMapper. Spring creates it for you and also registers all the modules needed. So I would just try to remove your ObjectMapper bean.
If you need to customize the ObjectMapper you could use a Jackson2ObjectMapperBuilder. See the documentation for more details - http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper
#Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return Jackson2ObjectMapperBuilder //
.json() //
.locale(ENGLISH) //
.timeZone("UTC") //
.indentOutput(true) //
.serializationInclusion(NON_NULL) //
.featuresToDisable(WRITE_DATES_AS_TIMESTAMPS, FAIL_ON_UNKNOWN_PROPERTIES) //
;
}
I would also let spring take care of the message converters:
So let spring inject them when you create the RestTemplate - so something like this:
#Bean
public RestTemplate restTemplate(List<HttpMessageConverter<?>> messageConverters) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}