REST-Services mit Spring und Maven (Teil 1) (Permalink)

Dieser Teil der Serie über Webprogrammierung mit dem Spring-Framework beschäftigt sich mit Representational State Transfer oder kurz REST. Bei REST handelt es sich um ein Programmierparadigma für Webanwendungen, welches von Roy Fielding in seiner Dissertation im Jahr 2000 geprägt wurde.

Design-Prinzipien bei REST

Wichtiges Element einer REST-Schnittstelle sind Ressourcen. Eine Ressource ist hierbei jedes Element der API, welches sinnvollerweise einzeln angesprochen werden kann.

  • Adressierbarkeit der einzelnen Ressourcen. Die Adressen bleiben idealerweise über Lebensspanne der Resource konstant.
  • Darstellung der abgefragten Ressourcen in verschiedenen Repräsentationen, beispielsweise JSON für Javascript, HTML oder als Grafikdatei zur Anzeige im Browser
  • REST ist Zustandslos.Die Zustandslosigkeit einer REST-Schnittstelle bedingt sich durch das verwendete HTTP(s)-Protokoll. Viele der Vorteile wie das Verwenden von Caches und die Skalierbarkeit ergeben sich aus diese Zustandslosigkeit.
  • REST verwendet die durch HTTP bekannten Operationen wie GET, POST, PUT und weiteren um Manipulationen an den Ressourcen vorzunehmen.

Basisoperationen zur Umsetzung von CRUD sind:

  • POST zum Anlegen (Create) neuer Ressourcen. Die Daten zum Anlegen der Ressource sind hierbei im Body des Requests enthalten, als Antwort erhält man die angelegte Ressource. Diese Funktion hat Seiteneffekte, wiederholtes Ausführen erzeugt neue Ressourcen mit gleichen Eigenschaften.
  • GET zum Abfragen (Read) von Ressourcen. Die Daten werden in der gewählten Repräsentation als Antwortet geliefert. Diese Funktion hat keine Seiteneffekte und kann beliebig wiederholt werden.
  • PUT zum Aktualisieren (Update) von Resourcen. Die aktualisierten Werte befinden sich im Body des Requests und die Antwort zeigt den neuen Zustand der Ressource. Diese Funktion hat Seiteneffekte, wiederholte Ausführung ist möglich.
  • DELETE zum Löschen einer Ressource. Diese Funktion hat Seiteneffekte, wiederholte Ausführung ist nicht möglich.

REST mit Spring

Ausgehend vom ersten Teil der Serie seien nun hier die Unterschiede erläutert.

1) An der Datei spring-servlet.xml ergeben sich folgende Änderungen:

  • ~~InternalResourceViewResolver~~ wird nicht mehr benötigt, da wir keine JSP-Dateien mehr als Views verwenden, sondern MessageConverter für XML und JSON
  • MappingJacksonHttpMessageConverter wird neu eingeführt, um Ergebnisse von Methoden in JSON zu konvertieren. Das benötigt die Abhängkeit  org.codehaus.jackson:jackson-mapper-asl (siehe pom.xml)
  • Jaxb2RootElementHttpMessageConverter ermöglicht die Wandlung von Ergebnistypen in XML. Eine Abhängigkeit wird hier nicht benötigt.
  • AnnotationMethodHandlerAdapter wird durch die Angabe einer Property **messageConverters ** ergänzt. Dadurch wird die automatische Wandlung in entsprechende Representationen ermöglicht.

Insgesamt ergibt sich folgender neuer Block:

<bean id="jacksonMessageConverter" class="...MappingJacksonHttpMessageConverter" />
<bean id="jaxbMessageConverter"  class="...Jaxb2RootElementHttpMessageConverter" />
<bean class="...AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <ref bean="jacksonMessageConverter"/>
            <ref bean="jaxbMessageConverter"/>
        </list>
    </property>
</bean>

