Développer des services REST avec Spring 3 – Part 1/2

L’objet de cet article est de présenter les différentes options qui permettent d’exposer des services REST à l’aide de Spring MVC 3.2.

Spring propose deux approches pour exposer des services REST.

-  La première est basée sur les principes de fonctionnement du framework MVC de Spring, à savoir l’utilisation du ContentNegotiatingViewResolver.

-  La seconde, plus récente,  fait appel  aux HttpMessageConverters et à l’annotation @ResponseBody.

Méthode 1 : Utiliser le ContentNegotiatingViewResolver

L’utilisation du ContentNegotiatingViewResolver pour exposer des API Rest a été la première approche proposée car elle s’appuie sur le mécanisme de Spring MVC existant.

Pour rappel SpringMVC permet de définir des ViewResolver qui permettent de définir la manière dont seront affichées les données retournées par le Controller MVC.

La déclaration de la servlet Spring dans le XML est nécessaire:

<web-app>
   <servlet>
     <servlet-name>dispatchServlet</servlet-name>
     <servlet-class>
         org.springframework.web.servlet.DispatcherServlet
     </servlet-class>
   </servlet>

   <servlet-mapping>
      <servlet-name>disptachServlet</servlet-name>
      <url-pattern>/rest/*</url-pattern>
   </servlet-mapping>
 </web-app>

Dans le cas classique d’affichage d’une JSP, on utilise l’InternalViewResolver ( qui permet d’appeler une JSP pour afficher les données), que l'on déclare dans la configuration Spring :

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

Note : La servlet de Spring (DispatcherServlet) référence les différents ViewResolver en parcourant les beans définis dans  la configuration Spring.

Lorsque vous utilisez le ContentNegotiatingViewResolver, votre controller web retourne un ModelView ou le nom d’une vue, et selon certains critères paramétrables, le ContentNegotiatingViewResolver va choisir la vue à utiliser pour retourner le résultat :

@Controller
@RequestMapping(value="/todo") 
public class TodoRestService2 { 
    @Autowired 
    private TodoService todoService; 

    @RequestMapping(value="/{id}", method= RequestMethod.GET) 
    public ModelAndView find(@PathVariable("id") Long id) { 
       Todo todo = todoService.find(id); 
       return new ModelAndView("todo", "todo", todo);
       }
 }

Pour l’exposition de service REST, Spring propose différentes implémentations de ces vues pour les formats de contenus les plus courants :

  • org.springframework.web.servlet.view.json.MappingJacksonJsonView : format de type Json.
  • org.springframework.web.servlet.view.xml.MarshallingView : format de type XML.
  • org.springframework.web.servlet.view.documentClass.AbstractPdfView : format de type Pdf.
  • etc…

Il est bien sur possible de créer des implémentations spécifiques ou d’étendre celles existant.

Les critères qui permettent au ContentNegotiatingViewResolver de choisir la bonne vue sont définis à l’aide de stratégies. Ces stratégies permettent de définir un lien entre l’url appelée par le client et le format de la réponse qui sera retournée.  La vue qui correspond au format sera alors appelée.

Spring fournit principalement les implémentations suivantes :

  • org.springframework.web.accept.PathExtensionContentNegotiationStrategy : Stratégie basée sur l’extension de l’url ( .json, .xml, .html…etc).
  • org.springframework.web.accept.ParameterContentNegotiationStrategy : Stratégie basée sur le passage d’un paramètre dans la requête.
  • org.springframework.web.accept.HeaderContentNegotiationStrategy : Stratégie basée sur l’analyse sur l’attribut « accept » du header de la requête.

Prenons l’exemple d’un service RESTFul  qui  permettrait de retourner le contenu d’un bean Java en XML ou en Json en fonction de l’extension de l’url.

Ci-dessous la configuration qui permet de le faire :

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
   <property name="contentNegotiationManager">
      <bean class="org.springframework.web.accept.ContentNegotiationManager">
      <constructor-arg>
        <bean
         class="org.springframework.web.accept.PathExtensionContentNegotiationStrategy">
          <constructor-arg>
           <map>
             <entry key="json" value="application/json"/>
             <entry key="xml" value="application/xml"/>
           </map>
          </constructor-arg>
         </bean>
       </constructor-arg>
      </bean>
   </property> 
<property name="defaultViews">
  <list>
    <!-- Renders JSON View -->
    <bean
     class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />

    <!-- Renders XML View -->
    <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
     <constructor-arg>
       <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
         <property name="packagesToScan">
            <list>
              <value>org.demo.domain</value>
            </list>
         </property>
      </bean>
     </constructor-arg>
    </bean>
  </list>
</property>
</bean>

Dans cet exemple, la stratégie PathExtensionContentNegotiationStrategy permet de définir qu’un appel vers une url finissant par .json sera dirigée vers la vue MappingJacksonJsonView car elle est capable de générer du contenu de type application/json.

Même chose avec les urls finissant par .xml et la vue MarshallingView.

Dans un second article nous étudierons la seconde solution qui consiste à utiliser des HttpMessageConverters.

Un commentaire

  1. Très bonne article.
    dans la dernière version de spring on utilise presque que des annotations pour la configuration.
    j’aimerais savoir comment je peux faire la configuration de beans dans écrire du xml (grace aux annotations).

    Merci bien à vous.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.