Spring rest template throws me the following exeption
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class [Lcom.flightsms.core.dto.AirlineResponseDTO;] and content type [text/html;charset=UTF-8]
Here my json response
[
{
"airlineId": "1",
"nameAirline": "American Airlines",
"codeIataAirline": "AA",
"iataPrefixAccounting": "1",
"codeIcaoAirline": "AAL",
"callsign": "AMERICAN",
"type": "scheduled",
"statusAirline": "active",
"sizeAirline": "963",
"ageFleet": "10.9",
"founding": "1934",
"codeHub": "DFW",
"nameCountry": "United States",
"codeIso2Country": "US"
}
]
dto class
#Data
public class AirlineResponseDTO {
private String airlineId;
private String nameAirline;
private String codeIataAirline;
private String iataPrefixAccounting;
private String codeIcaoAirline;
private String callsign;
private String type;
private String statusAirline;
private String sizeAirline;
private String ageFleet;
private String founding;
private String codeHub;
private String nameCountry;
private String codeIso2Country;
}
I suspect that the matter is in the converter. I changed the converter configuration but this did not work
#Configuration
public class MvcConfigSupport extends WebMvcConfigurationSupport {
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
addDefaultHttpMessageConverters(converters);
}
#Bean
MappingJackson2HttpMessageConverter converter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList(MediaType.TEXT_HTML));
return converter;
}
}
Problem solved.
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_HTML));
restTemplate.getMessageConverters().add(converter);
return restTemplate;
}
Try something like that the default converters are not registered by default and an end user has to be explicit about registering the defaults :
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter =
new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>>
converters) {
converters.add(customJackson2HttpMessageConverter());
super.addDefaultHttpMessageConverters();
}
}
Moreover please try fix type exception. Content type [text/html;charset=UTF-8] received from the service, the real content type should be application/json;charset=UTF-8
To get all ContentType you can do your custom converter like that:
List<HttpMessageConverter<?>> messageConverters =
new ArrayList<HttpMessageConverter<?>>();
MappingJackson2HttpMessageConverter converter =
new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(Arrays.asList({MediaType.ALL}));
messageConverters.add(converter);
restTemplate.setMessageConverters(messageConverters);
Use RestTemplateBuilders to create the resttemplate clients. The builders will add the default Message converters.
Related
I am trying to send an event using RabbitMQ and SpringBoot.
#Configuration class:
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
Event class:
public class TheEvent{
#JsonFormat(pattern = "dd::MM::yyyy")
private LocalDateTime date;
//setters getters
}
When I send it, it arrives as:
{"month":"JULY","year":2018,"dayOfMonth":12,"dayOfWeek":"THURSDAY","dayOfYear":193,"hour":16,"minute":29,"nano":835000000,"second":24,"monthValue":7,"chronology":{"id":"ISO","calendarType":"iso8601"}},"direction":1}"
How can I serialize this date object in predefined pattern? (Remember that I just registering bean Jackson2JsonMessageConverter)
Also tried this:
#Bean
#Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
return objectMapper;
}
It works for me:
#Bean
public Jackson2JsonMessageConverter converter(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
return new Jackson2JsonMessageConverter(objectMapper);
}
In project I used RabbitListener and that Jackson2JsonMessageConverter
Listener:
#RabbitListener(queues = "${spring.rabbitmq.queue}")
#Transactional
public void receiveSocialPost(SocialPost socialPost) {
}
Let me thank you in advance for your help!
I have a weird behaviour in an spring boot application. Let me explain it for you:
I'm wrapping some legacy web services (custom xml messages) with some nice rest-json services (via spring-mvc and spring boot and using jackson for serializing stuff)
In order to communicate with the legacy systems, I have created a custom XmlMapper, serializers and deserializers.
And finally, I have created an httpclientconfig, in order to define some http connection properties...
But after starting the app and trying to visit any endpoint (actuator ones for example), the app only returns xml. Event swagger endpoints return xml (what makes swagger-ui going nuts.
These are some of the classes:
#Configuration
public class HttpClientConfig {
private static final Logger logger = LoggerFactory.getLogger(HttpClientConfig.class);
#Value(value = "${app.http.client.max_total_connections}")
public String MAX_TOTAL_CONNECTIONS;
#Value(value = "${app.http.client.max_connections_per_route}")
public String MAX_CONNECTIONS_PER_ROUTE;
#Value(value = "${app.http.client.connection_timeout_milliseconds}")
public String CONNECTION_TIMEOUT_MILLISECONDS;
#Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
#Autowired
private XmlMapper xmlMapper;
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate(httpRequestFactory());
List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonConverter = (MappingJackson2HttpMessageConverter) converter;
jsonConverter.setObjectMapper(new ObjectMapper());
}
if (converter instanceof MappingJackson2XmlHttpMessageConverter) {
MappingJackson2XmlHttpMessageConverter jsonConverter = (MappingJackson2XmlHttpMessageConverter) converter;
jsonConverter.setObjectMapper(xmlMapper);
}
}
logger.debug("restTemplate object created====================================");
return restTemplate;
}
#Bean
public HttpClient httpClient() {
HttpClient httpClient = null;
try {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
// disable SSL check
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
#Override
public boolean isTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws CertificateException {
return true;
}
}).build();
httpClientBuilder.setSSLContext(sslContext);
// don't check Hostnames
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory).build();
PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connMgr.setMaxTotal(Integer.parseInt(MAX_TOTAL_CONNECTIONS));
connMgr.setDefaultMaxPerRoute(Integer.parseInt(MAX_CONNECTIONS_PER_ROUTE));
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(Integer.parseInt(CONNECTION_TIMEOUT_MILLISECONDS)).build();
httpClientBuilder.setDefaultRequestConfig(config);
httpClientBuilder.setConnectionManager(connMgr);
// to avoid nohttpresponse
httpClientBuilder.setRetryHandler(new HttpRequestRetryHandler() {
#Override
public boolean retryRequest(IOException exception, int executionCount,
org.apache.http.protocol.HttpContext context) {
// TODO Auto-generated method stub
return true;
}
});
httpClient = httpClientBuilder.build();
} catch (Exception e) {
logger.error("Excption creating HttpClient: ", e);
}
return httpClient;
}
}
And the xml mapper
#Configuration
public class XmlMapperConfig{
#Bean
public XmlMapper getXmlMapper() {
XmlMapper mapper=new XmlMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(CafRequestObject.class, new CafRequestObjectSerializer());
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.registerModule(module);
mapper.findAndRegisterModules();
CafXmlSerializationProvider cafXmlProvider=new CafXmlSerializationProvider(new XmlRootNameLookup());
mapper.setSerializerProvider(cafXmlProvider);
return mapper;
}
}
I call to findAndregisterModules, because I am also developing some libraries which provides additional serializers for services (modularized stuff)
I'm completely lost with this. Any help would be much appreciated...
Regards!
I have solved it extending WebMvcConfigurerAdapter:
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry
.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Thanks again!
I'm facing an issue after adding Hibernate4Module to support lazy-objects serialization.
My configuration file:
#EnableWebMvc
#ServletComponentScan(basePackages = "my.backend")
#EnableAutoConfiguration(exclude = MultipartAutoConfiguration.class)
#Configuration
#EnableSwagger2
public class MyConfiguration extends WebMvcConfigurerAdapter{
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).
favorParameter(true).
parameterName("mediaType").
ignoreAcceptHeader(true).
useJaf(false).
defaultContentType(MediaType.APPLICATION_JSON).
mediaType("xml", MediaType.APPLICATION_XML).
mediaType("json", MediaType.APPLICATION_JSON);
}
/* Here we register the Hibernate4Module into an ObjectMapper, then set this custom-configured ObjectMapper
* to the MessageConverter and return it to be added to the HttpMessageConverters of our application*/
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper current = messageConverter.getObjectMapper();
//Registering Hibernate4Module to support lazy objects
current.registerModule(new Hibernate4Module());
messageConverter.setObjectMapper(current);
return messageConverter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//Here we add our custom-configured HttpMessageConverter
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
}
Hibernate entities get serializied ok.
The problem is with plain jsons, that where stored in DB or generated in any other way.
For example
String myJson = {"myField":"2123456"}
Is returned in mailformed format
"{\"myField\":"2123456"}
Looks like some default modules get broken. Can anyone give a piece of advice?
I'm struggling with jackson-dataformat-csv to make it include CSV headers.
Right now I am able to output collections of entities (List) but the headers are not there, which make the file impossible to parse because columns doesn't have a title.
My MVC config (shortened) :
#Configuration
#EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private Environment env;
public static final String JSON_OBJECT_MAPPER_NAME = "json";
public static final String CSV_OBJECT_MAPPER_NAME = "csv";
private static final TimeZone OUTPUT_DATE_TIMEZONE = TimeZone.getTimeZone(ZoneOffset.UTC);
private static final DateFormat OUTPUT_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
#Autowired
#Qualifier(value = JSON_OBJECT_MAPPER_NAME)
private ObjectMapper jsonObjectMapper;
#Autowired
#Qualifier(value = CSV_OBJECT_MAPPER_NAME)
private ObjectMapper csvObjectMapper;
public MvcConfig() {
super();
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true);
configurer.ignoreAcceptHeader(false);
configurer.defaultContentType(MediaType.APPLICATION_JSON);
configurer.useJaf(false);
final Map<String,MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("html", MediaType.TEXT_HTML);
mediaTypes.put("json", MediaType.APPLICATION_JSON);
mediaTypes.put("csv", new MediaType("text","csv", Charset.forName("utf-8")));
configurer.mediaTypes(mediaTypes);
}
#Bean(name = JSON_OBJECT_MAPPER_NAME)
#Primary
public ObjectMapper jsonObjectMapper(){
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.indentOutput(Boolean.parseBoolean(env.getProperty("jsonPrettyPrint")));
builder.timeZone(OUTPUT_DATE_TIMEZONE);
builder.dateFormat(OUTPUT_DATE_FORMAT);
return builder.build();
}
#Bean(name = CSV_OBJECT_MAPPER_NAME)
public ObjectMapper csvObjectMapper(){
CsvMapper csvMapper = new CsvMapper();
//csvMapper.enable(CsvParser.Feature.WRAP_AS_ARRAY);
csvMapper.setTimeZone(OUTPUT_DATE_TIMEZONE);
csvMapper.setDateFormat(OUTPUT_DATE_FORMAT);
csvMapper.registerModule(new CsvMappingModule());
csvMapper.registerModule(new JodaModule());
return csvMapper;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(createJsonHttpMessageConverter());
converters.add(createCsvHttpMessageConverter());
}
private HttpMessageConverter<Object> createJsonHttpMessageConverter() {
return createHttpMessageConverter(jsonObjectMapper, MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8);
}
private HttpMessageConverter<Object> createCsvHttpMessageConverter() {
return createHttpMessageConverter(csvObjectMapper, TEXT_CSV);
}
private HttpMessageConverter<Object> createHttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes){
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper);
converter.setSupportedMediaTypes(Lists.newArrayList(supportedMediaTypes));
return converter;
}
}
A controller that ouput a list of values :
#Controller
#RequestMapping("/api/history")
public class HistoricController {
#Autowired
public IHistoryService historyService;
#Autowired
public IThingService thingService;
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
#ResponseBody
public List<HistoryDTO> findHistory(#PathVariable("id") Long thingId){
Thing thing = thingService.find(thingId);
return historyService.findByThing(thing);
}
}
I am able to return in JSON format:
[
{
"location": {
"id": 101483,
"name": "City A"
},
"dateEnteredLocation": "2016-06-06T18:44:03.000Z",
"dateLeavingLocation": "2016-06-13T13:02:34.000Z"
},
{
"location": {
"id": 101483,
"name": "City A"
},
"dateEnteredLocation": "2016-06-13T16:02:34.000Z",
"dateLeavingLocation": "2016-06-15T11:54:57.000Z"
},
{
"location": {
"id": 101485,
"name": "City C"
},
"dateEnteredLocation": "2016-06-16T04:05:06.000Z",
"dateLeavingLocation": "2016-06-16T11:34:58.000Z"
}
]
But when I try to use the CSV format I obtain :
2016-06-06T18:44:03.000Z,2016-06-13T13:02:34.000Z,101483,City A
2016-06-13T16:02:34.000Z,2016-06-15T11:54:57.000ZZ,101483,City A
2016-06-16T04:05:06.000Z,2016-06-16T11:34:58.000Z,101485,City C
So the format in CSV, it's fine. But there are no headers included.
I need headers to make the file understandable by humans or machines.
How can I make jackson csv mapper to automatically include headers. The headers names should be the same as the ones used for Json (which uses Entity #Json... annotations) ?
I want to keep it as generic as possible. I don't want to have to writer specific MVC controllers for CSV.
The mechanism Jackson uses is that CsvSchema used needs to have header enabled, so something like:
CsvMapper mapper = new CsvMapper();
CsvSchema schema = CsvSchema.emptySchema().withHeader();
String csv = mapper.writer(schema).writeValueAsString(...);
the challenge then is that of how to pass CsvSchema to use; or possible CsvWriter configured to use one. I am not sure if Spring MVC has explicit support for doing that.
In Spring boot 1.2.5 with a Jersey 2 interface, how can I set the JSON marshaller to not include fields that have null values?
For example:
[
{
"created": 1433987509174,
"lastModified": 1433876475580,
"id": 1,
"example": "example1b"
},
{
"created": 1434502031212,
"lastModified": 1434502031212,
"id": 10000,
"example": "example1c"
},
{
"created": 1439151444176,
"lastModified": 1439151444176,
"id": 10011,
"example": null
}
]
The field "example": null should not be included in the json output at all, but here it is specifying it is null.
In my #SpringBootApplication class, I've tried adding:
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
converter.setObjectMapper(objectMapper);
return converter;
}
or
#Bean
#Primary
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return builder;
}
or
#Primary
#Bean
public ObjectMapper mapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
and/or adding #JsonSerialize(include = Inclusion.NON_NULL) to the Object itself
But it still produces the same response above with the "example": null included.
It was working on Spring 3.0.7 with #JsonSerialize(include=Inclusion.NON_NULL) but that no longer works now that I've ported to Spring Boot 1.2.5.
I believe I've followed the documentation http://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper and it's not working so I'm hoping someone might see something I'm missing? Thanks in advance!
Edit: Also just tried adding the class:
#Configuration
public class WebConfiguration extends WebMvcAutoConfiguration {
#Primary
#Bean
public ObjectMapper mapper() {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
}
Solution:
package com.my.spring;
import javax.ws.rs.ext.ContextResolver;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletProperties;
import org.springframework.context.annotation.Configuration;
import com.my.spring.service.rs.MyRestServiceImpl;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
#Configuration
public class JerseyConfiguration extends ResourceConfig {
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
public JerseyConfiguration() {
register(new ObjectMapperContextResolver());
register(MyRestServiceImpl.class); // My jax-rs implementation class
property(ServletProperties.FILTER_FORWARD_ON_404, true); // Not needed for this non_null issue
}
}
I don't know about mixing the Spring way (of configuring the mapper) and how Jersey handles this. But the Jersey way to configure the ObjectMapper is through a ContextResolver, as seen in this answer.
Then register the ObjectMapperContextResolver with your Jersey configuration.
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
...
register(ObjectMapperContextResolver.class);
}
}
Or if you are package scanning, the #Provider annotation will pick up the class.