Resttemplate - how to post object with HAL representation? - spring

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;
}

Related

Spring boot - corrupt authorization header value after RestTemplate.exchange()

I'm having a weird problem, i'm using tokens on Microservice enviroment, I need to call another service with the token already generated.
Call to other service on a Client class... There is the method.
return restTemplate.exchange(URL_REST_METHOD,
HttpMethod.GET,
httpEntity, //HEADER OK
SubscriptionDto.class,
subscriptionId);
To do things easy, I get the previous request, get the token and added to current request.
public class HeaderLoginInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpServletRequest previousRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if(previousRequest.getHeader(AUTHORIZATION) != null){
request.getHeaders().add(AUTHORIZATION, previousRequest.getHeader(AUTHORIZATION));
}
return execution.execute(request, body);
}
}
I attach this interceptor to the Bean RestTemplate
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderLoginInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
The weird thing, after the execution, I see a defect on Authorization header:
Correct one:
Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhdWQiOiJBZGlkYXMgQ2hhbGxlbmdlIiwic3...
Weird after:
Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJhdWQiOiJBZGlkYXMgQ2hhbGxlbmdlIiwic3...
I try adding the token directly before call exchange method, same result.
HttpServletRequest previousRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpHeaders headers = new HttpHeaders();
headers.addAll((MultiValueMap<String, String>) previousRequest.getHeaderNames());
HttpEntity<?> httpEntity = new HttpEntity<>(new HttpHeaders());
In both cases, after call method restTemplate.exchange(...) it corrupts.
Any ideas?
Thanks a lot
After a lot of research, the problem is related to charset of RestTemplate.
If I add this line on #Bean configuration, the problem is solved:
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
/*** THE SAVERY LINE ***/
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8")));
/*** ***/
List<ClientHttpRequestInterceptor> interceptors
= restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new HeaderLoginInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}
After that, works normally.

Override JSON properties in #RestController in Spring 4.3.3 WebMVC

I want to override the following properties in Spring RestController in WebMVC.
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.setDateFormat(DATEFORMAT);
I've tried the following in multiple combinations, but nothing worked. Still getting the null value without root name in the response.
#EnableWebMvc
public class ApplicationContextConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.setDateFormat(DATEFORMAT);
//jsonConverter.setObjectMapper(objectMapper);
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(objectMapper);
//jsonConverter.setJsonPrefix(jsonPrefix);
converters.add(jsonConverter);
}
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) converter;
ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.setDateFormat(DATEFORMAT);
break;
}
}
}
#Bean
#Primary
public ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.setDateFormat(DATEFORMAT);
return objectMapper;
}
}
Reponse:
{
"Message": null,
"Admin": null,
"Company": null
}
You should "substitute" the object mapper bean provided by spring with your own and configure it.
#Configuration
public class MyJacksonCustomConfiguration {
#Bean
public ObjectMapper objectMapper() { // note the name should be 'objectMapper'
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(Include.NON_NULL);
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
objectMapper.setDateFormat(DATEFORMAT);
return objectMapper;
}
}
All in all, make sure that only one bean of type object mapper should exist in the application context. Apart of fact that its a pretty "heavy" object, if spring will inject a wrong mapper you won't be able to benefit from customization that you've done.

How can i recive a row response as json string from RestTemplate

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.

Spring Boot integration test - TestRestTemplate how to set response Content-Type to UTF-8

