Spring REST 控制器 content/type 并非所有内容类型都支持
Spring REST Controller content/type not supported for all content types
我正在尝试创建一个使用 HTML 的 Web 服务,稍后用于使用 iText 创建 PDF。
我正在使用 Postman 将我的请求发送到服务器,但每次我收到以下错误,无论我使用哪种内容类型 select:
{
"status": "CONFLICT",
"code": 40902,
"message": "Content type 'text/plain' not supported",
"developerMessage": "null",
"moreInfoUrl": "class org.springframework.web.HttpMediaTypeNotSupportedException",
"throwable": null
}
消息根据内容类型而变化 selected:
"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
注释中完全删除,结果相同。
我调查过类似的问题:
- Content type 'text/plain;charset=UTF-8' not supported error in spring boot inside RestController class 这是最接近我的问题的一个,但我已经将请求中的内容类型设置为我在端点上期望的内容类型。
但是 none 以上和其他一些与我检查过的问题不太接近的问题已经提供了解决此问题的答案。
我尝试发送到服务器的示例 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() {}
}
因为这个
@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 = (MappingJackson2HttpMessageConverter) converter;
m.setObjectMapper(mapper);
}
}
}
也许可以发表关于依赖默认转换器列表的评论。
我意识到 OP 的问题中提到的案例与我的略有不同,尽管我有同样的错误。
就我而言,它是在我开始使用不同的 ObjectMapper 后出现的。
最初我使用的是
"com.fasterxml.jackson.core.databind" 作为我项目中的 ObjectMapper。
我重构了一些代码,并在此过程中切换到 org.codehaus.jackson 作为 ObjectMapper 的源代码。
那是一切都崩溃的时候,Spring 会简单地拒绝每个请求,而不管 Content-Type 与 OP 具有相同的错误消息。
我花了 3 天的时间来解决这个问题,但最后我只是简单地切换回 "databind" 的 ObjectMapper。在那之后一切都神奇地工作了。
我在这里提到这一点,以防对某人有所帮助。取消 OP 我没有接触过 MessageConverters 或任何复杂的东西。
我正在尝试创建一个使用 HTML 的 Web 服务,稍后用于使用 iText 创建 PDF。
我正在使用 Postman 将我的请求发送到服务器,但每次我收到以下错误,无论我使用哪种内容类型 select:
{
"status": "CONFLICT",
"code": 40902,
"message": "Content type 'text/plain' not supported",
"developerMessage": "null",
"moreInfoUrl": "class org.springframework.web.HttpMediaTypeNotSupportedException",
"throwable": null
}
消息根据内容类型而变化 selected:
"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
注释中完全删除,结果相同。
我调查过类似的问题:
- Content type 'text/plain;charset=UTF-8' not supported error in spring boot inside RestController class 这是最接近我的问题的一个,但我已经将请求中的内容类型设置为我在端点上期望的内容类型。
但是 none 以上和其他一些与我检查过的问题不太接近的问题已经提供了解决此问题的答案。
我尝试发送到服务器的示例 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() {}
}
因为这个
@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 = (MappingJackson2HttpMessageConverter) converter;
m.setObjectMapper(mapper);
}
}
}
也许可以发表关于依赖默认转换器列表的评论。
我意识到 OP 的问题中提到的案例与我的略有不同,尽管我有同样的错误。 就我而言,它是在我开始使用不同的 ObjectMapper 后出现的。
最初我使用的是
"com.fasterxml.jackson.core.databind" 作为我项目中的 ObjectMapper。
我重构了一些代码,并在此过程中切换到 org.codehaus.jackson 作为 ObjectMapper 的源代码。
那是一切都崩溃的时候,Spring 会简单地拒绝每个请求,而不管 Content-Type 与 OP 具有相同的错误消息。
我花了 3 天的时间来解决这个问题,但最后我只是简单地切换回 "databind" 的 ObjectMapper。在那之后一切都神奇地工作了。
我在这里提到这一点,以防对某人有所帮助。取消 OP 我没有接触过 MessageConverters 或任何复杂的东西。