Du hast deinen AdBlocker an?

Es wäre ein Traum, wenn du ihn für t3n.de deaktivierst. Wir zeigen dir gerne, wie das geht. Und natürlich erklären wir dir auch, warum uns das so wichtig ist. Digitales High-five, deine t3n-Redaktion

t3n 39

RESTful entwickeln mit TYPO3 Flow: Der Weg zum eigenen Webservice

(Screenshot: commons.wikimedia.org)

Webservices gehören zu den wichtigsten Bestandteilen des WWW, denn sie ermöglichen die Kommunikation von Anwendungen untereinander. In den letzten Jahren hat sich der „Representational State Transfer“ als Standard-Architekturstil dafür etabliert. Dieser Artikel zeigt, wie sich standardkonforme REST-Webservices auf Grundlage von TYPO3 Flow entwickeln lassen.

Webservices sind ein essenzieller Bestandteil des Webs. Sie sorgen dafür, dass Anwendungen nicht nur von menschlichen Nutzern, sondern auch von anderen Applikationen genutzt werden können. Nahezu alle bekannten Betreiber bieten solche Schnittstellen an, sodass Anwendungsentwickler zum Beispiel die Dienste von Twitter oder Facebook direkt in eigene Anwendungen integrieren können. Lange Jahre waren XML-basierte Technologien wie etwa XML-RPC oder SOAP das Mittel der Wahl, um solche Webservices zu implementieren. Diese haben jedoch den Nachteil, dass sie ineffizient und übermäßig komplex sind. Speziell für SOAP existieren beispielsweise einige hundert Zusatz-Standards, über die niemand mehr einen richtigen Überblick hat.

Als „einfachere“ Alternative hat sich in den letzten Jahren der REST-Architekturstil etabliert. Er besticht durch seine Einfachheit und harmonisiert gut mit den ursprünglichen Architekturprinzipien des WWW. Anstatt wie SOAP das Rad in immer neuen Spezifikationen neu zu erfinden, nutzen REST-Webservices geschickt die Funktionen des gewählten Übertragungsprotokolls aus (meist HTTP). Sinnvolle Funktionen wie Caching und Skalierbarkeit gibt es dabei quasi gratis dazu.

Architekturprinzipien

Der Begriff des Representational State Transfer (kurz REST) – ursprünglich 2000 von Roy Fielding geprägt – beschreibt einen Architekturstil für verteilte Anwendungen. Dieser ist in der Theorie unabhängig von konkreten Übertragungsprotokollen. In der Regel wird allerdings HTTP als Protokoll genutzt.

Grundlegende Bausteine eines REST-Services sind die Ressourcen, also die von einer Anwendung modellierten Geschäftsobjekte. Jede davon muss laut Fielding die folgenden Bedingungen erfüllen:

  • Eindeutige Adressierbarkeit über einen Unique Resource Identifier (URI) – ein Produkt in einem Warenwirtschaftssystem wäre etwa über die URI http://ihre.url/products/12345 eindeutig identifizierbar.
  • Der Zugriff auf Ressourcen ist zustandslos. Dies bedeutet, dass bei jeder Anfrage alle Informationen mitzuschicken sind, die der Server zur Verarbeitung der Anfrage benötigt. Ein Beispiel dafür sind Authentifizierungs-Daten. Benutzersitzungen, etwa in Form von Sessions und Cookies, sind nicht vorgesehen.
  • Alle Ressourcen müssen den Zugriff über einen einheitlichen Satz von Operationen erlauben. HTTP bietet hierzu (unter anderem) die Methoden GET, POST, PUT und DELETE an.
  • Jede Ressource kann verschiedene Repräsentationen haben (beispielsweise als JSON, XML und HTML). Clients können explizit bestimmte Repräsentationsformen anfordern.

Für den Einsatz von REST wird häufig auf HTTP als Übertragungsprotokoll gesetzt. Hierfür stehen die HTTP-Methoden GET, POST, PUT und DELETE zur Verfügung. (Grafik: Martin Helmich)
Für den Einsatz von REST wird häufig auf HTTP als Übertragungsprotokoll gesetzt. Hierfür stehen die HTTP-Methoden GET, POST, PUT und DELETE zur Verfügung. (Grafik: Martin Helmich)

RESTful HTTP mit TYPO3 Flow

RestController und JsonView

Seit TYPO3 Flow 2.3 lassen sich REST-Webservices über einen herkömmlichen ActionController abbilden. Um Konfigurationsarbeit zu sparen, kann auch vom RestController geerbt werden. Der einzige Trick besteht darin, die einzelnen Action-Methoden nicht über verschiedene URLs, sondern über verschiedene HTTP-Methoden anzusprechen. Dies kann später über das Routing erfolgen, beispielsweise nach dem Schema in der folgenden Tabelle:

