mardi 11 mai 2010

Exposer une API REST/XML avec Play! - partie 2

Dans le billet précédent nous avons vu comment exposer des données au format XML avec Play!.
Aujourd'hui nous allons effectuer l'opération inverse, l'envoi d'un contenu XML au contrôleur Play!, à travers une URL RESTful.

On veut par exemple envoyer le contenu suivant en POST avec un content type application/xml :

<album>
      <artist>Metallica</artist>
      <name>Death Magnetic</name>
      <release-date>2008</release-date>
      <genre>METAL</genre>
   </album>

Pour cela on ajoute la ligne suivante au fichier routes pour autoriser l'opération POST sur l'url /album :

POST /album  Application.saveXml

La méthode saveXml récupère le contenu de la requete dans la variable request.body.
Elle parse ensuite le contenu pour créer un album et l'enregistrer dans la base. La classe play.libs.XPath facilite le parcours de documents XML :

public static void saveXML(){
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    Document document = null;
    try{
    //création du document XML à partir de la requête
    DocumentBuilder builder = factory.newDocumentBuilder();
    document = builder.parse(request.body);
    }
    catch(Exception e){
    }
    //parsing du contenu XML
    Element albumNode = document.getDocumentElement();    
    //Artiste
    String artistName = XPath.selectText("artist",albumNode);
    Artist artist = new Artist(artistName);
    //get the name
    String albumName = XPath.selectText("name", albumNode);
    Album album = new Album(albumName);
    //get the date
    String date = XPath.selectText("release-date",albumNode);
    DateFormat dateFormat = new SimpleDateFormat("yyyy");
    try {
 album.releaseDate = dateFormat.parse(date);
    } catch (ParseException e) {
 Logger.error(e.getMessage());
    }
    //genre
    String genre = XPath.selectText("genre", albumNode);
    Genre genreEnum = Genre.valueOf(genre.toString().toUpperCase());
    album.genre = genreEnum;
    //sauvegarde
    album.artist = artist;
    album.save();
}

NB: il est bien sûr possible d'obtenir un code moins verbeux en dé-sérialisant l'objet à l'aide d'un outil comme JAXB ou XStream, mais ce n'est pas l'objet de ce billet.

Lorsqu'on écrit le code album.artist=artist, la méthode setArtist(Artist artist) est appelée automatiquement par Play! (le code est modifié au runtime). On peut ainsi vérifier le fait que l'artiste existe ou non dans la base, pour savoir si on doit créer une nouvelle instance d'artiste ou récupérer l'artiste existant.
La méthode save() de la classe Album s'occupe alors d'enregistrer l'album en base, ainsi que l'artiste si il est inconnu dans la bibliothèque(à l'aide d'un cascade JPA).

public void setArtist(Artist artist){
        List<artist> existingArtists = Artist.find("byName", artist.name).fetch();
        if(existingArtists.size()>0){
            //Le nom d'artiste est unique
            this.artist=existingArtists.get(0);
        }
        else{
            this.artist=artist;
        }
    }

Notre API REST/XML nous permet donc maintenant de lire la liste des albums de note bibiothèque musicale et d'ajouter des albums.
Vous pouvez tester l'envoi de contenu XML avec le plugin Poster de Firefox (la démarche est expliquée dans ce billet) ou avec l'application rest-client.

Un autre post si vous désirez continuer : Play framework : exporter ses objets en JSON