web-dev-qa-db-fra.com

Spring REST Contenu / type de contrôleur non pris en charge pour tous les types de contenu

J'essaie de créer un service Web qui utilise un code HTML qui est ensuite utilisé pour créer un PDF avec iText.

J'utilise Postman afin d'envoyer mes demandes au serveur mais à chaque fois que j'obtiens l'erreur suivante, quel que soit le type de contenu que je sélectionne:

{
    "status": "CONFLICT",
    "code": 40902,
    "message": "Content type 'text/plain' not supported",
    "developerMessage": "null",
    "moreInfoUrl": "class org.springframework.web.HttpMediaTypeNotSupportedException",
    "throwable": null
}

Le message change en fonction du type de contenu sélectionné:

"message": "Content type 'text/xml' not supported"
"message": "Content type 'text/html' not supported"

Ceci est mon point final:

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

J'ai changé l'attribut consumes en:

  • MediaType.TEXT_PLAIN_VALUE
  • MediaType.TEXT_XML_VALUE

pour correspondre à ce que j'envoie sur Postman.

Ainsi que par le commentaire @dnault, supprimez-le complètement de l'annotation RequestMapping avec les mêmes résultats.

J'ai examiné des questions similaires:

Cependant, aucun des éléments ci-dessus et certains autres qui ne sont pas vraiment proches de mon problème que j'ai vérifié ne fournissent déjà une réponse qui résout ce problème.

L'exemple HTML que j'essaie d'envoyer au serveur est:

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

Notre configuration est faite par Java classes de configuration qui sont comme suit:

@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() {}

}
4
Frakcool

À cause de ce

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(jacksonMessageConverter());
    super.configureMessageConverters(converters);
}

Spring MVC ignore tous les convertisseurs par défaut qu'il aurait autrement enregistrés. (Si vous êtes curieux, cela se fait dans WebMvcConfigurationSupport#getMessageConverters(..).)

Votre seul HttpMessageConverter, MappingJackson2HttpMessageConverter, Ne peut lire que le contenu de MediaType.APPLICATION_JSON, C'est-à-dire. application/json. Par conséquent, tout autre type de contenu de demande sera rejeté.

Vous pouvez enregistrer vous-même tous les paramètres par défaut dans votre configureMessageConverters surcharge (ou uniquement ceux dont vous avez besoin pour lire les formulaires HTML, XML, texte brut, etc.).

Vous pouvez également remplacer extendMessageConverters pour trouver l'instance MappingJackson2HttpMessageConverter Par défaut et la configurer pour utiliser votre ObjectMapper personnalisé. Par exemple,

public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    ObjectMapper mapper = new ObjectMapper();
    // ...initialize...

    for (HttpMessageConverter<?> converter : converters) {
        if (converter instanceof MappingJackson2HttpMessageConverter) {
            MappingJackson2HttpMessageConverter m = (MappingJacksonHttpMessageConverter) converter;
            m.setObjectMapper(mapper);
        }
    } 
}

Et peut-être laisser un commentaire sur la façon de s'appuyer sur la liste par défaut des convertisseurs.

7

Je me rends compte que le cas mentionné dans le problème du PO était légèrement différent du mien, bien que j'aie eu la même erreur. Dans mon cas, il est apparu après avoir commencé à utiliser un autre ObjectMapper.

Au départ, j'utilisais le

"com.fasterxml.jackson.core.databind" comme ObjectMapper dans mon projet.

J'ai refactorisé du code et, dans le processus, je suis passé à org.codehaus.jackson comme source pour l'ObjectMapper.

C'est alors que l'enfer s'est déchaîné, et Spring rejetterait simplement chaque demande, quel que soit le type de contenu avec le même message d'erreur que l'OP.

Il m'a fallu 3 jours pour régler ce problème, mais finalement je suis simplement revenu à "databind" pour l'ObjectMapper. Tout a fonctionné comme par magie après cela.

Je le mentionne ici, au cas où cela aiderait quelqu'un. Détachez l'OP que je n'avais pas touché MessageConverters ou quelque chose de compliqué.

1
user2076066