web-dev-qa-db-fra.com

HttpMediaTypeNotAcceptableException: impossible de trouver une représentation acceptable dans le gestionnaire d'exceptions

J'ai la méthode de téléchargement d'image suivante dans mon contrôleur (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);
}

La méthode ControllerAdvice suivante doit gérer un fichier inexistant et renvoyer une réponse d'erreur 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;
}

Mon test JUnit fonctionne parfaitement

( EDIT c'est à cause de l'extension .bla: cela fonctionne aussi sur 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));
}

et donne la sortie suivante:

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 = []

Cependant, sur mon serveur d'applications, j'obtiens le message d'erreur suivant lorsque j'essaie de télécharger une image non existante:

( EDIT c'est à cause de l'extension .jpg: cela échoue également sur le test JUnit avec l'extension .jpg):

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

J'ai configuré les convertisseurs de messages dans ma configuration mvc comme suit:

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

Qu'est-ce que je rate? Et pourquoi le test JUnit fonctionne-t-il?

15
s.ijpma

Vous devez décider comment le type de support de la réponse doit être déterminé par Spring. Cela peut se faire de plusieurs manières:

  • extension de chemin (par exemple. /image.jpg)
  • Paramètre URL (par exemple.? Format = jpg)
  • En-tête HTTP Accept (par exemple, Accept: image/jpg)

Par défaut, Spring regarde l'extension plutôt que l'en-tête Accept. Ce comportement peut être modifié si vous implémentez une classe @Configuration Qui étend WebMvcConfigurerAdapter (ou depuis Spring 5.0 implémentez simplement WebMvcConfigurer =. Là, vous pouvez remplacer configureContentNegotiation(ContentNegotiationConfigurer configurer) et configurer ContentNegotiationConfigurer selon vos besoins, par exemple en appelant

ContentNegotiationConfigurer#favorParameter
ContentNegotiationConfigurer#favorPathExtension

Si vous définissez les deux sur false, Spring examinera l'en-tête Accept. Étant donné que votre client peut dire Accept: image/*,application/json Et gérer les deux, Spring devrait être en mesure de renvoyer l'image ou l'erreur JSON.

Voir ce tutoriel de printemps sur la négociation de contenu pour plus d'informations et d'exemples.

29
Adam Michalik

Faites attention à votre en-tête HTTP Accept. Par exemple, si votre contrôleur produit "application/octet-stream" (en réponse), votre en-tête Accept ne doit PAS être "application/json" (en demande):

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