REST mit Spring 3: @PathVariable und Co.

Keine Zeit? Hier die Kurzversion in 30 Sekunden.
- Mit
@Controllerund@RequestMappingbildet man URLs und HTTP-Methoden auf Java-Methoden ab.@PathVariableliest Teile der URL wie/kunden/{id}aus.@ResponseBodyund@RequestBodywandeln Objekte mit Jackson automatisch von und nach JSON.<mvc:annotation-driven/>richtet die Konverter ein, sofern Jackson im Klassenpfad liegt.- Als Client gibt es in Spring 3 den
RestTemplate.
In meinem Spring-MVC-Einstieg habe ich bisher Formulare und JSPs gebaut. Spring 3 kann aber auch REST-artige Schnittstellen recht entspannt, und das schauen wir uns heute an. Als Beispiel dient ein Kunden-Dienst. Voraussetzung sind Spring 3.0 und die Jackson-Bibliothek (1.x) im Klassenpfad.
Konfiguration
Die Konfiguration ist überschaubar:
<context:component-scan base-package="de.beispiel.web"/>
<mvc:annotation-driven/>mvc:annotation-driven schaltet die Annotationen für Controller ein und registriert Konverter für Nachrichten. Liegt Jackson im Klassenpfad, ist der MappingJacksonHttpMessageConverter automatisch dabei. Den DispatcherServlet in der web.xml braucht ihr natürlich trotzdem.
Ein Kunde per GET
Die Klasse Kunde ist ein ganz normales Bean mit Standardkonstruktor, Getter und Setter für id und name; Jackson braucht das.
@Controller
@RequestMapping("/kunden")
public class KundenController {
@Autowired
private KundenService service;
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public Kunde holen(@PathVariable("id") long id) {
Kunde k = service.finde(id);
if (k == null) {
throw new KundeNichtGefunden();
}
return k;
}
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class KundeNichtGefunden extends RuntimeException {
}Ein Aufruf von /kunden/42 liefert JSON zurück, ungefähr {"id":42,"name":"Meier"}. Die {id} in der URL landet über @PathVariable im Parameter. Den Namen schreibe ich lieber explizit hin, sonst ist Spring auf Debug-Informationen im Bytecode angewiesen. @ResponseBody sorgt dafür, dass der Rückgabewert nicht als Name einer View gedeutet, sondern direkt in den Body geschrieben wird. Die Exception mit @ResponseStatus ergibt für den Aufrufer einen 404.
Neuer Kunde per POST
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
@ResponseBody
public Kunde anlegen(@RequestBody Kunde kunde) {
return service.speichere(kunde);
}Hier macht @RequestBody das Gegenstück: Der JSON-Text aus der Anfrage wird in ein Kunde-Objekt umgewandelt. Der Client muss dafür den passenden Content-Type (application/json) mitschicken.
HTTP-Methoden und Statuscodes
Die Idee von REST ist ja, dass die Methode sagt, was passiert:
- GET holt etwas und verändert nichts
- POST legt neu an
- PUT ersetzt eine vorhandene Ressource
- DELETE löscht sie
Passend dazu gibt es Statuscodes: 200 für OK, 201 nach dem Anlegen, 404 wenn es die Ressource nicht gibt, 400 bei kaputten Eingaben. Schaut euch das ruhig mal an, wenn ihr eine Schnittstelle entwerft, es erspart später viel Rätselraten. Browser können aus einem Formular nur GET und POST, für PUT und DELETE braucht man dort Umwege.
Der Client: RestTemplate
Zum Ausprobieren, ohne selbst HTTP von Hand zu bauen, bringt Spring 3 den RestTemplate mit:
RestTemplate rest = new RestTemplate();
String url = "http://localhost:8080/app/kunden/{id}";
Kunde k = rest.getForObject(url, Kunde.class, 42);
Kunde neu = new Kunde();
neu.setName("Albers");
Kunde gespeichert = rest.postForObject(
"http://localhost:8080/app/kunden", neu, Kunde.class);Auch hier übernimmt Jackson das Umwandeln, wenn die Bibliothek im Klassenpfad liegt. Mir gefällt, dass man nicht mehr mit Streams und Strings hantiert.
Setzt ihr Spring für solche Schnittstellen schon ein? Oder nehmt ihr lieber etwas anderes? Schreibt mir, was ihr davon haltet.
Beitragsbild: eigene Collage mit Logos von Wikimedia Commons (JSON vector logo: Douglas Crockford, gemeinfrei); die Marken gehören ihren Inhabern.
Christian Ackermann
Softwareentwickler (C++, C, Java, C#) aus der Oberpfalz, bloggt seit 2001 über Linux, Technik und Kochen.