web-dev-qa-db-fra.com

Spring MVC + JSON = 406 non acceptable

J'essaie de générer une réponse JSON simple. À l'heure actuelle, je reçois une erreur 406 non acceptable. Tomcat a déclaré: "La ressource identifiée par cette demande est uniquement capable de générer des réponses avec des caractéristiques non acceptables selon la requête" accepte "les en-têtes." même si mes en-têtes Accept sont

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Dans Tomcat/lib, j'ai tous les bocaux Tomcat, bocaux Spring et jackson-all-1.9.0.jar. J'utilise Spring 3.2.2 avec Tomcat 7. 

Je suis conscient que cette question a été discutée à plusieurs reprises, mais aucune solution ne fonctionne pour moi.

web.xml

<web-app id="WebApp_ID" version="2.4" 
    xmlns="http://Java.Sun.com/xml/ns/j2ee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://Java.Sun.com/xml/ns/j2ee 
    http://Java.Sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  <display-name>Spring Web MVC Application</display-name>

  <servlet>
    <servlet-name>dispatcher</servlet-name>
        <servlet-class>
                  org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

</web-app>

dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
 <context:component-scan base-package="com.smiechmateusz.controller" />
 <context:annotation-config />

    <mvc:annotation-driven />

</beans>

HelloWorldController.Java

package com.smiechmateusz.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;

import com.smiechmateusz.dao.Foo;

@Controller
@RequestMapping("/")
public class HelloWorldController extends AbstractController{

    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
        HttpServletResponse response) throws Exception {

        ModelAndView model = new ModelAndView("HelloWorldPage");
        return model;
    }

    @RequestMapping(value="foobar.htm", method = RequestMethod.GET)
    public @ResponseBody Foo getShopInJSON() {
        Foo f = new Foo();
        f.setX(1);
        f.setY(2);
        f.setDescription("desc");
        return f;
    }
}

Foo.Java

package com.smiechmateusz.dao;

import Java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="foobaz")
public class Foo implements Serializable
{
    private int x, y;
    String description;
    int id;

    @Column(name = "x")
    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
    @Column(name = "y")
    public int getY() {
        return y;
    }
    public void setY(int y) {
        this.y = y;
    }
    @Column(name = "description")
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }

    @Id @GeneratedValue
    @Column(name = "id")
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}

J'ai déjà essayé d'ajouter 

<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
          <list>
            <ref bean="jsonConverter"/>
          </list>
    </property>
</bean>

vers mon dispatcher-servlet.xml ou en changeant jakcson-all en jackson-asl et jackson-core-asl, mais le résultat obtenu était identique.

50
Mateusz

Accepter: text/html, application/xhtml + xml, application/xml; q = 0,9,/; q = 0,8

Cela devrait être le problème. JSON est servi comme application/json. Si vous définissez l'en-tête Accept en conséquence, vous devriez obtenir la réponse appropriée. (Il existe des plugins de navigateur qui vous permettent de définir des en-têtes, j'aime mieux "Poster" pour Firefox)

17

