Beliebte Suchanfragen
//

Einführung in Akka Http Path Directives

17.10.2016 | 4 Minuten Lesezeit

In vielen Web-Frameworks werden die Pfade einer Webanwendung mithilfe von speziellen Konfigurationsdateien oder per Annotations definiert. Akka bietet eine ausgezeichnete DSL, um Pfade programmatisch festzulegen. Dabei werden Prinzipien wie DRY und Separation of Concerns sehr gut unterstützt. In diesem Artikel sollen die dazu nutzbaren Direktiven und ihre Nutzung anhand eines kurzen Beispiels eingeführt werden.

Zu Beginn starten wir mit einem einfachen Server, der beim Aufruf von http://localhost:8080/my-path mit dem Text My-Path antwortet.

1object Main extends App {
2 implicit val system = ActorSystem()
3 implicit val executor = system.dispatcher
4 implicit val materializer = ActorMaterializer()
5 
6 val paths = {
7   path("my-path") {
8     complete("My-Path")
9   }
10 }
11 
12 Http().bindAndHandle(paths, "localhost", 8080)
13 
14 println(s"Server online at http://localhost:8080")
15}

Wichtig ist dabei, dass der Aufruf exakt http://localhost:8080/my-path lautet – der Aufruf von http://localhost:8080/my-path/ ist nicht erfolgreich, sondern wird mit The requested resource could not be found. beantwortet.

Um den Server robuster zu gestalten, so dass beide Anfragen erfolgreich sind, ist eine kleine Anpassung der Pfad-Definition notwendig.

1val paths = {
2  pathPrefix("my-path") {
3    pathEndOrSingleSlash {
4      complete("My-Path")
5    }
6  }
7}

Path-Direktiven lassen sich also sehr leicht kombinieren und verschachteln.

Ist der Pfad vollständig, lässt sich diese Pfadangabe auch kürzer schreiben.

1val paths = {
2   (pathPrefix("my-path") & pathEndOrSingleSlash) {
3       complete("My-Path")
4   }
5 }

Da für ähnliche Konstellationen Code wiederholt werden würde, lassen sie sich derartige Kombinationen zur Wiederverwendung auslagern.

1trait ConcisePaths {
2 
3  def concisePath(segment: String) = {
4    pathPrefix(segment) & pathEndOrSingleSlash
5  }
6}

Im Trait ConcisePaths ist die Kombination aus pathPrefix und pathEndOrSingleSlash bereitgestellt und kann bei der Definition der Pfade verwendet werden.

1object Main extends App with ConcisePaths {
2  implicit val system = ActorSystem()
3  implicit val executor = system.dispatcher
4  implicit val materializer = ActorMaterializer()
5 
6  val paths = {
7    concisePath("my-path") {
8        complete("My-Path")
9    }
10  }
11 
12  Http().bindAndHandle(paths, "localhost", 8080)
13 
14  println(s"Server online at http://localhost:8080")
15}

Eine genauere Auswertung des Requests ist weiterhin möglich. Hier wird nicht mehr jede Request-Methode gleich behandelt, sondern zwischen POST und GET unterschieden. Die jeweiligen Direktiven werden dabei mit ~ im Block der Pfad-Behandlung miteinander konkateniert.

1val paths = {
2  concisePath("my-path") {
3    get {
4       complete("GET My-Path")
5    } ~
6    post {
7        complete("POST My-Path")
8    }
9  }
10}

Darüber hinaus lassen sich default- bzw. globale Behandlungen definieren. Der folgende Code gibt den Text DEFAULT My-Path aus, wenn die Anfrage nicht mit GET oder POST erfolgt.

1val paths = {
2   concisePath("my-path") {
3      get {
4        complete("GET My-Path")
5      } ~
6      post {
7        complete("POST My-Path")
8      } ~
9      complete("DEFAULT My-Path")
10    }

Bisher kann unser Beispiel-Server nur auf einen Pfad reagieren. Das Hinzufügen weiterer Pfad-Definitionen ist ebenfalls möglich, indem ein weiterer Pfad konkateniert wird.

1val paths = {
2    concisePath("my-path") {
3      get {
4        complete("GET My-Path")
5      } ~
6      post {
7        complete("POST My-Path")
8      } ~
9      complete("DEFAULT My-Path")
10    } ~
11    path("my-other-path1") {
12      complete("My-Other-Path1")
13    }

Da bei vielen Pfaden die Übersicht verloren gehen kann, kann die Pfade-Definition ganz oder teilweise ausgelagert werden, bspw. in einen Trait: Im Trait OtherPaths wird der Pfad my-other-path2 gesetzt. Die Behandlung des Pfades erfolgt mit oder ohne Slash am Ende. Außerdem sind zwei untergeordnete Pfade definiert, Alle Behandlungen innerhalb des Blocks von pathPrefix(„my-other-path2“) sind mit der Tilde verknüpft. Während der erste untergeordnete Pfad exakt auf my-other-path2/sub1 festgelegt ist, behandelt der zweite sowohl Anfrage mit oder ohne Slash am Ende, da concisePath aus dem Trait ConcisePaths verwendet wird, der durch diesen Trait erweitert wird.

1// OtherPaths.scala
2trait OtherPaths extends ConcisePaths {
3 
4  val otherPathWithSubPaths = {
5    pathPrefix("my-other-path2") {
6      pathEndOrSingleSlash {
7        complete("my-other-path2/")
8      } ~
9      path("sub1") {
10          complete("sub1")
11      } ~
12      concisePath("sub2") {
13        complete("sub2")
14      }
15    }
16  }
17}

OtherPaths wird anstelle von ConcisePaths verwendet und der value otherPathWithSubPaths wird als weiterer Pfad angehängt, um auf die ausgelagerte Path-Definition zuzugreifen.

1//Main.scala
2object Main extends App with OtherPaths {
3  implicit val system = ActorSystem()
4  implicit val executor = system.dispatcher
5  implicit val materializer = ActorMaterializer()
6 
7 
8 
9  val paths = {
10    concisePath("my-path") {
11      get {
12        complete("GET My-Path")
13      } ~
14      post {
15        complete("POST My-Path")
16      } ~
17      complete("DEFAULT My-Path")
18    } ~
19    path("my-other-path1") {
20      complete("My-Other-Path1")
21    } ~
22    otherPathWithSubPaths
23  }
24 
25  Http().bindAndHandle(paths, "localhost", 8080)
26 
27  println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
28}

Fazit

Mithilfe der Path-Directives lassen sich Pfade sehr leicht beschreiben und bspw. in passende Module auslagern. Mithilfe der DSL lassen sich die Pfade je nach Bedarf sowohl grob- als auch feingranular behandeln. Die DSL selbst lässt sich sehr leicht auf die jeweiligen Bedürfnisse anpassen sowie spezialisieren. Gemäß dem Prinzip DRY können diese Funktionalitäten ausgelagert werden.

Um tiefer einzusteigen, bietet die Akka Http Doku einen übersichtlichen Einstiegspunkt über die Routing-DSL .

Der Quellcode mit dem letzten Stand befindet sich in diesem Repo .

Beitrag teilen

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.