【问题标题】:HttpMediaTypeNotAcceptableException: Could not find acceptable representation in exceptionhandlerHttpMediaTypeNotAcceptableException:在异常处理程序中找不到可接受的表示
【发布时间】:2015-11-27 20:13:52
【问题描述】:

我的控制器中有以下图片下载方法(Spring 4.1):

@RequestMapping(value = "/get/image/{id}/{fileName}", method=RequestMethod.GET)
public @ResponseBody byte[] showImageOnId(@PathVariable("id") String id, @PathVariable("fileName") String fileName) {
    setContentType(fileName); //sets contenttype based on extention of file
    return getImage(id, fileName);
}

下面的ControllerAdvice方法应该处理一个不存在的文件并返回一个json错误响应:

@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public @ResponseBody Map<String, String> handleResourceNotFoundException(ResourceNotFoundException e) {
    Map<String, String> errorMap = new HashMap<String, String>();
    errorMap.put("error", e.getMessage());
    return errorMap;
}

我的 JUnit 测试完美无缺

EDIT这是因为扩展 .bla :这也适用于 appserver):

@Test
public void testResourceNotFound() throws Exception {
    String fileName = "bla.bla";
      mvc.perform(MockMvcRequestBuilders.get("/get/image/bla/" + fileName)
            .with(httpBasic("test", "test")))
            .andDo(print())
            .andExpect(jsonPath("$error").value(Matchers.startsWith("Resource not found")))
            .andExpect(status().is(404));
}

并给出以下输出:

MockHttpServletResponse:
          Status = 404
   Error message = null
         Headers = {X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY], Content-Type=[application/json]}
    Content type = application/json
            Body = {"error":"Resource not found: bla/bla.bla"}
   Forwarded URL = null
  Redirected URL = null
         Cookies = []

但是在我的应用服务器上,当我尝试下载不存在的图像时收到以下错误消息:

编辑这是因为扩展 .jpg :这在带有 .jpg 扩展的 JUnit 测试中也失败):

ERROR org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Failed to invoke @ExceptionHandler method: public java.util.Map<java.lang.String, java.lang.String> nl.krocket.ocr.web.controller.ExceptionController.handleResourceNotFoundException(nl.krocket.ocr.web.backing.ResourceNotFoundException) org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

我在我的 mvc 配置中配置了 messageconverters,如下所示:

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

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    //objectMapper.registerModule(new JSR310Module());
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setObjectMapper(objectMapper);
    converter.setSupportedMediaTypes(getJsonMediaTypes());
    return converter;
}

@Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
    ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
    arrayHttpMessageConverter.setSupportedMediaTypes(getImageMediaTypes());
    return arrayHttpMessageConverter;
}

我错过了什么?为什么 JUnit 测试有效?

【问题讨论】:

  • 检查您的请求的“接受”标头(“真实”标头,对应用服务器)。它是否同时接受 image/* 和 application/json?如果它只接受 image/* 那么 Spring 不能生成 JSON 消息,因为它与请求接受的内容不兼容。
  • 我有以下内容:Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
  • 我该如何改变?添加到消息转换器 (setSupportedMediaTypes)?
  • 客户端在期待图像时接收 JSON 是否可能/有用?通常在请求图像时,错误响应代码是客户端需要的所有信息(例如,在 Web 应用程序中)。尝试调试抛出异常的位置,可能是AbstractMessageConverterMethodProcessor#writeWithMessageConverters()RequestMappingInfoHandlerMapping#handleNoMatchServletHandlerMethodInvoker#writeWithMessageConverters
  • 我可以在我的 ajax 调用中处理 404 响应以显示错误消息而不是图像,所以那里没有问题。错误是从AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:134) 抛出的

标签: java spring spring-mvc junit


【解决方案1】:

您需要决定 Spring 应如何确定响应的媒体类型。这可以通过多种方式完成:

  • 路径扩展名(例如/image.jpg)
  • URL 参数(例如 ?format=jpg)
  • HTTP Accept 标头(例如,Accept: image/jpg)

默认情况下,Spring 查看 extension 而不是 Accept 标头。如果您实现扩展 WebMvcConfigurerAdapter@Configuration 类(或从 Spring 5.0 开始只需实现 WebMvcConfigurer。您可以覆盖 configureContentNegotiation(ContentNegotiationConfigurer configurer) 并根据您的需要配置 ContentNegotiationConfigurer,例如通过调用

ContentNegotiationConfigurer#favorParameter
ContentNegotiationConfigurer#favorPathExtension

如果将两者都设置为 false,那么 Spring 将查看 Accept 标头。由于您的客户可以说 Accept: image/*,application/json 并同时处理这两种情况,因此 Spring 应该能够返回图像或错误 JSON。

有关更多信息和示例,请参阅 this Spring tutorial 内容协商。

【讨论】:

  • 感谢您的出色回答!最重要的是,我删除了消息转换器中支持的媒体类型的设置和控制器方法中的内容类型设置,所以一切都由 spring 协商。另一个补充:禁用路径变量截断在 webconfig 中使用 matcher.setUseSuffixPatternMatch(false)
  • 你的回答很中肯。更改为 1.9.2 并像魅力一样工作
  • 美丽的答案。
  • 引用了大量与此问题相关的其他 SO 问题。没有其他帮助。非常感谢您的澄清。这句话说明了这一点——“默认情况下,Spring 查看扩展而不是 Accept 标头。”
【解决方案2】:

注意您的 HTTP Accept 标头。 例如,如果您的控制器产生“application/octet-stream”(作为响应),您的 Accept 标头不应是“application/json”(在请求中):

@GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public void download(HttpServletResponse response) {}

【讨论】:

  • @Adam 我想在这种情况下客户端会接受任何 mime 类型。使用Postman 等工具亲自尝试。
  • 在 Spring Boot 2.2.2.RELEASE 中使用@GetMapping 时如何做到这一点?
  • @PAA 我正在使用@GetMapping。我的答案与 Spring Boot 2.2.2.RELEASE 不兼容吗?
猜你喜欢
  • 1970-01-01
  • 2017-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-08
  • 1970-01-01
  • 2015-05-08
  • 1970-01-01
相关资源
最近更新 更多