HTTP-Verb Action-Methode
GET listAction() showAction($resource)
POST createAction($resource)
PUT updateAction($resource)
DELETE deleteAction($resource)

Um maschinenlesbare Antworten ausgeben zu können, setzt Flow außerdem auf die JsonView-Klasse, die PHP-Objekte ins JSON-Format übersetzt. Die JSON-Repräsentation wird dabei aus den aufrufbaren get...()-Methoden einer Klasse generiert.

Listing 1 zeigt als Beispiel einen einfachen Controller zur Verwaltung von Produkten. Dieser kommt bisher nicht ganz ohne eigene Konfiguration aus. Über das Attribut $supportedMediaTypes muss explizit definiert werden, dass der Controller JSON als Inhaltstyp unterstützt (1). Das Attribut $viewFormatToObjectNameMap übernimmt anschließend die Verknüpfung des JSON-Formats mit der JsonView-Klasse (2).

Einfacher Controller zur Verwaltung von Produkten

class ProductController extends RestController {

	/** @var ProductRepository
	*  @Flow\Inject */
	protected $productRepository;

	protected $supportedMediaTypes = ['application/json']; /* (1) */

	protected $viewFormatToObjectNameMap = [
		'json' => \TYPO3\Flow\Mvc\View\JsonView::class]; /* (2) */

	public function listAction() {
		$this->view->assign('products', $this->productRepository->findAll());

	public function showAction(Product $product) {
		$this->view->assign('product', $product);
	}

	public function createAction(Product $product) {
		$this->productRepository->add($product);
		$this->response->setStatus(201);
	}

	public function updateAction(Product $product) {
		$this->productRepository->update($product);
	}

	public function deleteAction(Product $product) {
		$this->productRepository->remove($product);
	}
}

Listing 1

Routing

Damit Flow die entsprechenden Action-Methoden korrekt aufruft, benötigt das Routing eine entsprechende Konfiguration. Listing 2 zeigt dafür ein Beispiel. Das uriPattern variiert nur geringfügig. Die Routen werden hauptsächlich über die HTTP-Methode differenziert.

Routing

- name: Product API (list)
  uriPattern: products
  defaults:
    @package: Helmich.RestExample
	@controller: Product
	@action: index
	@format: json
  httpMethods: [GET]

- name: Product API (create)
  uriPattern: products
  defaults: {"@action": "create"} # Restliche Defaults ausgelassen
  httpMethods: [POST]

- name: Product API (show)
  uriPattern: products/{product.__identifier}
  defaults: {"@action": "show"}
  httpMethods: [GET]

# Und so weiter für PUT/update und DELETE

Listing 2

Ob der Webservice funktioniert, lässt sich anschließend mit ein paar kurzen Kommandozeilen-Aufrufen testen:

Webservice testen

> curl –X POST –H'Content-Type: application/json' \
	–d'{"product":{"name":"Test","quantity":3}}' \
	http://flow.local/products
> curl –X GET http://flow.local/products

Listing 3

Ein Schnelltest mit einem Konsolenwerkzeug wie HTTPie (http://httpie.org) ermittelt, ob der Webservice wie gewünscht funktioniert. (Screenshot: httpie.org)
Ein Schnelltest mit einem Konsolenwerkzeug wie HTTPie (http://httpie.org) ermittelt, ob der Webservice wie gewünscht funktioniert. (Screenshot: httpie.org)

Wenn Daten an den Controller übermittelt werden, greift das von TYPO3 Flow bekannte Property Mapping. Besitzt die oben verwendete Product-Klasse also die Methoden setName und setQuantity, wird automatisch eine passende Instanz der Klasse erstellt. Dabei ist es egal, ob die Daten JSON-, XML- oder URL-codiert sind. Wichtig ist lediglich, dass ein entsprechender Content-Type-Header dem Framework übermittelt, wie der Request zu verarbeiten ist.

Bitte beachte unsere Community-Richtlinien

4 Reaktionen
Murks

Ich sehe kein Typo3/Flow ich sehe nur ein Symfony erweitert um völligen Murks...
Dann doch lieber was gescheites anstatt sowas...
---

Werner

.. wäre ja nicht verkehrt gewesen, den Namen Rails im Artikel mal zu erwähnen..

Controller sieht da so aus:

class ArticlesController < ApplicationController

before_action :set_article, only: :show

def index
@articles = Article.online
end

def show
end

private
def set_article
@article = Article.find(params[:id])
end

end

Wenn ich das nun mit dem Typo code vergleiche..ist klar
warum ich Rails bevorzuge.

Du musst angemeldet sein, um einen Kommentar schreiben zu können.

Jetzt anmelden

Finde einen Job, den du liebst