web-dev-qa-db-ja.com

Spring REST Controller content / typeは、すべてのコンテンツタイプでサポートされているわけではありません

後でiTextでPDFを作成するために使用されるHTMLを使用するWebサービスを作成しようとしています。

リクエストをサーバーに送信するためにPostmanを使用していますが、選択するコンテンツタイプに関係なく、次のエラーが発生するたびに:

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

メッセージは、選択したコンテンツタイプによって異なります。

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

これは私のエンドポイントです:

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

consumes属性を次のように変更しました:

  • MediaType.TEXT_PLAIN_VALUE
  • MediaType.TEXT_XML_VALUE

postmanで送信するものと一致させます。

@dnaultコメントと同様に、RequestMappingアノテーションから完全に削除すると、同じ結果になります。

私は同様の質問を調べました:

しかし、上記のどれも、また私がチェックした私の問題に実際にはあまり関係のない他のいくつかは、この問題を解決する答えをすでに提供していません。

サーバーに送信しようとしているサンプルHTMLは次のとおりです。

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

私たちの設定は次のようにJava設定クラスによって行われます:

@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

このため

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

Spring MVCは、他の方法では登録されていたであろうすべてのデフォルトのコンバーターをスキップします。 (もし興味があれば、これはWebMvcConfigurationSupport#getMessageConverters(..)で行われます。)

あなたの唯一のHttpMessageConverter、_MappingJackson2HttpMessageConverter_は、_MediaType.APPLICATION_JSON_のコンテンツのみを読み取ることができます。 _application/json_。したがって、他のすべての要求コンテンツタイプは拒否されます。

configureMessageConvertersオーバーライド(またはHTMLフォーム、XML、プレーンテキストなどを読み取るために必要なもののみ)に、すべての通常のデフォルトを自分で登録できます。

または、代わりにextendMessageConvertersをオーバーライドしてデフォルトの_MappingJackson2HttpMessageConverter_インスタンスを検索し、カスタムObjectMapperを使用するように構成できます。例えば、

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

そして、コンバーターのデフォルトのリストに依存することについてのコメントをドロップするかもしれません。

私は同じエラーを抱えていましたが、OPの問題で言及されているケースは私のものとは少し異なることに気づきました。私の場合、別のObjectMapperを使い始めた後に表示されました。

最初は私が使っていた

プロジェクトのObjectMapperとして「com.fasterxml.jackson.core.databind」。

私はいくつかのコードをリファクタリングし、その過程でObjectMapperのソースとしてorg.codehaus.jacksonに切り替えました。

OPと同じエラーメッセージを持つContent-Typeに関係なく、すべての要求が拒否され、Springはすべての要求を拒否します。

これを整理するのに3日かかりましたが、最後にObjectMapperを「データバインド」に切り替えました。その後、すべてが魔法のように機能しました。

誰かを助けるために、ここでこれについて言及しています。 MessageConvertersや複雑なものには触れていなかったOPのラインを外します。

1
user2076066