2) In der Datei web.xml wird das url-pattern von *.html auf /rest/* geändert. Dadurch ergeben sich URL der Form http://HOST:PORT/CONTEXT/rest/persons/1, d.h. es wird klar, das es sich um eine REST-API handelt und gleichzeitig entfällt das Postfix .html.

3) Die Klassen PersonCreateDto und PersonDto sind Klassen zum Datentransfer. Die Annotation \@XmlRootElement dient dem Übersetzen der Instanzen der Klassen in XML und umgekehrt. Wird als Repräsentationen lediglich JSON benötigt,  so ist diese Annotation unnötig.

4) Die Klasse RestController exponiert die einzelnen Ressourcen und Methoden. Über Annotationen wird das Spring Framework genutzt um einzelne Anfragen auf Methoden der Klassen abzubilden.
Die Annotation \@Controller markiert die Klasse als für das Spring Framework.
Die Annotation \@RequestMapping legt dabei den letzten Teil der URL und HTTP Methode fest. Wird dabei eine Variable ("persons/{id}") in die URL eingebaut, so kann diese als Parameter mit der Annotation \@PathVariable verwendet werden.
Soll eine Methode im Erfolgsfall nicht den Statuscode 200 liefern, kann dies über die Annotation \@ResponseStatus festgelegt werden.
Die Annotationen \@RequestBody und \@ResponseBody ermöglicht den Transfer von Daten über den jeweiligen Body von Anfrage und Antwort.

5) Die Exception ResourceNotFoundException wird geworfen, wenn eine angefragte Resource nicht existiert.

Beispiel:

@RequestMapping(value = "persons/{id}", method = RequestMethod.DELETE)
@ResponseBody
@ResponseStatus(HttpStatus.GONE)
public boolean deletePerson(@PathVariable(value = "id") int id) {
    PersonDto toDelete = null;
    for (PersonDto person : persons) {
        if (id == person.getId()) {
            toDelete = person;
        }
    }
    if (toDelete != null) {
        return persons.remove(toDelete);
    } else {
        throw new ResourceNotFoundException();
    }
}

Nachdem Start der Anwendung beispielsweise in Tomcat können die Ressourcen unter der Wurzel-URL http://localhost:8080/RestBasics1/rest/ abgerufen werden. Mögliche URLs sind:

  • http://localhost:8080/RestBasics1/rest/persons mit Methoden GET und POST
  • http://localhost:8080/RestBasics1/rest/persons/{id} mit Methoden GET, DELETE und PUT

Diese URLs und Methoden können mit REST Clients wie SoapUI oder RESTClientverwendet werden.

Résumé

In diesem Teil werden noch keine Verknüpfungen zwischen den einzelnen Resourcen in den unterschiedlichen Representationen vorgenommen. Beispielsweise enthält die Resource /persons keine direkten Links auf die einzelnen Personen. Der Nutzer der API muss also den Link zu Jack (/persons/1) anderweitig erfahren.

<Response>
   <e>
      <email>foo@bar.com</email>
      <id>1</id>
      <name>Jack</name>
   </e>
</Response>

Unter dem Stichwort Hypermedia as the Engine of Application State versteht man in diesem Fall die Einbettung von Links direkt in die Resource. Besser wäre für obiges Beispiel also

<Response>
   <e>
      <email>foo@bar.com</email>      
      <name>Jack</name>
      <link xlink:href="../persons/1" />
   </e>
</Response>

Es bleiben also noch einige Sachen zu tun, aber die Basisarbeiten sind getan.

Update 6. Juni 2018

Die Quellen zu diesem Artikel findet man jetzt unter https://gitea.lusiardi.de/jlusiardi/restbasics1. Maven 3.3 kann in einem Docker-Container mit folgendem Kommando zum Build benutzt werden:

docker run -it --rm -v "$(pwd)":/usr/src/mymaven   
 -w /usr/src/mymaven maven:3.3-jdk-8 mvn clean install