I servizi restful possono essere un'ottima soluzione per poter fornire i propri Web Service al mondo esterno, in particolare qui descriveremo come realizzare
servizi RESTful a partire da web application realizzate con Struts MVC.
Per illustrare la realizzazione di un servizio restful, ci serviremo di un'applicazione che gestisce un database di capitoli di manga, in particolare illustreremo la creazione di un'interfaccia REST, che un client utilizzerà per poter effettuare le ricerche di singoli personaggi (
character) presenti nei manga.
La prima cosa da fare è quello di individuare la risorsa che il servizio dovrà restituire, in questo caso si tratta del personaggio; successivamente si deve passare a
definire un URI che determini univocamente un personaggio. Un esempio che risponde a queste caratteristiche ha il seguente schema:
<host>/<applicationname>/<resourceName>s/<id>
Seguendo tale schema, in risposta a
GET effettuato sul nostro servizio RESTful, si dovrà avere un URI simile a "http://localhost:8080/RestServer/characters/1", dove 1 è l'ID del personaggio trovato all'interno del database.
Utilizzano lo stesso URI in abbinamento a
POST, potremmo indicare al servizio RESTful l'inserimento di un nuovo personaggio; se invece non è presente alcun ID, si indica la richiesta di una lista completa di tutti i personaggi dei manga.
Passando alla parte operativa, presupponiamo che l'applicazione di memorizzazione dei manga sia realizzata con un
template di Spring MVC — dovremo configurare il
servlet-context.xml, dove andiamo a specificare la volontà di utilizzare le annotazioni — e l'utilizzo della classe
InternalResourceViewResolver per la costruzione dell'output del servizio, specificando di voler utilizzare il package
org.springframework.rest per risolvere alcune dipendenze nel bean:
<?xml version="1.0" encoding="UTF-8" ?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-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">
<annotation-driven />
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="org.springframework.rest" />
</beans:beans>
A questo punto è possibile definire la classe
Character, che si occuperà di contenere tutte le informazioni su un singolo personaggio.
package org.springframework.rest;
import java.net.URL;
import org.codehaus.jackson.annotate.JsonAutoDetect;
@XmlRootElement
public final class Character {
private int id;
private String name;
private boolean isHuman;
private URL characterUrl;
protected Character() {
}
public Character(int id, String name, boolean isHuman, URL characterUrl) { super();
this.id = id;
this.name = name;
this.isHuman = isHuman;
this.characterUrl = characterUrl;
}
public int getId() { return id;
}
public void setId(int id) { this.id = id;
}
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public boolean isHuman() { return isHuman;
}
public void setHuman(boolean isHuman) { this.isHuman = isHuman;
}
public URL getCharacterUrl() { return characterUrl;
}
public void setCharacterUrl(URL characterUrl) { this.characterUrl = characterUrl;
}
@Override
public int hashCode() { final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) { if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Character other = (Character) obj;
if (id != other.id)
return false;
return true;
}
}
Da notare l'annotazione
@XmlRootElement che fa sì che il contenuto dell'intero oggetto venga mappato in una serie di tag XML.
A questo punto non ci rimane che vedere la classe
HomeController, che implementa il servizio RESTful vero e proprio: tale classe è implementata come una classe
Controller di Spring e inizializza una
HashMap che rappresenterà il nostro database di manga.
@Controller
public class HomeController {
private static final Map<Integer, Character> characters = new HashMap<Integer, Character>();
static { try { characters.put(1, new Character(1, "Totoro", false, new URL("http://animeonly.org/albums/VISINAUJI/EGIO/fourth/Mon-Voisin-Totoro/normal_totoro_001.jpg"))); characters.put(2, new Character(2, "Satsuki Kusakabe", true, new URL("http://profile.ak.fbcdn.net/hprofile-ak-ash2/48980_1802552968_7286_n.jpg"))); characters.put(3, new Character(3, "Therru", false, new URL("http://28.media.tumblr.com/tumblr_lj4ctjKA8Y1qdvyqpo1_400.jpg"))); } catch (MalformedURLException e) { e.printStackTrace();
}
}
...
Di seguito è mostrato il metodo
findCharacter, richiamato ogni qualvolta il server si ritrova con un metodo
GET e un URI che finisce con
/characters/{characterId} , dove per
characterID c'è un numero intero che rappresenta l'ID del personaggio all'interno del database: ciò viene fatto mediante l'annotazione
@RequestMapping.
...
@RequestMapping(value = "/characters/{characterId}", method = RequestMethod.GET) @ResponseBody
public Character findCharacter(@PathVariable int characterId) { return characters.get(characterId);
}
}
Inoltre l'annotazione
@ResponseBody fa sì che l'oggetto restituito dal metodo venga scritto all'interno del body della risposta HTTP. A questo punto non ci rimane che effettuare i test del nostro servizio RESTful utilizzando strumenti quali
Rest client per avere più dettagli sui test si rimanda all'articolo di
JavaCodeGeek