Si vous utilisez Maven et le dernier code Jackson , vous pouvez supprimer toutes les configurations spécifiques à Jackson de vos fichiers XML de configuration de printemps (vous aurez toujours besoin d'une balise pilotée par une annotation <mvc: annotation-driven /> ) et ajoutez simplement quelques dépendances de Jackson à votre fichier pom.xml. Voir ci-dessous pour un exemple des dépendances. Cela a fonctionné pour moi et j'utilise:

  • Apache Maven 3.0.4 (r1232337; 2012-01-17 01: 44: 56-0700)
  • org.springframework version 3.1.2.RELEASE
  • spring-Security version 3.1.0.RELEASE.

    ...<dependencies>
    ...
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.2.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.2.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.2.3</version>
        </dependency>
        ...
    </dependencies>...
    
38
KSev

Une autre façon d’obtenir cette erreur est de créer une classe sans membre public. 406 inacceptable est un message d'erreur assez inutile dans ce scénario.

20
Doo Dah

Avec Spring 4, vous n’ajoutez que @EnableWebMvc, par exemple:

@Controller
@EnableWebMvc
@RequestMapping(value = "/articles/action", headers="Accept=*/*",  produces="application/json")
public class ArticlesController {

}
9
Nguyen Viet

Aucune des autres réponses ne m'a aidé.

J'ai lu des dizaines de réponses de Stackoverflow à propos de l'exception 406 non acceptable, exception HttpMediaTypeNotAcceptableEx, fichier multipart, ResponseBody, configuration des en-têtes Accepter, produire, consommer, etc.

Nous avions Spring 4.2.4 avec SpringBoot et Jackson configurés dans build.gradle:

compile "com.fasterxml.jackson.core:jackson-core:2.6.7"
compile "com.fasterxml.jackson.core:jackson-databind:2.6.7"

Tous les itinéraires fonctionnaient correctement dans nos autres contrôleurs et nous pouvions utiliser GET, POST, PUT et DELETE. Ensuite, j'ai commencé à ajouter une fonctionnalité de téléchargement de fichiers en plusieurs parties et à créer un nouveau contrôleur. Les itinéraires GET fonctionnent bien, mais pas notre POST et DELETE. Peu importe la façon dont j'ai essayé différentes solutions à partir d'ici à SO, je n'arrêtais pas d'obtenir 406 Not Acceptable.

Enfin, je suis tombé sur cette réponse SO: Printemps qui lance HttpMediaTypeNotAcceptableException: Impossible de trouver une représentation acceptable en raison d'un point dans le chemin de l'url

Lisez la réponse de Raniz et tous les commentaires.

Tout se résumait à nos valeurs @RequestMapping:

@RequestMapping(value = "/audio/{fileName:.+}", method = RequestMethod.POST, consumes="multipart/*")
public AudioFileDto insertAudio(@PathVariable String fileName, @RequestParam("audiofile") MultipartFile audiofile) {

    return audioService.insert(fileName, audiofile);
}

@RequestMapping(value = "/audio/{fileName:.+}", method = RequestMethod.DELETE)
public Boolean deleteAudio(@PathVariable String fileName) {

    return audioService.remove(fileName);
}

La partie {fileName:.+} dans une valeur @RequestMapping a causé 406 Inacceptable dans notre cas.

Voici le code que j'ai ajouté à partir de la réponse de Raniz:

@Configuration
public class ContentNegotiationConfig extends WebMvcConfigurerAdapter {
    @Override
    void configureContentNegotiation(final ContentNegotiationConfigurer configurer) {
        // Turn off suffix-based content negotiation
        configurer.favorPathExtension(false);
    }
}

EDIT le 29 août 2016:

Nous avons eu des problèmes avec configurer.favorPathExtension(false): les images SVG statiques ont cessé de se charger. Après analyse, nous avons constaté que Spring avait commencé à envoyer des fichiers SVG à l'interface utilisateur avec le type de contenu "application/octet-stream" au lieu de "image/svg + xml". Nous avons résolu ce problème en envoyant fileName comme paramètre de requête, par exemple:

@RequestMapping(value = "/audio", method = RequestMethod.DELETE)
public Boolean deleteAudio(@RequestParam String fileName) {

    return audioService.remove(fileName);
}

Nous avons également supprimé la configurer.favorPathExtension(false). Une autre méthode pourrait consister à coder nomFichier dans le chemin, mais nous avons choisi la méthode du paramètre de requête pour éviter d’autres effets secondaires.

8
Jarno Argillander

Utilisez ci-dessous la dépendance dans votre pom

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.3</version>
</dependency>
6
Nirbhay Rana

Vous devez enregistrer la liaison d'annotation pour Jackson dans votre spring-mvc-config.xml, par exemple:

<!-- activates annotation driven binding -->
<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" validator="validator">
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
        <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>

Ensuite, dans votre contrôleur, vous pouvez utiliser:

@RequestMapping(value = "/your_url", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
5
alain.janinm

Je suppose que le problème était lié à l'utilisation de l'extension * .htm dans RequestMapping (foobar.htm). Essayez de le changer en footer.json ou quelque chose d’autre.

Le lien vers la réponse correcte: https://stackoverflow.com/a/21236862/537246

P.S.

Il est en quelque sorte Spring de faire quelque chose par défaut, inquiétant, que les développeurs connaissent toute l'API Spring de A à Z. Et ensuite, "406 n'est pas acceptable" sans aucun détail, et les logs de Tomcat sont vides!

4
Demwis

Aujourd'hui, j'ai moi aussi traversé le même problème. Dans mon cas, dans web.xml j'ai 

   <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

mon url a l'extension .html. Ex: .../getUsers.html. Mais je retourne des données JSON dans le contrôleur. L’extension .html définira par défaut le type d’acceptation au format html. 

J'ai donc changé pour ce qui suit:

web.xml:

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>*.html</url-pattern>
    <url-pattern>*.json</url-pattern>
</servlet-mapping>

URL:

.../getUsers.json

Tout fonctionne bien maintenant. J'espère que ça aide.

2
vissu

Voir le problème est avec l'extension. Selon l'extension, spring pourrait déterminer le type de contenu. Si votre URL se termine par .com alors il envoie text/html comme en-tête de type de contenu Si vous voulez changer ce comportement de Spring, veuillez utiliser le code ci-dessous:

@Configuration
@Import(HibernateConfig.class)
@EnableWebMvc
// @EnableAsync()
// @EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.azim.web.service.*",  basePackageClasses = { WebSecurityConfig.class }, excludeFilters = { @ComponentScan.Filter(Configuration.class) })
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false).favorParameter(true).parameterName("mediaType").ignoreAcceptHeader(true).useJaf(false)
                .defaultContentType(MediaType.APPLICATION_JSON).mediaType("xml", MediaType.APPLICATION_XML).mediaType("json", MediaType.APPLICATION_JSON);
    }

    @Bean(name = "validator")
    public Validator validator() {
        return new LocalValidatorFactoryBean();
    }
}

