2011-01-16 6 views
5

Si nous développons REST à l'aide de Spring MVC, il prendra en charge les données XML et JSON. Je l'ai écrit ContentNegotiationViewResorver dans mon grain de configuration printemps app-servlet.xmlPrintemps REST 3 pour prendre en charge XML et JSON

<bean 
     class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" 
     p:order="1"> 
     <property name="mediaTypes"> 
      <map> 
       <entry key="xml" value="application/xml" /> 
       <entry key="json" value="application/json" /> 
      </map> 
     </property> 
     <property name="defaultViews"> 
      <list> 
       <bean class="org.springframework.web.servlet.view.xml.MarshallingView"> 
        <property name="marshaller"> 
         <bean class="org.springframework.oxm.xstream.XStreamMarshaller" 
          p:autodetectAnnotations="true" /> 
        </property> 
       </bean> 
       <bean 
        class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" /> 
      </list> 
     </property> 
    </bean> 

Et mon ressort REST Controller est:

@Controller 
@RequestMapping("/rest/customers") 
class CustomerRestController { 

protected Log log = LogFactory.getLog(CustomerRestController.class); 

@RequestMapping(method = POST) 
@ResponseStatus(CREATED) 
public void createCustomer(@RequestBody Customer customer, 
     HttpServletResponse response) { 

    log.info(">>>" + customer.getName()); 
    response.setHeader("Location", String.format("/rest/customers/%s", 
      customer.getNumber())); 
} 


@RequestMapping(value = "/{id}", method = GET) 
@ResponseBody 
public Customer showCustomer(@PathVariable String id) { 
    Customer c = new Customer("0001", "teddy", "bean"); 
    return c; 
} 


@RequestMapping(value = "/{id}", method = PUT) 
@ResponseStatus(OK) 
public void updateCustomer(@RequestBody Customer customer) { 
    log.info("customer: " + customer.getName()); 
} 

Je mis @XStreamAlias("customer") annotation dans ma classe de domaine du client. Mais quand j'essaie d'accéder à http://localhost:8080/rest/customers/teddy.xml, il répond toujours aux données JSON.

J'ai défini l'annotation @XmlRootElement(name="customer") dans ma classe de domaine client. Mais quand j'essaie d'accéder à http://localhost:8080/rest/customers/teddy.json, il répond toujours aux données XML.

Y at-il quelque chose qui ne va pas?

+0

Où est app/clients/teddy.xml cartographié dans votre contrôleur? – chris

+0

désolé .., l'url est: /rest/customers/teddy.xml, cette url supposée invoquer méthode showCustomer. et Teddy est le paramètre {id}. –

+0

Comment essayez-vous d'accéder à cette URL? Un navigateur Web? Envoyez-vous l'en-tête de codage de type de contenu approprié dans la demande? – skaffman

Répondre

2

Je pense que le type de contenu "xml" doit être mappé sur "text/xml" et non sur "application/xml". En outre, pour forcer les résolveurs de type de contenu basés sur l'extension, vous pouvez essayer de définir la propriété "favorPathExtension" de "ContentNegotiatingViewResolver" sur true (bien que cela aurait dû être le cas par défaut!)

EDIT: J'ai maintenant ajouté un travail échantillon à cet emplacement GIT - git://github.com/bijukunjummen/mvc-samples.git, si vous élevez le point de terminaison, en utilisant mvn tomcat: run, le json est servi à http://localhost:8080/mvc-samples/rest/customers/teddy.json et xml à http://localhost:8080/mvc-samples/rest/customers/teddy.xml. Cela utilise JAXB2 pas XStream, car je suis familier avec JAXB. Une chose que j'ai remarquée était que lorsque mes annotations JAXB n'étaient pas correctes dans la classe Customer, Spring distribuait JSON et pas XML comme vous l'avez vu (vous pouvez le répliquer en supprimant l'annotation XMLRootElement de la classe Customer), une fois que j'ai corrigé annotations, j'ai récupéré XML comme prévu. Il se peut donc qu'il y ait un problème avec votre configuration XStream.

EDIT 2: Vous avez raison !! Je n'ai pas remarqué, une fois que je suis revenu xml, j'ai supposé que json travaille maintenant. Je vois le problème, dans AnnotationMethodHandlerAdapter, la manipulation pour @ResponseBody est un peu étrange, il ignore complètement les ViewResolvers, et utilise les MessageConverters enregistrés en contournant complètement le ContentNegotiatingViewResolver, une solution de contournement pour l'instant est d'utiliser l'annotation @ModelAttribute pour la réponse, au lieu de @ResponseBody , de cette façon la vue Resolvers sont appelés. Essayez maintenant en utilisant le projet au [email protected]:bijukunjummen/mvc-samples.git et voyez si cela fonctionne pour vous. Cela pourrait être un bug de printemps, vous pouvez essayer de le présenter dans le forum de printemps et voir ce qu'ils recommandent.

+0

J'ai changé le type de contenu en text/xml et text/json. et définissez faforPathExtension sur true. Mais je json texte pendant que j'accède app/rest/clients/teddy.xml –

+0

Pas sûr @adisembiring, j'ai essayé d'utiliser JAXB2 et cela fonctionne sans problèmes - J'ai mis un échantillon à cet endroit - git: //github.com/ bijukunjummen/mvc-samples.git, il sert json correctement à /rest/customers/teddy.json et xml à rest/customers/teddy.xml. –

+0

J'ai exécuté votre projet, Essayez d'accéder à l'application en utilisant 'firefox',' chrome', et 'poster firefox plugin'. mais j'obtiens la valeur 'xml' pendant l'accès' rest/customers/teddy.json'. Vous pouvez consulter le résultat de mon navigateur dans http://i52.tinypic.com/29hb4j.jpg –

1

J'ai eu le même problème. Je suppose que vous utilisez Spring 3 et vous avez utilisé <mvc:annotation-driven/>. Je ne suis pas entièrement sûr, mais je pense que cela crée un conflit basé sur les convertisseurs de messages que l'espace de noms mvc configure.

Utilisation de l'espace de noms OXM a fonctionné pour moi:

@XmlRootElement(name="person") 
class Person { 
    private String firstName; 
    private String lastName; 
} 

@Controller 
@RequestMapping("person") 
class PersonController { 
    @RequestMapping("list") 
    public @ResponseBody Person getPerson() { 
     Person p = new Person(); 
     p.setFirstName("hello"); 
     p.setLastName("world"); 
     return p; 
    } 
} 

Configuration contenu (vue mvc interne et résolveur sont dans un autre contexte):

<?xml version="1.0" encoding="UTF-8" standalone="no"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:context="http://www.springframework.org/schema/context" xmlns:oxm="http://www.springframework.org/schema/oxm" 
    xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 

     <oxm:jaxb2-marshaller id="jaxbMarshaller"> 
     <oxm:class-to-be-bound name="package.Person" /> 
    </oxm:jaxb2-marshaller> 

    <bean 
     class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> 
     <property name="defaultContentType" value="text/html" /> 
     <property name="ignoreAcceptHeader" value="true" /> 
     <property name="favorPathExtension" value="true" /> 
     <property name="mediaTypes"> 
      <map> 
       <entry key="json" value="application/json" /> 
       <entry key="xml" value="application/xml" /> 
      </map> 
     </property> 
     <property name="defaultViews"> 
      <list> 
       <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" /> 
       <bean class="org.springframework.web.servlet.view.xml.MarshallingView"> 
        <property name="marshaller" ref="jaxbMarshaller" /> 
       </bean> 
      </list> 
     </property> 
    </bean> 
</beans> 

Cet exemple utilise JAXB, de sorte que vous auriez besoin jaxb-api et jaxb-impl sur le classpath.

De plus, juste un conseil, vous n'avez pas besoin du fichier app-servlet.xml. Dans votre site Webxml, définir la configuration null et laissez le contexte Listener les charger pour vous:

<listener> 
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener> 
    <context-param> 
      <param-name>contextConfigLocation</param-name> 
      <param-value>/WEB-INF/spring/mvc-context.xml, /WEB-INF/spring/content-negotiation-context.xml</param-value> 
    </context-param> 
    <servlet> 
     <servlet-name>app</servlet-name> 
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
     <init-param> 
      <param-name>contextConfigLocation</param-name> 
      <param-value/> 
     </init-param> 
     <load-on-startup>1</load-on-startup> 
    </servlet> 
    <servlet-mapping> 
     <servlet-name>app</servlet-name> 
     <url-pattern>/</url-pattern> 
    </servlet-mapping> 
2

Que Accepter les en-têtes sont envoyés à votre serveur? Assurez-vous que le type de contenu que vous souhaitez demander figure dans cette liste.

+0

lorsque j'ajoute l'annotation @XmlRootElement dans la classe de domaine, le serveur renvoie le format xml. lorsque j'ajoute l'annotation @XStreamAlias, le serveur renvoie le format JSON. Je pense que le serveur accepte à la fois xml et json. –

0

L'accès au contrôleur à l'aide d'un navigateur enverra un en-tête de navigateur typique. Il ne correspond à aucun résolveur d'affichage et par défaut au premier (application/xml) ou il correspond car application/xml est dans la liste Accepter.

Je peux recommander l'utilisation de RestClient http://code.google.com/p/rest-client/ pour avoir un contrôle complet sur l'en-tête Accepter (le cas échéant) que vous voulez envoyer.

Je ne recommande pas l'utilisation de text/xml car le jeu de caractères par défaut est US-ASCII et non UTF-8. Cela pourrait créer des problèmes d'encodage funky sur la route. Vous pouvez toujours spécifier l'encodage mais appliation/xml a un encodage par défaut UTF-8.

1

Eh bien, j'obtenu une solution, mais je ne sais pas si c'est la bonne façon dans votre client show méthode:

@RequestMapping(value = "/{id}", method = GET) 
@ResponseBody 
public Customer showCustomer(@PathVariable String id) { 
    Customer c = new Customer("0001", "teddy", "bean"); 
    return c; 
} 

Dans cette partie, nous utilisons MVC du printemps et dans le contrôleur que nous devrions être retourner une vue, donc j'ai supprimé l'annotation @ResponseBody et je retourne un String avec le nom de la vue parce que dans notre XML nous avons ajouté un ContentNegotiatingViewResolver et quand nous avons ResponseBody le contentnegociationviewresolver est ignoré car attend une vue mais nous avons retourné l'objet la méthode devrait être comme ça:

@RequestMapping(value = "/{id}", method = GET) 

public String showCustomer(@PathVariable String id, ModelMap model) { 
    Customer c = new Customer("0001", "teddy", "bean"); 
    model.addAttribute("customer",c); 
    return "myView"; 
} 

bien qui fonctionne pour moi, si vous avez des problèmes, vous pouvez ajouter à votre app-servlet.xml

ce haricot, mais je ne pense pas que vous devez ajouter ceci.

<bean 
    class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
    <property name="prefix"> 
     <value>/WEB-INF/views/</value> 
    </property> 
    <property name="suffix"> 
     <value>.jsp</value> 
    </property> 
</bean> 

I got the answers from mkyong.com

Questions connexes