I tried to integrate test Spring Boot application, now I got one issue
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.http.ResponseEntity] and content type [application/x-json;charset=iso-8859-1]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:109)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:917)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:901)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:655)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531)
at org.springframework.boot.test.web.client.TestRestTemplate.exchange(TestRestTemplate.java:735)
If I do below
#Before
public void init() {
List<HttpMessageConverter<?>> converters = restTemplate.getRestTemplate().getMessageConverters();
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonConverter = (MappingJackson2HttpMessageConverter) converter;
jsonConverter.setObjectMapper(new ObjectMapper());
jsonConverter.setSupportedMediaTypes(ImmutableList
.of(new MediaType("application", "x-json", Charset.forName("iso-8859-1"))));
}
}
}
I added the application/x-json and iso8859-1 support to MappingJackson2HttpMessageConverter, it can works well.
This is an workaround, but I want to know why the response content-type is application/x-json;charset=iso-8859-1? I have the produces configuration in my controller
#RequestMapping(method = RequestMethod.GET, value = "/{id}", produces = "application/json;charset=UTF-8")
Does someone know how to config it and let the response use "application/json;charset=UTF-8" content type?
Below is my test method
#Autowired
protected TestRestTemplate restTemplate;
#Test
public void testGetPerformanceObligationById() {
PerformanceObligationEntity entity = restTemplate.getForObject("/performance-obligations/{id}", PerformanceObligationEntity.class, "InvalidId");
Assert.assertNull(entity.getId());
}
I added one filter
#TestConfiguration
static class Config {
#Bean
public CharacterEncodingFilter characterEncodingFilter() {
final CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return characterEncodingFilter;
}
}
Also modify the converter to
#Before
public void init() {
List<HttpMessageConverter<?>> converters =
restTemplate.getRestTemplate().getMessageConverters();
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonConverter = (MappingJackson2HttpMessageConverter) converter;
jsonConverter.setObjectMapper(new ObjectMapper());
jsonConverter.setSupportedMediaTypes(ImmutableList
.of(new MediaType("application", "x-json", Charset.forName("UTF-8"))));
}
}
}
Now I got application/x-json;charset=utf-8, so just remain how to config from application/x-json to application/json

Exposing OAuth2RestTemplate as AsyncRestTemplate

I have a working OAuth2RestTemplate client (I'm using spring-security-oauth2 2.0.7.RELEASE). Now I'd like to expose/wrap it as AsyncRestTemplate to take advantage of asynchronous semantic of ListenableFuture. Unfortunately the following straightforward approach doesn't work:
// instantiate and configure OAuth2RestTemplate - works
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(...);
// wrap sync restTemplate with AsyncRestTemplate - doesn't work
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate(
new HttpComponentsAsyncClientHttpRequestFactory(), oAuth2RestTemplate);
How can I get OAuth2 Rest client for my HTTP service as AsyncRestTemplate?
Ok, I was able to make AsyncRestTemplate work by manually setting "Authorization" header with accessToken from OAuth2RestTemplate; here's Spring java configuration for that:
#Bean
public OAuth2RestTemplate restTemplate() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
// configure oauth details
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(details);
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
return restTemplate;
}
#Bean
public AsyncRestTemplate asyncRestTemplate(final OAuth2RestTemplate oAuth2RestTemplate) {
HttpComponentsAsyncClientHttpRequestFactory asyncRequestFactory = new HttpComponentsAsyncClientHttpRequestFactory() {
#Override
public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException {
AsyncClientHttpRequest asyncRequest = super.createAsyncRequest(uri, httpMethod);
OAuth2AccessToken accessToken = oAuth2RestTemplate.getAccessToken();
asyncRequest.getHeaders().set("Authorization", String.format("%s %s", accessToken.getTokenType(), accessToken.getValue()));
return asyncRequest;
}
};
return new AsyncRestTemplate(asyncRequestFactory, oAuth2RestTemplate);
}
I wish there would be easier way to expose configured OAuth2RestTemplate as AsyncRestTemplate in Spring.
The above works, but I've found a much neater way of doing it.
Register an implementation AsyncClientHttpRequestInterceptor
Example code:
private class Oauth2RequestInterceptor implements AsyncClientHttpRequestInterceptor
{
private final OAuth2RestTemplate oAuth2RestTemplate;
public Oauth2RequestInterceptor( OAuth2RestTemplate oAuth2RestTemplate )
{
this.oAuth2RestTemplate = oAuth2RestTemplate;
}
public ListenableFuture<ClientHttpResponse> intercept( HttpRequest request, byte[] body,
AsyncClientHttpRequestExecution execution ) throws IOException
{
OAuth2AccessToken accessToken = oAuth2RestTemplate.getAccessToken();
request.getHeaders()
.set( "Authorization", String.format( "%s %s", accessToken.getTokenType(), accessToken.getValue() ) );
return execution.executeAsync( request, body );
}
}
Then register it with your AsyncRestTemplate:
#Bean
public AsyncRestTemplate asyncRestTemplate( AsyncClientHttpRequestFactory factory, OAuth2RestTemplate restTemplate )
{
AsyncRestTemplate asyncRestTemplate = new AsyncRestTemplate( factory, restTemplate );
asyncRestTemplate.setInterceptors( Collections.singletonList( new Oauth2RequestInterceptor( restTemplate ) ) );
return asyncRestTemplate;
}

Resources