I'm trying to create a Web Service that consumes an HTML which is later used to create a PDF with iText.
I'm using Postman in order to send my requests to server but everytime I get the following error, no matter which content type I select:
{
"status": "CONFLICT",
"code": 40902,
"message": "Content type 'text/plain' not supported",
"developerMessage": "null",
"moreInfoUrl": "class org.springframework.web.HttpMediaTypeNotSupportedException",
"throwable": null
}
The message changes depending on the content type selected:
"message": "Content type 'text/xml' not supported"
"message": "Content type 'text/html' not supported"
This is my endpoint:
@RequestMapping(path = "/reports/pdf", method = RequestMethod.POST, consumes = MediaType.TEXT_HTML_VALUE)
public ResponseEntity<byte[]> generatePdfReport(@RequestBody String html) throws Exception {
byte[] pdfBytes = reportsService.generatePdf(html);
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(pdfBytes, HttpStatus.OK);
return response;
}
I've changed consumes
attribute to:
MediaType.TEXT_PLAIN_VALUE
MediaType.TEXT_XML_VALUE
to match what I'm sending on Postman.
As well as per @dnault comment, removing it completely from the RequestMapping
annotation with the same results.
I've looked into similar questions:
However none of the above and some others that aren't really close to my problem that I've checked already provide an answer that solves this issue.
The sample HTML I'm trying to send to server is:
<table style="border: 1px solid black; font-size: 12px; margin-top: 1px; width: 900px;" id="table_direction">
<tr>
<td width="33%" colspan="2">
<strong>Data1</strong>
</td>
<td width="33%" colspan="2">
<strongData2</strong>
</td>
<td width="16%" colspan="1">Foo</td>
<td width="16%" colspan="1">Bar</td>
</tr>
<tr>
<td colspan="">ID</td>
<td colspan="">123456</td>
<td colspan="">Property 1</td>
<td colspan="">Foo</td>
<td colspan="">Property 2</td>
<td colspan="">Bar</td>
</tr>
</table>
Our configuration is made by Java configuration classes which are as follows:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.company.project")
@PropertySource("classpath:application.properties")
@EnableTransactionManagement // proxyTargetClass = true
@EnableJpaRepositories(basePackages = { "com.company.project.dao", "com.company.project.repository" })
public class HelloWorldConfiguration extends WebMvcConfigurerAdapter {
@Inject
Environment env;
@Value("${jdbc.user}")
private String userDB;
@Value("${jdbc.password}")
private String passDB;
@Value("${jdbc.url}")
private String urlDB;
@Value("${jdbc.driverClassName}")
private String driverClassName;
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public CommonsMultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
@Bean(name = "dataSource")
public DriverManagerDataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(driverClassName);
driverManagerDataSource.setUrl(urlDB);
driverManagerDataSource.setUsername(userDB);
driverManagerDataSource.setPassword(passDB);
return driverManagerDataSource;
}
@Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.company.project.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactoryBean.setPackagesToScan("com.company.project.model");
entityManagerFactoryBean.setJpaProperties(hibernateProperties());
return entityManagerFactoryBean;
}
private Properties hibernateProperties() {
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
jpaProperties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
jpaProperties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
return jpaProperties;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
@Bean(name = "transactionManager2")
@Autowired
@Named("transactionManager2")
public HibernateTransactionManager transactionManager2(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
return vendorAdapter;
}
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
Hibernate5Module module = new Hibernate5Module();
module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
mapper.registerModule(module);
messageConverter.setObjectMapper(mapper);
return messageConverter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
}
@ControllerAdvice
public class GenericRepositoryRestExceptionHandler extends RepositoryRestExceptionHandler {
@Autowired
public GenericRepositoryRestExceptionHandler(MessageSource messageSource) {
super(messageSource);
// TODO Auto-generated constructor stub
}
@ResponseBody
@ExceptionHandler(Exception.class)
ResponseEntity<?> handleException(Exception e) {
// return response(HttpStatus.CONFLICT, 40902, e.getMessage());
return response(HttpStatus.CONFLICT, 40902, e.getMessage(), e.getCause() + "", e.getClass() + "");
}
private ResponseEntity<RestError> response(HttpStatus status, int code, String msg) {
return response(status, code, msg, "", "");
}
private ResponseEntity<RestError> response(HttpStatus status, int code, String msg, String devMsg, String moreInfo) {
return new ResponseEntity<>(new RestError(status, code, msg, devMsg, moreInfo, null), status);
}
}
public class CORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println("Filtering on...........................................................");
SecurityContext ctx = SecurityContextHolder.getContext();
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
Because of this
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
Spring MVC skips all the default converters it would have otherwise registered. (If you're curious, this is done in WebMvcConfigurationSupport#getMessageConverters(..)
.)
Your only HttpMessageConverter
, MappingJackson2HttpMessageConverter
, can only read MediaType.APPLICATION_JSON
content, ie. application/json
. Therefore, every other request content type will be rejected.
You can register all the regular defaults yourself in your configureMessageConverters
override (or just the ones you need to read HTML forms, XML, plain text, etc.).
Or, you could instead override extendMessageConverters
to find the default MappingJackson2HttpMessageConverter
instance and configure it to use your custom ObjectMapper
. For example,
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper mapper = new ObjectMapper();
// ...initialize...
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter m = (MappingJackson2HttpMessageConverter) converter;
m.setObjectMapper(mapper);
}
}
}
And maybe drop a comment about relying on the default list of converters.