Ici, nous définissons favorPathExtension sur false et le type de contenu par défaut sur Application/json. Remarque: La classe HibernateConfig contient tous les beans.

2
Azim

c'est à cause de l'objet n'est pas acceptable à jsp ... utiliser son 

ajoutez cette dépendance ou toute autre chaîne json convertie à envoyer en jsp ...

par exemple, ajouter ceci dans pom

<dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.6.2</version>
    </dependency>

et utiliser un code comme ça:

@RequestMapping(value="foobar.htm", method = RequestMethod.GET)
    public @ResponseBody String getShopInJSON() {
        Foo f = new Foo();
        f.setX(1);
        f.setY(2);
        f.setDescription("desc");
        return new Gson().toJson(f); //converted object into json string
    }//return converted json string
1
aditya Goyal

Essayez d'ajouter

@RequestMapping(method = RequestMethod.GET,headers = {"Accept=text/xml, application/json"})

sur getShopInJSON().

Cela a fonctionné pour moi.

1
Shubham

Peut-être que tous les champs de votre POJO ont besoin de Getter et Setter.

Je l'ai corrigé en fonction de ce problème . reference: Spring MVC - HttpMediaTypeNotAcceptableException

Et 406 n'est pas un message utile pour résoudre le bogue. Vous devriez déboguer dans les codes et voir ce que l'exception est sur terre.

1
Dai Kaixian

Il existe un autre cas où cet état sera retourné: si le mappeur de Jackson ne peut pas déterminer comment sérialiser votre bean. Par exemple, si vous avez deux méthodes d'accès pour la même propriété booléenne, isFoo() et getFoo().

supprimé getFoo() et mettre isFoo(). cela a fonctionné pour moi.

0
The Java Guy

Je ne pouvais pas voir cela comme une réponse ici, alors je pensais mentionner que j'avais reçu cette erreur en utilisant le ressort 4.2 lorsque j'avais accidentellement retiré le getter/passeur de la classe que je m'attendais à retrouver comme Json. 

0
jwv

Ma valeur RequestMapping finissait par .html , ce qui devrait être différent.

J'ai essayé de le changer en .json et cela a fonctionné pour moi.

0
Swapneel

J'ai le problème similaire et résolu en utilisant le code ci-dessous 

public class ProductList {

    private List<Product> productList =  new ArrayList<Product>();

    @JsonProperty("data")
    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
    public List<Product> getProductList() {
        return productList;
    }
public void setProductList(List<Product> productList) {
        this.productList = productList;
    }

I am setting ProductList object in ResponseEntity object and returning from controller.
0
Sachin

Ma classe a été annotée avec JsonSerialize et le paramètre include a été défini sur JsonSerialize.Inclusion.NON_DEFAULT. Cela a amené Jackson à déterminer les valeurs par défaut pour chaque propriété de bean. J'ai eu une propriété de haricot qui a retourné un int. Le problème dans mon cas était que le get Bean faisait appel à une méthode ayant un type de retour inféré (c'est-à-dire: une méthode générique). Pour une raison étrange, ce code compilé; il n'aurait pas dû être compilé car vous ne pouvez pas utiliser un int pour un type de retour inféré. J'ai changé le "int" en "Integer" pour cette propriété de bean et je n'ai plus un 406. Le plus étrange, c'est que le code ne parvient pas à être compilé si je modifie Integer en int. 

0
Jared Simon

On dirait que vous essayez de produire/recevoir une sortie json . Je vois deux problèmes avec votre approche . 1) Vous n'avez pas spécifié l'application/json dans votre en-tête Accept 2) Vous devez spécifiez produc = "application/json" dans votre @RequestMapping

0
Balaji Katika