This is an automated email from the ASF dual-hosted git repository. fanningpj pushed a commit to branch wip-adding-tests in repository https://gitbox.apache.org/repos/asf/incubator-pekko-http-quickstart-scala-g8.git
commit 37de54b3d04a3cdf4e873b3c1813089eb52d6691 Author: Henrik Engstrom <[email protected]> AuthorDate: Wed Jul 19 18:45:12 2017 -0400 Text and structure updates based on feedback from Ruth. --- docs/src/main/paradox/backend.md | 14 +- docs/src/main/paradox/index.md | 170 ++++++++++++++++----- docs/src/main/paradox/intellij-idea.md | 26 +++- docs/src/main/paradox/json.md | 20 ++- docs/src/main/paradox/running-the-application.md | 100 +++--------- docs/src/main/paradox/server-class.md | 67 ++++---- .../akka/http/sample/QuickstartServer.scala | 9 -- 7 files changed, 215 insertions(+), 191 deletions(-) diff --git a/docs/src/main/paradox/backend.md b/docs/src/main/paradox/backend.md index 788a694..c0505a9 100644 --- a/docs/src/main/paradox/backend.md +++ b/docs/src/main/paradox/backend.md @@ -1,12 +1,10 @@ -Backend -------- +Backend logic +------------- -It is probably an exaggeration to call this for a "backend" since it, in this sample, only consists of a basic actor. In a real system, we would have many actors interacting with each other and perhaps a database or microservices. Also, we suggest designing the backend first and thereafter expose its functionality via an HTTP server. +In this example, the backend only uses one basic actor. In a real system, we would have many actors interacting with each other and perhaps, multiple data stores and microservices. However, the focus of this tutorial is on how to interact with a backend from within Akka HTTP -- not on the actor itself. -However, since the focus of this tutorial is on Akka HTTP, it is not very important what this backend does but more how to interact with a backend from within Akka HTTP. Hopefully, you already have a good grasp of how to communicate back and forth between Akka HTTP and actors by now. - -If you feel you should brush up your Akka Actor knowledge, then the [Quickstart guide]((http://developer.lightbend.com/guides/akka-quickstart-scala/)) for Akka actors tutorial is a good start. - -The sample code in the `UserRegistryActor` is very simple. It keeps registered users in a `Set`. Once it receives messages it will match those into what action it should take: +The sample code in the `UserRegistryActor` is very simple. It keeps registered users in a `Set`. Once it receives messages it matches them to the defined cases to determine which action to take: @@snip [UserRegistryActor.scala]($g8src$/scala/com/lightbend/akka/http/sample/UserRegistryActor.scala) + +If you feel you need to brush up on your Akka Actor knowledge, the [Getting Started Guide]((http://doc.akka.io/docs/akka/current/scala/guide/index.html)) reviews actor concepts in the context of a simple Internet of Things (IoT) example. diff --git a/docs/src/main/paradox/index.md b/docs/src/main/paradox/index.md index a1408a5..6877f63 100644 --- a/docs/src/main/paradox/index.md +++ b/docs/src/main/paradox/index.md @@ -1,26 +1,32 @@ -Akka HTTP Quickstart with Scala -=============================== +Akka HTTP Quickstart for Scala +============================== -The Akka HTTP modules implement a full server- and client-side HTTP stack on top of akka-actor and akka-stream. It’s not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services. While interaction with a browser is of course also in scope, it is not the primary focus of Akka HTTP. +Akka HTTP offers a general toolkit for providing and consuming HTTP-based services. The Akka HTTP modules implement a full server- and client-side HTTP stack on top of `akka-actor` and `akka-stream`. A typical application does not sit on top of Akka HTTP. Instead, Akka HTTP makes it easier to build integration layers based on HTTP, and therefore stays on the sidelines. This allows you to base your app on what makes sense and use Akka HTTP for HTTP integration. -Akka HTTP follows a rather open design and many times offers several different API levels for "doing the same thing." The users get to pick the API level of abstraction that is most suitable for their applications. This approach means that, if you have trouble achieving something using a high-level API, there’s a good chance that you can get it done with a low-level API, which offers more flexibility but might require you to write more application code. +Akka HTTP is not a framework--not because we don't like frameworks--but to provide maximum flexibility. For example, you might use the Play framework to implement browser-based interactions or Lagom framework for creating microservices, both of them are also based on Akka. -This Hello World example demonstrates some Akka HTTP fundamentals. Within 30 minutes, you should be able to download, run the sample application, and use this documentation to guide you through this example's building blocks. +Akka HTTP follows a rather open design and often offers several APIs for accomplishing the same thing. You can choose the API with the level of abstraction that best suits your application. If you have trouble achieving something using a high-level API, you can probably find a lower-level API to use. The low-level APIs offer more flexibility, but might require you to write more application code. + +This guide highlights Akka HTTP fundamentals in the context of a simple user registry [REST service](https://en.wikipedia.org/wiki/Representational_state_transfer). Within 30 minutes, you should be able to: + +* Download the quickstart project and run the app. +* Follow this guide to explore the code. +* Integrate the project into IntelliJ. If you prefer a different IDE, the principles should be similar, as long as it supports Scala. ## Prerequisite -Having a basic understanding of Akka actors will help the reader of this guide. There is a [Quickstart guide](https://developer.lightbend.com/guides/akka-quickstart-scala/) for Akka actors should you feel like brushing up your knowledge thereof. +Having a basic understanding of Akka actors will make it easier to understand the example. If you are new to Akka, we recommend completing the [Akka Quickstart](https://developer.lightbend.com/guides/akka-quickstart-scala/) guide before this tutorial. -## Downloading the example +You can run the Akka HTTP example project on Linux, MacOS, or Windows. The only prerequisite is Java 8. -The Hello World example for Scala is a zipped project that includes a distribution of sbt (build tool). You can run it on Linux, MacOS, or Windows. The only prerequisite is Java 8. +## Downloading the example -Download and unzip the example: +The Akka HTTP example for Scala is a zipped project that includes a distribution of the [sbt](http://www.scala-sbt.org) build tool. Download and unzip the example as follows: -1. Download the zip file from Lightbend Tech Hub by clicking `CREATE A PROJECT FOR ME`. +1. Download the project as a compressed file from Lightbend Tech Hub by clicking `CREATE A PROJECT FOR ME`. 2. Extract the zip file to a convenient location: -* On Linux and MacOS systems, open a terminal and use the command `unzip` akka-quickstart-scala.zip. Note: On OSX, if you unzip using Archiver, you also have to make the sbt files executable: +* On Linux and MacOS systems, open a terminal and use the command `unzip` akka-quickstart-scala.zip. Note: On OSX, if you unzip using Archiver, you also have to make the sbt files executable by entering the following two commands: ``` $ chmod u+x ./sbt @@ -35,29 +41,18 @@ To run Hello World: In a console, change directories to the top level of the unzipped project. -For example, if you used the default project name, `akka-http-quickstart-scala`, and extracted the project to your root directory, from the root directory, enter: +For example, if you used the default project name, `akka-http-quickstart-scala`, and extracted the project to your root directory, from the root directory, enter: `cd akka-http-quickstart-scala` -Enter `./sbt` on OSX/Linux or run `sbt.bat` on Windows to start [sbt](http://www.scala-sbt.org). +Start sbt: -sbt downloads project dependencies. The `>` prompt indicates sbt has started in interactive mode. + * On OSX or Linux systems, enter `./sbt` + * On Windows systems, enter `sbt.bat`. -At the sbt prompt, enter `run`. - -OSX/Linux -: ``` -$ cd akka-http-quickstart-scala -$ ./sbt -> run -``` +When you run sbt, it downloads project dependencies. The `>` prompt indicates that sbt is running in interactive mode. -Windows -: ``` -$ cd akka-http-quickstart-scala -$ sbt.bat -> run -``` +At the sbt prompt, enter `run`. -sbt builds the project and runs Hello World. +sbt builds the project, starts an Akka HTTP server, and runs the example application. The output should look something like this: @@ -69,25 +64,122 @@ Server online at http://localhost:8080/ Press RETURN to stop... ``` -The command above will start an Akka HTTP server. We will look at how to invoke the available endpoints of the server later in this guide. - -Congratulations, you just ran your first Akka HTTP app. Now take a look at what happened under the covers. +The Akka HTTP server is now running, and you can test it by sending simple HTTP requests. -## What Hello World does +## Example app overview -In this example, a simple user registry service represented by an actor is presented to consumers as a [REST service](https://en.wikipedia.org/wiki/Representational_state_transfer). +The user registry example contains functionality for adding, retrieving, or deleting a single user and for retrieving all users. Akka HTTP provides a [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) to simplify the definition of endpoints as a `Route`. In this example, a `Route` defines: the paths `/users` and `/user`, the available HTTP methods, and when applicable, parameters or payloads. -When Hello World starts up, it creates an ActorSystem and an instance of the `UserRegistryActor` that implements the functionality of the service. Then the HTTP service is started by binding so-called "routes" to the given interface and port, in this case, `localhost:8080`. Below is an overview of what things look like after the application has started: +When the example app starts up, it creates an ActorSystem with a `userRegistryActor` and binds the defined routes to a port, in this case, `localhost:8080`. When the endpoints are invoked, they interact with the `userRegistryActor`, which contains the business logic. The diagram below illustrates runtime behavior, where that the HTTP server receives for the defined `route` endpoints, which are handled by the `userRegistryActor`:  -The value `routes` defines the endpoint `/users` that route requests to the business logic actor. Routes are defined in terms of so-called "directives" which are basic building blocks of Akka Http that define how requests and responses are handled and how data is translated from HTTP to and from your domain objects. +## Exercising the example + +To get started, try registering and retrieving a few users. You simply need to know the appropriate parameters for the requests. In our example app, each user has a `name`, an `age`, and a `countryOfResidence`. + +You pass request parameters in JSON format. While you could do this in a browser, it is much simpler to use one of the following: + +* A command line tool, such as [cURL](https://en.wikipedia.org/wiki/CURL). +* Browser add-ons such as [RESTClient](http://restclient.net/) for Firefox or Postman for Chrome. + +Follow the steps appropriate for your tool: + +* [cURL commands](#cURL-commands) +* [Browser-based tools](#Browser-based-tools) + +### cURL commands + +Open a shell that supports cURL and follow these steps to add and retrieve users: + +Copy and paste the following lines to create a few users (enter one command at a time): + +``` +curl -H "Content-type: application/json" -X POST -d '{"name": "MrX", "age": 31, "countryOfResidence": "Canada"}' http://localhost:8080/users + +curl -H "Content-type: application/json" -X POST -d '{"name": "Anonymous", "age": 55, "countryOfResidence": "Iceland"}' http://localhost:8080/users + +curl -H "Content-type: application/json" -X POST -d '{"name": "Bill", "age": 67, "countryOfResidence": "USA"}' http://localhost:8080/users +``` + +The system should respond after each command to verify that the user was created. + +To retrieve all users, enter the following command: + +``` +curl http://localhost:8080/users +``` + +The system responds with the list of users: + +``` +{"users":[{"name":"Anonymous","age":55,"countryOfResidence":"Iceland"},{"name":"MrX","age":31,"countryOfResidence":"Canada"},{"name":"Bill","age":67,"countryOfResidence":"USA"}]} +``` + +To retrieve a specific user, enter the following command: + +``` +curl http://localhost:8080/users/Bill +``` + +The system should respond wiht the following: + +``` +{"name":"Bill","age":67,"countryOfResidence":"USA"} +``` + +Finally, it is possible to delete users: + +``` +curl -X DELETE http://localhost:8080/users/Bill +``` + +The response should be: + +``` +User Bill deleted. +``` + +### Browser-based clients + +Open a tool such as RESTClient or Postman and follow these steps to create and retrieve users: + +To create users: + +1. Select the `POST` method. +1. Enter the URL: `http://localhost:8080/users` +1. Set the Header to `Content-Type: application/json`. +1. In the body, copy and paste the following lines to create three users (send each payload separately). Note: for Postman, you might need to specify that you want to send `raw` data. + +``` +{"name": "MrX", "age": 31, "countryOfResidence": "Canada"} + +{"name": "Anonymous", "age": 55, "countryOfResidence": "Iceland"} + +{"name": "Bill", "age": 67, "countryOfResidence": "USA"} +``` + +The system should respond after each command to verify that the user was created. + +To retrieve all users: + +1. Select the `GET` method. +1. Enter the URL: `http://localhost:8080/users` +1. Send the request. + +The system should respond with a list of users in JSON format. + +## Learning from the example + +Congratulations, you just ran and exercised your first Akka HTTP app! You got a preview of how routes make it easy to communicate over HTTP. -In the following sections, we will take a detailed look at the individual pieces of this application. +The example is implemented in the following three source files: -## The Akka HTTP philosophy +* `QuickstartServer.scala` -- contains the main class and Akka HTTP `routes`. +* `UserRegistryActor.scala` -- implements the actor that handles registration. +* `JsonSupport.scala` -- converts the JSON data from requests into Scala types and from Scala types into JSON responses. -Akka HTTP is designed specifically as "not-a-framework," not because we don’t like frameworks, but for use cases where a framework is not the right choice. Akka HTTP is for building integration layers based on HTTP and as such tries to "stay on the sidelines." Therefore, a typical application doesn't sit on top of Akka HTTP but instead on top of whatever makes sense and uses Akka HTTP merely for the HTTP integration needs. Should you look for a web framework it is recommended to take a [...] +First, let's dissect the backend logic. @@@index diff --git a/docs/src/main/paradox/intellij-idea.md b/docs/src/main/paradox/intellij-idea.md index 9dd3629..6248de1 100644 --- a/docs/src/main/paradox/intellij-idea.md +++ b/docs/src/main/paradox/intellij-idea.md @@ -1,26 +1,36 @@ IntelliJ IDEA ------------- -[IntelliJ](https://www.jetbrains.com/idea/) from JetBrains is one of the leading IDEs in the Java/Scala community, and it has excellent support for Akka HTTP. This section will guide you through setting up, testing and running the sample project. +[IntelliJ IDEA](https://www.jetbrains.com/idea/) from JetBrains is one of the leading IDEs in the Java/Scala community, and it has excellent support for Akka HTTP. This section of the tutorial describes how to set up, test and run the sample project in IntelliJ. ## Setting up the project -Open IntelliJ and select File -> Open... and point to the directory where you have installed the sample project. There should be a pop-up like this: +To add the Akka HTTP example project to IntelliJ, follow these steps: - +1. Open IntelliJ. +1. Select **Open** from the Welcome dialog or **File > Open** from the Editor. +1. Browse to select the top-level directory of the sample project and click **OK**. -Fill out the settings according to the above and press `OK` to import the project. If IntelliJ will warn about missing Scala SDK, just follow the instructions to add support. +IntelliJ opens the project. -## Inspecting the code +## Removing comments -If we open up the file `src/main/scala/com/lightbend/akka/http/sample/QuickstartServer.scala` we can see a lot of lines beginning with //# .... These lines are used as directives for this documentation. To get rid of these lines from the source code we can utilize the awesome Find/Replace functionality in IntelliJ. Select Edit -> Find -> Replace in Path.... Check the Regex box and add the following regex `[//#].*` and click on Replace in Find Window.... Select to replace all occurrences [...] +The main source file contains comments with special directives used by the documentation. To get rid of these lines, you can use IntelliJ's awesome find and replace functionality. Follow these steps: + +1. From the Project pane, open the `QuickstartServer` source file. +1. From the **Edit** menu, select **Find > Replace**. +1. In the find box, enter `[//#].*`. +1. Check **Regex**. +1. Click **Replace**. + +Voila the lines are gone! ## Running the application -Right click on the file `src/main/scala/com/lightbend/akka/http/sample/QuickstartServer.scala` and select Run 'QuickstartServer' and the output should look like this: +In the **Project** pane, right click the `QuickstartServer` source file. Select **Run 'QuickstartServer'**. The output should look like the output in the shell:  ## Tutorial done! -Congratulations! We have now learned enough concepts to get started with building real-world Akka HTTP applications. Of course, there is plenty of more that we can do with Akka HTTP and the [documentation](http://doc.akka.io/docs/akka-http/current/scala/http/index.html) is a good starting point if there is something more you need. +Congratulations! You can start building real-world applications that use Akka HTTP. Of course, we didn't have time to cover all Akka HTTP features in this short guide. See the [documentation](http://doc.akka.io/docs/akka-http/current/scala/http/index.html) to learn more. diff --git a/docs/src/main/paradox/json.md b/docs/src/main/paradox/json.md index 06e09a8..69c2689 100644 --- a/docs/src/main/paradox/json.md +++ b/docs/src/main/paradox/json.md @@ -1,18 +1,22 @@ -JSON ----- +JSON conversion +--------------- -In the server class, we saw that JSON is somehow converted into Scala classes and vice versa. In this section, we shall look at how this is implemented and what you need to do to make it work. - -Let's take a look at the server class definition once again: +When exercising the app, you interacted with JSON payloads. How does the example app convert data between JSON format and data that can be used by Scala classes? The answer begins in the server class definition `JsonSupport` trait: @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #main-class } -See the `JsonSupport` up there? This is a trait that we have created and it looks like this: +This trait is implemented in the `JsonSupport.scala` source file: @@snip [JsonSupport.scala]($g8src$/scala/com/lightbend/akka/http/sample/JsonSupport.scala) -The above trait defines two implicit values; `userJsonFormat` and `usersJsonFormat`. To do so we use the `jsonFormatX` methods, from [Spray Json](https://github.com/spray/spray-json), where X is representing the number of parameters in the underlying case classes: +To handle the two different payloads, the trait defines two implicit values; `userJsonFormat` and `usersJsonFormat`. Defining the formatters as `implicit` ensures that the compiler can map the formatting functionality with the case classes to convert. + +The `jsonFormatX` methods come from [Spray Json](https://github.com/spray/spray-json). The `X` represents the number of parameters in the underlying case classes: @@snip [UserRegistryActor.scala]($g8src$/scala/com/lightbend/akka/http/sample/UserRegistryActor.scala) { #user-case-classes } -By defining the Formatters as `implicit`, we ensure that the compiler can map the formatting functionality with the case classes we want to convert. We won't go into the underlying functionality for how the formatters are implemented. All we have to remember for now is to define the formatters as implicit and that the Formatter used should map the number of parameters of "its" case class. +We won't go into how the formatters are implemented. All you need to remember for now is to define the formatters as implicit and that the formatter used should map the number of parameters belonging to the case class it converts. + +Comment: I was a bit confused by the previous paragraph. Does the user have to write their own formatters or are these available as libraries? + +Now that we've examined the example app thoroughly, let's test a few the remaining use cases. diff --git a/docs/src/main/paradox/running-the-application.md b/docs/src/main/paradox/running-the-application.md index 3142b52..d58681f 100644 --- a/docs/src/main/paradox/running-the-application.md +++ b/docs/src/main/paradox/running-the-application.md @@ -1,94 +1,30 @@ -Running the application ------------------------ +Final testing +------------- -You can run the Hello World application from the command line or an IDE. The final topic in this guide describes how to run it from IntelliJ IDEA. However, before we get there, let’s take a quick look at the build tool: sbt. +When you ran the example for the first time, you were able to create and retrieve multiple users. Now that you understand how the example is implemented, let's confirm that the rest of the functionality works. We want to verify that: -## The build files +* If we try to retrieve users when none exist, we get an empty list. +* If we try to retrieve a specific user that doesn't exist, we get an informative message. +* We can delete users. -sbt uses a build.sbt file to handle the project. This project’s build.sbt file looks like this: +To test this functionality, follow these steps. If you need reminders on starting the app or sending requests, refer to the @ref:[instructions](index.md#exercising-the-example) in the beginning. -@@snip [build.sbt]($g8root$/build.sbt) +1. If the Akka HTTP server is still running, stop and restart it. +2. With no users registered, use your tool of choice to: +3. Retrieve a list of users. Hint: use the `GET` method and append `/users` to the URL. -## Running the project +You should get back an empty list: `{"users":[]}` -We run the application from a console/terminal window and enter the following commands: +4. Try to retrieve a single user named `MrX`. Hint: use the `GET` method and append `users/MrX` to the URL. -OSX/Linux -: ``` -$ cd akka-http-quickstart-scala -$ ./sbt -> run -``` +You should get back the message: `User MrX is not registered.` -Windows -: ``` -$ cd akka-http-quickstart-scala -$ sbt.bat -> run -``` +5. Try adding one or more users. Hint: use the `POST` method, append `/users` to the URL, and format the data in JSON, similar to: `{"name":"MrX","age":31,"countryOfResidence":"Canada"}` -The output should look like this: +You should get back the message: `User MrX created.` -``` -... -[info] Running com.lightbend.akka.http.sample.QuickstartServer -Server online at http://localhost:8080/ -Press RETURN to stop... -``` +6. Try deleting a user you just added. Hint: use the `DELETE`, and append `/users/<NAME>` to the URL. -## Interacting with the application +You should get back the message: `User MrX deleted.` -The Akka HTTP server is now running, and we will use the [cURL](https://en.wikipedia.org/wiki/CURL) command to test the application. If you prefer to use your browser to test the service then a tool like [RESTClient](http://restclient.net/) may be good to install. - -Open another console/terminal window to investigate the functionality of the application. - -We start by looking at the existing users (there should be none as we just launched the application): - -``` -$ curl http://localhost:8080/users -{"users":[]} -``` - -The next step is to add a couple of users: -``` -$ curl -H "Content-type: application/json" -X POST -d '{"name": "MrX", "age": 31, "countryOfResidence": "Canada"}' http://localhost:8080/user -User MrX created. - -$ curl -H "Content-type: application/json" -X POST -d '{"name": "Anonymous", "age": 55, "countryOfResidence": "Iceland"}' http://localhost:8080/user -User Anonymous created. - -$ curl -H "Content-type: application/json" -X POST -d '{"name": "Bill", "age": 67, "countryOfResidence": "USA"}' http://localhost:8080/user -User Bill created. -``` - -We can try to retrieve user information for various users now: - -``` -$ curl http://localhost:8080/user/MrX -{"name":"MrX","age":31,"countryOfResidence":"Canada"} - -$ curl http://localhost:8080/user/SomeUnknownUser -User SomeUnknownUser is not registered. -``` - -Now, when we inquire the system for all existing users it looks like this: - -``` -$ curl http://localhost:8080/users -{"users":[{"name":"Anonymous","age":55,"countryOfResidence":"Iceland"},{"name":"MrX","age":31,"countryOfResidence":"Canada"},{"name":"Bill","age":67,"countryOfResidence":"USA"}]} -``` - -Next, we should make sure that the delete functionality works as expected: - -``` -$ curl -X DELETE http://localhost:8080/user/Bill -User Bill deleted. - -$ curl http://localhost:8080/user/Bill -User Bill is not registered. - -$ curl http://localhost:8080/users -{"users":[{"name":"Anonymous","age":55,"countryOfResidence":"Iceland"},{"name":"MrX","age":31,"countryOfResidence":"Canada"}]} -``` - -We have now tried all the functionality available in this sample. The next step is to see how we can use an IDE to work with the application. +Now that you've confirmed all of the example functionality, see how simple it is to integrate the project into an IDE. diff --git a/docs/src/main/paradox/server-class.md b/docs/src/main/paradox/server-class.md index daefde2..b4957fd 100644 --- a/docs/src/main/paradox/server-class.md +++ b/docs/src/main/paradox/server-class.md @@ -1,32 +1,33 @@ -The Akka HTTP server class --------------------------- +Server logic +------------ -Let's dissect the server class, `QuickstartServer`. We make this class runnable by extending `App` (we will discuss the trait `JsonSupport` later): +The main class, `QuickstartServer`, is runnable because it extends `App`, as shown in the following snippet. We will discuss the trait `JsonSupport` later. @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #main-class } -Now that we have a class to run we should add some Akka HTTP fundamentals with which we will build our RESTful web service. The aim is to use routes to define endpoints by choosing what to do for which requests. +## Binding endpoints -## Routes +Each Akka HTTP `Route` contains one or more `akka.http.scaladsl.server.Directives`, such as: `path`, `get`, `post`, `complete`, etc. There is also a [low-level API](http://doc.akka.io/docs/akka-http/current/scala/http/low-level-server-side-api.html) that allows to inspect requests and create responses manually. For the user registry service, the example needs to support the actions listed below. For each, we can identify a path, the HTTP method, and return value: -For our service we want to define the following endpoints: +|Functionality | Path | HTTP method | Returns | +|--------------------|------------|-----------------|----------------------| +| Create a user | /users | POST | Confirmation message | +| Retrieve a user | /users/$ID | GET | JSON payload | +| Remove a user | /users/$ID | DELETE | Confirmation message | +| Retrieve all users | /users | GET | JSON payload | -| Path | Http method | Intent | Returns | -|-------------|-----------------|--------------------|----------------------| -| /users | POST | Create a new user | Confirmation message | -| /users | GET | Retrieve all users | JSON payload | -| /users/$ID | GET | Retrieve a user | JSON payload | -| /users/$ID | DELETE | Remove a user | Confirmation message | +In the `QuickstartServer` source file, the definition of the `Route` begins with the line: +`lazy val routes: Route =`. -Akka HTTP provides a [domain-specific language](https://en.wikipedia.org/wiki/Domain-specific_language) (DSL) to simplify the routes/endpoints definition. Each route is composed of one or more `akka.http.scaladsl.server.Directives`, e.g. `path`, `get`, `post`, `complete`, etc. There is also a [low-level API](http://doc.akka.io/docs/akka-http/current/scala/http/low-level-server-side-api.html) that allows to inspect requests and create responses manually. +Let's look at the pieces of the example `Route` that bind the endpoints, HTTP methods, and message or payload for each action. -### Creating and retrieving a user +### Retrieving and creating users -Let us take a look at the source code for the first two endpoints, the `/users` path with `GET` and `POST` HTTP methods: +The definition of the endpoint to retrieve and create users look like the following: @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #users-get-post } -The snippet above contains a couple of interesting Akka HTTP building blocks: +Note the following building blocks from the snippet: **Generic functionality** @@ -47,11 +48,11 @@ The snippet above contains a couple of interesting Akka HTTP building blocks: ### Retrieving and removing a user -Next we need to define how to retrieve and remove a user, i.e. for the case when the URI `/users/$ID` is used where `$ID` is the id of the user: +Next, the example defines how to retrieve and remove a user. In this case, the URI must include the user's id in the form: `/users/$ID`. See if you can identify the code that handles that in the following snippet. This part of the route includes logic for both the GET and the DELETE methods. @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #users-get-delete } -This Route snippet contains a couple of interesting concepts: +This part of the `Route` contains the following: **Generic functionality** @@ -76,43 +77,35 @@ The `rejectEmptyResponse` here above is a convenience method that automatically The "business logic" for when deleting a user is straight forward; send an instruction about removing a user to the user registry actor, wait for the response and return an appropriate HTTP status code to the client. -### The complete Route +## The complete Route -Below is the complete `Route` definition used in the sample application: +Below is the complete `Route` definition from the sample application: @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #all-routes } -So far we have referred to `Route` without explaining what it is but now is the time to do so. Under the hood, Akka HTTP uses [Akka Streams](http://doc.akka.io/docs/akka/current/scala/stream/index.html). We don't have time to cover Akka Streams here, but if you are interested, you should take a look at the Hello World sample application for Akka Streams. Since Akka HTTP is built on top of Akka Streams, it means that some concepts of Akka Streams are available for us to use. In the case o [...] +## Binding the HTTP server -## Http server - -To set up an Akka HTTP server we must first define some implicit values that will be used by the server: +At the beginning of the `main` class, the example defines some implicit values that will be used by the Akka HTTP server: @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #server-bootstrapping } -What does the above mean and why do we need it? +Akka Streams uses these values: -* `ActorSystem` : the context in which actors will run. What actors, you may wonder? Akka Streams uses actors under the hood, and the actor system defined in this `val` will be picked up and used by Streams. +* `ActorSystem` : provides a context in which actors will run. What actors, you may wonder? Akka Streams uses actors under the hood, and the actor system defined in this `val` will be picked up and used by Streams. * `ActorMaterializer` : while the ActorSystem is the host of all thread pools and live actors, an ActorMaterializer is specific to Akka Streams and is what makes them run. The ActorMaterializer interprets stream descriptions into executable entities which are run on actors, and this is why it requires an ActorSystem to function. -With that defined we can move on to instantiate the server: +Further down in `QuickstartServer.scala`, you will find the code to instantiate the server: @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #http-server } -We provide three parameters; `routes`, the hostname, and the port. That's it! When running this program, we will have an Akka HTTP server on our machine (localhost) on port 8080. Note that starting a server happens asynchronously and therefore a `Future` is returned by the `bindAndHandle` method. - -We should also add code for stopping the server. To do so we use the `StdIn.readLine()` method that will wait until RETURN is pressed on the keyboard. When that happens we `flatMap` the `Future` returned when we started the server to get to the `unbind()` method. Unbinding is also an asynchronous function and when the `Future` returned by `unbind()` is completes we make sure that the actor system is properly terminated. - -## Error handling +The `bindAndhandle` method only takes three parameters; `routes`, the hostname, and the port. That's it! When this program runs--as you've seen--it starts an Akka HTTP server on localhost port 8080. Note that startup happens asynchronously and therefore the `bindAndHandle` method returns a `Future`. -Finally, we should take a look at how to [handle errors](http://doc.akka.io/docs/akka-http/current/scala/http/routing-dsl/exception-handling.html). We know, as the astute engineers we are, that errors will happen. We should prepare our program for this and error handling should not be an afterthought when we build systems. - -In this sample, we use a very simple exception handler which catches all unexpected exceptions and responds back to the client with an `InternalServerError` (HTTP status code 500) with an error message and for what URI the exception happened. We extract the URI by using the `extractUri` directive. - -@@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) { #exception-handler } +The code for stopping the server includes the `StdIn.readLine()` method that will wait until RETURN is pressed on the keyboard. When that happens, `flatMap` uses the `Future` returned when we started the server to get to the `unbind()` method. Unbinding is also an asynchronous function. When the `Future` returned by `unbind()` completes, the example code makes sure that the actor system is properly terminated. ## The complete server code Here is the complete server code used in the sample: @@snip [QuickstartServer.scala]($g8src$/scala/com/lightbend/akka/http/sample/QuickstartServer.scala) + +Let's move on to the actor that handles registration. diff --git a/src/main/g8/src/main/scala/com/lightbend/akka/http/sample/QuickstartServer.scala b/src/main/g8/src/main/scala/com/lightbend/akka/http/sample/QuickstartServer.scala index 3181755..45b5579 100644 --- a/src/main/g8/src/main/scala/com/lightbend/akka/http/sample/QuickstartServer.scala +++ b/src/main/g8/src/main/scala/com/lightbend/akka/http/sample/QuickstartServer.scala @@ -39,15 +39,6 @@ object QuickstartServer extends App with JsonSupport { // Required by the `ask` (?) method below implicit val timeout = Timeout(5 seconds) - //#exception-handler - implicit val exceptionHandler = ExceptionHandler { - case e: Exception => - extractUri { uri => - complete((StatusCodes.InternalServerError, s"Exception ${e.getMessage} happened for URI: $uri.")) - } - } - //#exception-handler - //#all-routes lazy val routes: Route = //#users-get-post --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
