=doc Significantly extend HTTP documentation with new content and ports from spray docs
This commit is contained in:
parent
6149b328a0
commit
20759e1b34
238 changed files with 6541 additions and 1563 deletions
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.stream.scaladsl._
|
||||
import akka.stream.testkit.AkkaSpec
|
||||
import scala.concurrent.Future
|
||||
|
||||
class HttpServerExampleSpec
|
||||
extends AkkaSpec("akka.actor.default-mailbox.mailbox-type = akka.dispatch.UnboundedMailbox") {
|
||||
def ActorSystem(): ActorSystem = system
|
||||
|
||||
"binding example" in {
|
||||
//#bind-example
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
|
||||
Http(system).bind(interface = "localhost", port = 8080)
|
||||
val bindingFuture: Future[Http.ServerBinding] = serverSource.to(Sink.foreach { connection =>
|
||||
// foreach materializes the source
|
||||
println("Accepted new connection from " + connection.remoteAddress)
|
||||
// ... and then actually handle the connection
|
||||
}).run()
|
||||
//#bind-example
|
||||
}
|
||||
|
||||
"full-server-example" in {
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val serverSource = Http(system).bind(interface = "localhost", port = 8080)
|
||||
|
||||
//#full-server-example
|
||||
import akka.http.scaladsl.model.HttpMethods._
|
||||
import akka.stream.scaladsl.{ Flow, Sink }
|
||||
|
||||
val requestHandler: HttpRequest => HttpResponse = {
|
||||
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
|
||||
HttpResponse(
|
||||
entity = HttpEntity(MediaTypes.`text/html`,
|
||||
"<html><body>Hello world!</body></html>"))
|
||||
|
||||
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
|
||||
HttpResponse(entity = "PONG!")
|
||||
|
||||
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
|
||||
sys.error("BOOM!")
|
||||
|
||||
case _: HttpRequest =>
|
||||
HttpResponse(404, entity = "Unknown resource!")
|
||||
}
|
||||
|
||||
val bindingFuture: Future[Http.ServerBinding] = serverSource.to(Sink.foreach { connection =>
|
||||
println("Accepted new connection from " + connection.remoteAddress)
|
||||
|
||||
connection handleWithSyncHandler requestHandler
|
||||
// this is equivalent to
|
||||
// connection handleWith { Flow[HttpRequest] map requestHandler }
|
||||
}).run()
|
||||
//#full-server-example
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
|
||||
class HttpClientExampleSpec extends WordSpec with Matchers {
|
||||
|
||||
"outgoing-connection-example" in {
|
||||
pending // compile-time only test
|
||||
//#outgoing-connection-example
|
||||
import scala.concurrent.Future
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.Http
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
|
||||
Http().outgoingConnection("http://akka.io")
|
||||
val responseFuture: Future[HttpResponse] =
|
||||
Source.single(HttpRequest(uri = "/"))
|
||||
.via(connectionFlow)
|
||||
.runWith(Sink.head)
|
||||
//#outgoing-connection-example
|
||||
}
|
||||
|
||||
"host-level-example" in {
|
||||
pending // compile-time only test
|
||||
//#host-level-example
|
||||
import scala.concurrent.Future
|
||||
import scala.util.Try
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.Http
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
// construct a pool client flow with context type `Int`
|
||||
val poolClientFlow = Http().cachedHostConnectionPool[Int]("http://akka.io")
|
||||
val responseFuture: Future[(Try[HttpResponse], Int)] =
|
||||
Source.single(HttpRequest(uri = "/") -> 42)
|
||||
.via(poolClientFlow)
|
||||
.runWith(Sink.head)
|
||||
//#host-level-example
|
||||
}
|
||||
|
||||
"single-request-example" in {
|
||||
pending // compile-time only test
|
||||
//#single-request-example
|
||||
import scala.concurrent.Future
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.Http
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val responseFuture: Future[HttpResponse] =
|
||||
Http().singleRequest(HttpRequest(uri = "http://akka.io"))
|
||||
//#single-request-example
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl
|
||||
|
||||
import scala.concurrent.Future
|
||||
import org.scalatest.{ WordSpec, Matchers }
|
||||
import akka.actor.ActorSystem
|
||||
|
||||
class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||
|
||||
"binding-example" in {
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.Http
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
|
||||
Http().bind(interface = "localhost", port = 8080)
|
||||
val bindingFuture: Future[Http.ServerBinding] =
|
||||
serverSource.to(Sink.foreach { connection => // foreach materializes the source
|
||||
println("Accepted new connection from " + connection.remoteAddress)
|
||||
// ... and then actually handle the connection
|
||||
}).run()
|
||||
}
|
||||
|
||||
"full-server-example" in {
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.stream.scaladsl.Sink
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model.HttpMethods._
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val serverSource = Http().bind(interface = "localhost", port = 8080)
|
||||
|
||||
val requestHandler: HttpRequest => HttpResponse = {
|
||||
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
|
||||
HttpResponse(entity = HttpEntity(MediaTypes.`text/html`,
|
||||
"<html><body>Hello world!</body></html>"))
|
||||
|
||||
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
|
||||
HttpResponse(entity = "PONG!")
|
||||
|
||||
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
|
||||
sys.error("BOOM!")
|
||||
|
||||
case _: HttpRequest =>
|
||||
HttpResponse(404, entity = "Unknown resource!")
|
||||
}
|
||||
|
||||
val bindingFuture: Future[Http.ServerBinding] =
|
||||
serverSource.to(Sink.foreach { connection =>
|
||||
println("Accepted new connection from " + connection.remoteAddress)
|
||||
|
||||
connection handleWithSyncHandler requestHandler
|
||||
// this is equivalent to
|
||||
// connection handleWith { Flow[HttpRequest] map requestHandler }
|
||||
}).run()
|
||||
}
|
||||
|
||||
"low-level-server-example" in {
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model.HttpMethods._
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val requestHandler: HttpRequest => HttpResponse = {
|
||||
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
|
||||
HttpResponse(entity = HttpEntity(MediaTypes.`text/html`,
|
||||
"<html><body>Hello world!</body></html>"))
|
||||
|
||||
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
|
||||
HttpResponse(entity = "PONG!")
|
||||
|
||||
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
|
||||
sys.error("BOOM!")
|
||||
|
||||
case _: HttpRequest =>
|
||||
HttpResponse(404, entity = "Unknown resource!")
|
||||
}
|
||||
|
||||
Http().bindAndHandleSync(requestHandler, "localhost", 8080)
|
||||
}
|
||||
|
||||
// format: OFF
|
||||
|
||||
"high-level-server-example" in {
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val route =
|
||||
get {
|
||||
pathSingleSlash {
|
||||
complete {
|
||||
<html>
|
||||
<body>Hello world!</body>
|
||||
</html>
|
||||
}
|
||||
} ~
|
||||
path("ping") {
|
||||
complete("PONG!")
|
||||
} ~
|
||||
path("crash") {
|
||||
sys.error("BOOM!")
|
||||
}
|
||||
}
|
||||
|
||||
Http().bindAndHandle(route, "localhost", 8080)
|
||||
}
|
||||
|
||||
"minimal-routing-example" in {
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
|
||||
|
||||
object Main extends App {
|
||||
implicit val system = ActorSystem("my-system")
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val route =
|
||||
path("hello") {
|
||||
get {
|
||||
complete {
|
||||
<h1>Say hello to spray</h1>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
|
||||
|
||||
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
||||
Console.readLine()
|
||||
|
||||
import system.dispatcher // for the future transformations
|
||||
bindingFuture
|
||||
.flatMap(_.unbind()) // trigger unbinding from the port
|
||||
.onComplete(_ ⇒ system.shutdown()) // and shutdown when done
|
||||
}
|
||||
}
|
||||
|
||||
"long-routing-example" in {
|
||||
import akka.actor.ActorRef
|
||||
import akka.util.Timeout
|
||||
import akka.pattern.ask
|
||||
import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
||||
import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
|
||||
import akka.http.scaladsl.model.StatusCodes.MovedPermanently
|
||||
import akka.http.scaladsl.coding.Deflate
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
|
||||
// types used by the API routes
|
||||
type Money = Double // only for demo purposes, don't try this at home!
|
||||
type TransactionResult = String
|
||||
case class User(name: String)
|
||||
case class Order(email: String, amount: Money)
|
||||
case class Update(order: Order)
|
||||
case class OrderItem(i: Int, os: Option[String], s: String)
|
||||
implicit val orderUM: FromRequestUnmarshaller[Order] = ???
|
||||
implicit val orderM: ToResponseMarshaller[Seq[Order]] = ???
|
||||
implicit val timeout: Timeout = ??? // for actor asks
|
||||
|
||||
val route = {
|
||||
path("orders") {
|
||||
authenticateBasic(realm = "admin area", myAuthenticator) { user =>
|
||||
get {
|
||||
encodeResponseWith(Deflate) {
|
||||
complete {
|
||||
// marshal custom object with in-scope marshaller
|
||||
retrieveOrdersFromDB
|
||||
}
|
||||
}
|
||||
} ~
|
||||
post {
|
||||
// decompress gzipped or deflated requests if required
|
||||
decodeRequest {
|
||||
// unmarshal with in-scope unmarshaller
|
||||
entity(as[Order]) { order =>
|
||||
complete {
|
||||
// ... write order to DB
|
||||
"Order received"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} ~
|
||||
// extract URI path element as Int
|
||||
pathPrefix("order" / IntNumber) { orderId =>
|
||||
pathEnd {
|
||||
(put | parameter('method ! "put")) {
|
||||
// form extraction from multipart or www-url-encoded forms
|
||||
formFields('email, 'total.as[Money]).as(Order) { order =>
|
||||
complete {
|
||||
// complete with serialized Future result
|
||||
(myDbActor ? Update(order)).mapTo[TransactionResult]
|
||||
}
|
||||
}
|
||||
} ~
|
||||
get {
|
||||
// debugging helper
|
||||
logRequest("GET-ORDER") {
|
||||
// use in-scope marshaller to create completer function
|
||||
completeWith(instanceOf[Order]) { completer =>
|
||||
// custom
|
||||
processOrderRequest(orderId, completer)
|
||||
}
|
||||
}
|
||||
}
|
||||
} ~
|
||||
path("items") {
|
||||
get {
|
||||
// parameters to case class extraction
|
||||
parameters('size.as[Int], 'color ?, 'dangerous ? "no")
|
||||
.as(OrderItem) { orderItem =>
|
||||
// ... route using case class instance created from
|
||||
// required and optional query parameters
|
||||
complete("") // hide
|
||||
}
|
||||
}
|
||||
}
|
||||
} ~
|
||||
pathPrefix("documentation") {
|
||||
// optionally compresses the response with Gzip or Deflate
|
||||
// if the client accepts compressed responses
|
||||
encodeResponse {
|
||||
// serve up static content from a JAR resource
|
||||
getFromResourceDirectory("docs")
|
||||
}
|
||||
} ~
|
||||
path("oldApi" / Rest) { pathRest =>
|
||||
redirect("http://oldapi.example.com/" + pathRest, MovedPermanently)
|
||||
}
|
||||
}
|
||||
|
||||
// backend entry points
|
||||
def myAuthenticator: Authenticator[User] = ???
|
||||
def retrieveOrdersFromDB: Seq[Order] = ???
|
||||
def myDbActor: ActorRef = ???
|
||||
def processOrderRequest(id: Int, complete: Order => Unit): Unit = ???
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
package docs.http
|
||||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl
|
||||
|
||||
//#import-model
|
||||
import akka.http.scaladsl.model._
|
||||
|
|
@ -7,19 +11,18 @@ import akka.http.scaladsl.model._
|
|||
|
||||
import akka.stream.testkit.AkkaSpec
|
||||
import akka.util.ByteString
|
||||
import akka.http.scaladsl.model.headers.{ GenericHttpCredentials, BasicHttpCredentials }
|
||||
import org.scalatest.MustMatchers
|
||||
import akka.http.scaladsl.model.headers.BasicHttpCredentials
|
||||
|
||||
class ModelSpec extends AkkaSpec {
|
||||
"construct request" in {
|
||||
//#construct-request
|
||||
import HttpMethods._
|
||||
|
||||
// construct simple GET request to `homeUri`
|
||||
// construct a simple GET request to `homeUri`
|
||||
val homeUri = Uri("/abc")
|
||||
HttpRequest(GET, uri = homeUri)
|
||||
|
||||
// construct simple GET request to "/index" which is converted to Uri automatically
|
||||
// construct simple GET request to "/index" (implicit string to Uri conversion)
|
||||
HttpRequest(GET, uri = "/index")
|
||||
|
||||
// construct simple POST request containing entity
|
||||
|
|
@ -70,11 +73,13 @@ class ModelSpec extends AkkaSpec {
|
|||
// create an ``Authorization`` header with HTTP Basic authentication data
|
||||
val auth = Authorization(BasicHttpCredentials("joe", "josepp"))
|
||||
|
||||
// a method that extracts basic HTTP credentials from a request
|
||||
// custom type
|
||||
case class User(name: String, pass: String)
|
||||
|
||||
// a method that extracts basic HTTP credentials from a request
|
||||
def credentialsOfRequest(req: HttpRequest): Option[User] =
|
||||
for {
|
||||
Authorization(BasicHttpCredentials(user, pass)) <- req.header[headers.Authorization]
|
||||
Authorization(BasicHttpCredentials(user, pass)) <- req.header[Authorization]
|
||||
} yield User(user, pass)
|
||||
//#headers
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
import akka.http.scaladsl.server._
|
||||
|
||||
class CaseClassExtractionExamplesSpec extends RoutingSpec {
|
||||
|
||||
// format: OFF
|
||||
|
||||
"example-1" in {
|
||||
case class Color(red: Int, green: Int, blue: Int)
|
||||
|
||||
val route =
|
||||
path("color") {
|
||||
parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]) { (red, green, blue) =>
|
||||
val color = Color(red, green, blue)
|
||||
// ... route working with the `color` instance
|
||||
null // hide
|
||||
}
|
||||
}
|
||||
Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(1,2,3)" } // hide
|
||||
}
|
||||
|
||||
"example-2" in {
|
||||
case class Color(red: Int, green: Int, blue: Int)
|
||||
|
||||
val route =
|
||||
path("color") {
|
||||
parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]).as(Color) { color =>
|
||||
// ... route working with the `color` instance
|
||||
null // hide
|
||||
}
|
||||
}
|
||||
Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(1,2,3)" } // hide
|
||||
}
|
||||
|
||||
"example-3" in {
|
||||
case class Color(name: String, red: Int, green: Int, blue: Int)
|
||||
|
||||
val route =
|
||||
(path("color" / Segment) & parameters('r.as[Int], 'g.as[Int], 'b.as[Int]))
|
||||
.as(Color) { color =>
|
||||
// ... route working with the `color` instance
|
||||
null // hide
|
||||
}
|
||||
Get("/color/abc?r=1&g=2&b=3") ~> route ~> check { responseAs[String] shouldEqual "Color(abc,1,2,3)" } // hide
|
||||
}
|
||||
|
||||
//# example-4
|
||||
case class Color(name: String, red: Int, green: Int, blue: Int) {
|
||||
require(!name.isEmpty, "color name must not be empty")
|
||||
require(0 <= red && red <= 255, "red color component must be between 0 and 255")
|
||||
require(0 <= green && green <= 255, "green color component must be between 0 and 255")
|
||||
require(0 <= blue && blue <= 255, "blue color component must be between 0 and 255")
|
||||
}
|
||||
//#
|
||||
|
||||
"example 4 test" in {
|
||||
val route =
|
||||
(path("color" / Segment) &
|
||||
parameters('r.as[Int], 'g.as[Int], 'b.as[Int])).as(Color) { color =>
|
||||
doSomethingWith(color) // route working with the Color instance
|
||||
}
|
||||
Get("/color/abc?r=1&g=2&b=3") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Color(abc,1,2,3)"
|
||||
}
|
||||
Get("/color/abc?r=1&g=2&b=345") ~> route ~> check {
|
||||
rejection must beLike {
|
||||
case ValidationRejection("requirement failed: blue color component must be between 0 and 255", _) => ok
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
import akka.http.scaladsl.server._
|
||||
import Directives._
|
||||
|
|
@ -11,7 +11,9 @@ import org.scalatest._
|
|||
|
||||
class DirectiveExamplesSpec extends RoutingSpec {
|
||||
|
||||
"example-1, example-2" in {
|
||||
// format: OFF
|
||||
|
||||
"example-1" in {
|
||||
val route: Route =
|
||||
path("order" / IntNumber) { id =>
|
||||
get {
|
||||
|
|
@ -19,37 +21,49 @@ class DirectiveExamplesSpec extends RoutingSpec {
|
|||
"Received GET request for order " + id
|
||||
}
|
||||
} ~
|
||||
put {
|
||||
complete {
|
||||
"Received PUT request for order " + id
|
||||
}
|
||||
put {
|
||||
complete {
|
||||
"Received PUT request for order " + id
|
||||
}
|
||||
}
|
||||
}
|
||||
verify(route) // hide
|
||||
}
|
||||
|
||||
"example-3" in {
|
||||
"example-2" in {
|
||||
def innerRoute(id: Int): Route =
|
||||
get {
|
||||
complete {
|
||||
"Received GET request for order " + id
|
||||
}
|
||||
} ~
|
||||
put {
|
||||
complete {
|
||||
"Received PUT request for order " + id
|
||||
}
|
||||
put {
|
||||
complete {
|
||||
"Received PUT request for order " + id
|
||||
}
|
||||
}
|
||||
|
||||
val route: Route = path("order" / IntNumber) { id => innerRoute(id) }
|
||||
verify(route) // hide
|
||||
}
|
||||
|
||||
"example-3" in {
|
||||
val route =
|
||||
path("order" / IntNumber) { id =>
|
||||
(get | put) { ctx =>
|
||||
ctx.complete(s"Received ${ctx.request.method.name} request for order $id")
|
||||
}
|
||||
}
|
||||
verify(route) // hide
|
||||
}
|
||||
|
||||
"example-4" in {
|
||||
val route =
|
||||
path("order" / IntNumber) { id =>
|
||||
(get | put) { ctx =>
|
||||
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
|
||||
(get | put) {
|
||||
extractMethod { m =>
|
||||
complete(s"Received ${m.name} request for order $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
verify(route) // hide
|
||||
|
|
@ -59,8 +73,10 @@ class DirectiveExamplesSpec extends RoutingSpec {
|
|||
val getOrPut = get | put
|
||||
val route =
|
||||
path("order" / IntNumber) { id =>
|
||||
getOrPut { ctx =>
|
||||
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
|
||||
getOrPut {
|
||||
extractMethod { m =>
|
||||
complete(s"Received ${m.name} request for order $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
verify(route) // hide
|
||||
|
|
@ -69,51 +85,18 @@ class DirectiveExamplesSpec extends RoutingSpec {
|
|||
"example-6" in {
|
||||
val getOrPut = get | put
|
||||
val route =
|
||||
(path("order" / IntNumber) & getOrPut) { id =>
|
||||
ctx =>
|
||||
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
|
||||
(path("order" / IntNumber) & getOrPut & extractMethod) { (id, m) =>
|
||||
complete(s"Received ${m.name} request for order $id")
|
||||
}
|
||||
verify(route) // hide
|
||||
}
|
||||
|
||||
"example-7" in {
|
||||
val orderGetOrPut = path("order" / IntNumber) & (get | put)
|
||||
val orderGetOrPutWithMethod =
|
||||
path("order" / IntNumber) & (get | put) & extractMethod
|
||||
val route =
|
||||
orderGetOrPut { id =>
|
||||
ctx =>
|
||||
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
|
||||
}
|
||||
verify(route) // hide
|
||||
}
|
||||
|
||||
"example-8" in {
|
||||
val orderGetOrPut = path("order" / IntNumber) & (get | put)
|
||||
val requestMethod = extract(_.request.method)
|
||||
val route =
|
||||
orderGetOrPut { id =>
|
||||
requestMethod { m =>
|
||||
complete("Received " + m.name + " request for order " + id)
|
||||
}
|
||||
}
|
||||
verify(route) // hide
|
||||
}
|
||||
|
||||
"example-9" in {
|
||||
val orderGetOrPut = path("order" / IntNumber) & (get | put)
|
||||
val requestMethod = extract(_.request.method)
|
||||
val route =
|
||||
(orderGetOrPut & requestMethod) { (id, m) =>
|
||||
complete("Received " + m.name + " request for order " + id)
|
||||
}
|
||||
verify(route) // hide
|
||||
}
|
||||
|
||||
"example-A" in {
|
||||
val orderGetOrPutMethod =
|
||||
path("order" / IntNumber) & (get | put) & extract(_.request.method)
|
||||
val route =
|
||||
orderGetOrPutMethod { (id, m) =>
|
||||
complete("Received " + m.name + " request for order " + id)
|
||||
orderGetOrPutWithMethod { (id, m) =>
|
||||
complete(s"Received ${m.name} request for order $id")
|
||||
}
|
||||
verify(route) // hide
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
// format: OFF
|
||||
|
||||
object MyExplicitExceptionHandler {
|
||||
|
||||
//#explicit-handler-example
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server._
|
||||
import StatusCodes._
|
||||
import Directives._
|
||||
|
||||
val myExceptionHandler = ExceptionHandler {
|
||||
case _: ArithmeticException =>
|
||||
extractUri { uri =>
|
||||
println(s"Request to $uri could not be handled normally")
|
||||
complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!"))
|
||||
}
|
||||
}
|
||||
|
||||
object MyApp extends App {
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val route: Route =
|
||||
handleExceptions(myExceptionHandler) {
|
||||
// ... some route structure
|
||||
null // hide
|
||||
}
|
||||
|
||||
Http().bindAndHandle(route, "localhost", 8080)
|
||||
}
|
||||
//#
|
||||
}
|
||||
|
||||
object MyImplicitExceptionHandler {
|
||||
|
||||
//#implicit-handler-example
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server._
|
||||
import StatusCodes._
|
||||
import Directives._
|
||||
|
||||
implicit def myExceptionHandler: ExceptionHandler =
|
||||
ExceptionHandler {
|
||||
case _: ArithmeticException =>
|
||||
extractUri { uri =>
|
||||
println(s"Request to $uri could not be handled normally")
|
||||
complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!"))
|
||||
}
|
||||
}
|
||||
|
||||
object MyApp extends App {
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val route: Route =
|
||||
// ... some route structure
|
||||
null // hide
|
||||
|
||||
Http().bindAndHandle(route, "localhost", 8080)
|
||||
}
|
||||
//#
|
||||
}
|
||||
|
||||
class ExceptionHandlerExamplesSpec extends RoutingSpec {
|
||||
|
||||
"test explicit example" in {
|
||||
Get() ~> handleExceptions(MyExplicitExceptionHandler.myExceptionHandler) {
|
||||
_.complete((1 / 0).toString)
|
||||
} ~> check {
|
||||
responseAs[String] === "Bad numbers, bad result!!!"
|
||||
}
|
||||
}
|
||||
|
||||
"test implicit example" in {
|
||||
import akka.http.scaladsl.server._
|
||||
import MyImplicitExceptionHandler.myExceptionHandler
|
||||
Get() ~> Route.seal(ctx => ctx.complete((1 / 0).toString)) ~> check {
|
||||
responseAs[String] === "Bad numbers, bad result!!!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
// format: OFF
|
||||
|
||||
//# source-quote
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
import akka.http.scaladsl.model.StatusCodes
|
||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||
import akka.http.scaladsl.server._
|
||||
import Directives._
|
||||
|
||||
class FullTestKitExampleSpec extends WordSpec with Matchers with ScalatestRouteTest {
|
||||
|
||||
val smallRoute =
|
||||
get {
|
||||
pathSingleSlash {
|
||||
complete {
|
||||
"Captain on the bridge!"
|
||||
}
|
||||
} ~
|
||||
path("ping") {
|
||||
complete("PONG!")
|
||||
}
|
||||
}
|
||||
|
||||
"The service" should {
|
||||
|
||||
"return a greeting for GET requests to the root path" in {
|
||||
Get() ~> smallRoute ~> check {
|
||||
responseAs[String] should contain("Captain on the bridge")
|
||||
}
|
||||
}
|
||||
|
||||
"return a 'PONG!' response for GET requests to /ping" in {
|
||||
Get("/ping") ~> smallRoute ~> check {
|
||||
responseAs[String] shouldEqual "PONG!"
|
||||
}
|
||||
}
|
||||
|
||||
"leave GET requests to other paths unhandled" in {
|
||||
Get("/kermit") ~> smallRoute ~> check {
|
||||
handled shouldBe false
|
||||
}
|
||||
}
|
||||
|
||||
"return a MethodNotAllowed error for PUT requests to the root path" in {
|
||||
Put() ~> Route.seal(smallRoute) ~> check {
|
||||
status === StatusCodes.MethodNotAllowed
|
||||
responseAs[String] shouldEqual "HTTP method not allowed, supported methods: GET"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//#
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
// format: OFF
|
||||
|
||||
object MyRejectionHandler {
|
||||
|
||||
//#custom-handler-example
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server._
|
||||
import StatusCodes._
|
||||
import Directives._
|
||||
|
||||
implicit def myRejectionHandler =
|
||||
RejectionHandler.newBuilder()
|
||||
.handle { case MissingCookieRejection(cookieName) =>
|
||||
complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!"))
|
||||
}
|
||||
.handle { case AuthorizationFailedRejection ⇒
|
||||
complete(Forbidden, "You're out of your depth!")
|
||||
}
|
||||
.handleAll[MethodRejection] { methodRejections ⇒
|
||||
val names = methodRejections.map(_.supported.name)
|
||||
complete(MethodNotAllowed, s"Can't do that! Supported: ${names mkString " or "}!")
|
||||
}
|
||||
.handleNotFound { complete(NotFound, "Not here!") }
|
||||
.result()
|
||||
|
||||
object MyApp extends App {
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
val route: Route =
|
||||
// ... some route structure
|
||||
null // hide
|
||||
|
||||
Http().bindAndHandle(route, "localhost", 8080)
|
||||
}
|
||||
//#
|
||||
}
|
||||
|
||||
class RejectionHandlerExamplesSpec extends RoutingSpec {
|
||||
import MyRejectionHandler._
|
||||
|
||||
"example-1" in {
|
||||
import akka.http.scaladsl.coding.Gzip
|
||||
|
||||
val route =
|
||||
path("order") {
|
||||
get {
|
||||
complete("Received GET")
|
||||
} ~
|
||||
post {
|
||||
decodeRequestWith(Gzip) {
|
||||
complete("Received compressed POST")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"test custom handler example" in {
|
||||
import akka.http.scaladsl.server._
|
||||
Get() ~> Route.seal(reject(MissingCookieRejection("abc"))) ~> check {
|
||||
responseAs[String] === "No cookies, no service!!!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
import akka.http.scaladsl.server.Directives
|
||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||
|
|
@ -2,16 +2,14 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
import akka.util.ByteString
|
||||
import akka.http.scaladsl.model.headers.RawHeader
|
||||
import akka.http.scaladsl.server.RouteResult.Rejected
|
||||
import akka.http.scaladsl.server._
|
||||
import akka.util.ByteString
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
class BasicDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
|
@ -164,7 +162,7 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
|
|||
rejections.nonEmpty shouldEqual true
|
||||
}
|
||||
}
|
||||
"mapRouteResponsePF" in {
|
||||
"mapRouteResultPF" in {
|
||||
case object MyCustomRejection extends Rejection
|
||||
val rejectRejections = // not particularly useful directive
|
||||
mapRouteResultPF {
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.coding._
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.server._
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model.{ HttpResponse, HttpRequest }
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model.StatusCodes
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.server.Route
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
class FormFieldDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
"formFields" in {
|
||||
val route =
|
||||
formFields('color, 'age.as[Int]) { (color, age) =>
|
||||
complete(s"The color is '$color' and the age ten years ago was ${age - 10}")
|
||||
}
|
||||
|
||||
Post("/", FormData(Seq("color" -> "blue", "age" -> "68"))) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue' and the age ten years ago was 58"
|
||||
}
|
||||
|
||||
Get("/") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.BadRequest
|
||||
responseAs[String] shouldEqual "Request is missing required form field 'color'"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{ Success, Failure }
|
||||
import akka.http.scaladsl.server.ExceptionHandler
|
||||
import akka.actor.{ Actor, Props }
|
||||
import akka.util.Timeout
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server.Route
|
||||
import StatusCodes._
|
||||
|
||||
// format: OFF
|
||||
|
||||
class FutureDirectivesExamplesSpec extends RoutingSpec {
|
||||
object TestException extends Throwable
|
||||
|
||||
implicit val myExceptionHandler =
|
||||
ExceptionHandler {
|
||||
case TestException => ctx =>
|
||||
ctx.complete(InternalServerError, "Unsuccessful future!")
|
||||
}
|
||||
|
||||
val resourceActor = system.actorOf(Props(new Actor {
|
||||
def receive = { case _ => sender ! "resource" }
|
||||
}))
|
||||
implicit val responseTimeout = Timeout(2, TimeUnit.SECONDS)
|
||||
|
||||
"example-1" in {
|
||||
def divide(a: Int, b: Int): Future[Int] = Future {
|
||||
a / b
|
||||
}
|
||||
|
||||
val route =
|
||||
path("divide" / IntNumber / IntNumber) { (a, b) =>
|
||||
onComplete(divide(a, b)) {
|
||||
case Success(value) => complete(s"The result was $value")
|
||||
case Failure(ex) => complete(InternalServerError, s"An error occurred: ${ex.getMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/divide/10/2") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The result was 5"
|
||||
}
|
||||
|
||||
Get("/divide/10/0") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual InternalServerError
|
||||
responseAs[String] shouldEqual "An error occurred: / by zero"
|
||||
}
|
||||
}
|
||||
|
||||
"example-2" in {
|
||||
val route =
|
||||
path("success") {
|
||||
onSuccess(Future { "Ok" }) { extraction =>
|
||||
complete(extraction)
|
||||
}
|
||||
} ~
|
||||
path("failure") {
|
||||
onSuccess(Future.failed[String](TestException)) { extraction =>
|
||||
complete(extraction)
|
||||
}
|
||||
}
|
||||
|
||||
Get("/success") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Ok"
|
||||
}
|
||||
|
||||
Get("/failure") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual InternalServerError
|
||||
responseAs[String] shouldEqual "Unsuccessful future!"
|
||||
}
|
||||
}
|
||||
|
||||
"example-3" in {
|
||||
val route =
|
||||
path("success") {
|
||||
onFailure(Future { "Ok" }) { extraction =>
|
||||
failWith(extraction) // not executed.
|
||||
}
|
||||
} ~
|
||||
path("failure") {
|
||||
onFailure(Future.failed[String](TestException)) { extraction =>
|
||||
failWith(extraction)
|
||||
}
|
||||
}
|
||||
|
||||
Get("/success") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Ok"
|
||||
}
|
||||
|
||||
Get("/failure") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual InternalServerError
|
||||
responseAs[String] shouldEqual "Unsuccessful future!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server.MissingHeaderRejection
|
||||
import akka.http.scaladsl.server.Route
|
||||
import headers._
|
||||
import StatusCodes._
|
||||
|
||||
class HeaderDirectivesExamplesSpec extends RoutingSpec {
|
||||
"headerValueByName-0" in {
|
||||
val route =
|
||||
headerValueByName("X-User-Id") { userId =>
|
||||
complete(s"The user is $userId")
|
||||
}
|
||||
|
||||
Get("/") ~> RawHeader("X-User-Id", "Joe42") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The user is Joe42"
|
||||
}
|
||||
|
||||
Get("/") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual BadRequest
|
||||
responseAs[String] shouldEqual "Request is missing required HTTP header 'X-User-Id'"
|
||||
}
|
||||
}
|
||||
"headerValue-0" in {
|
||||
def extractHostPort: HttpHeader => Option[Int] = {
|
||||
case h: `Host` => Some(h.port)
|
||||
case x => None
|
||||
}
|
||||
|
||||
val route =
|
||||
headerValue(extractHostPort) { port =>
|
||||
complete(s"The port was $port")
|
||||
}
|
||||
|
||||
Get("/") ~> Host("example.com", 5043) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The port was 5043"
|
||||
}
|
||||
Get("/") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual NotFound
|
||||
responseAs[String] shouldEqual "The requested resource could not be found."
|
||||
}
|
||||
}
|
||||
"headerValuePF-0" in {
|
||||
def extractHostPort: PartialFunction[HttpHeader, Int] = {
|
||||
case h: `Host` => h.port
|
||||
}
|
||||
|
||||
val route =
|
||||
headerValuePF(extractHostPort) { port =>
|
||||
complete(s"The port was $port")
|
||||
}
|
||||
|
||||
Get("/") ~> Host("example.com", 5043) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The port was 5043"
|
||||
}
|
||||
Get("/") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual NotFound
|
||||
responseAs[String] shouldEqual "The requested resource could not be found."
|
||||
}
|
||||
}
|
||||
"headerValueByType-0" in {
|
||||
val route =
|
||||
headerValueByType[Origin]() { origin ⇒
|
||||
complete(s"The first origin was ${origin.originList.head}")
|
||||
}
|
||||
|
||||
val originHeader = Origin(Seq(HttpOrigin("http://localhost:8080")))
|
||||
|
||||
// extract a header if the type is matching
|
||||
Get("abc") ~> originHeader ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The first origin was http://localhost:8080"
|
||||
}
|
||||
|
||||
// reject a request if no header of the given type is present
|
||||
Get("abc") ~> route ~> check {
|
||||
rejection must beLike { case MissingHeaderRejection("Origin") ⇒ ok }
|
||||
}
|
||||
}
|
||||
"optionalHeaderValueByType-0" in {
|
||||
val route =
|
||||
optionalHeaderValueByType[Origin]() {
|
||||
case Some(origin) ⇒ complete(s"The first origin was ${origin.originList.head}")
|
||||
case None ⇒ complete("No Origin header found.")
|
||||
}
|
||||
|
||||
val originHeader = Origin(Seq(HttpOrigin("http://localhost:8080")))
|
||||
// extract Some(header) if the type is matching
|
||||
Get("abc") ~> originHeader ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The first origin was http://localhost:8080"
|
||||
}
|
||||
|
||||
// extract None if no header of the given type is present
|
||||
Get("abc") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "No Origin header found."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
import headers._
|
||||
import StatusCodes._
|
||||
|
||||
class HostDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
"extract-hostname" in {
|
||||
val route =
|
||||
hostName { hn =>
|
||||
complete(s"Hostname: $hn")
|
||||
}
|
||||
|
||||
Get() ~> Host("company.com", 9090) ~> route ~> check {
|
||||
status shouldEqual OK
|
||||
responseAs[String] shouldEqual "Hostname: company.com"
|
||||
}
|
||||
}
|
||||
|
||||
"list-of-hosts" in {
|
||||
val route =
|
||||
host("api.company.com", "rest.company.com") {
|
||||
complete("Ok")
|
||||
}
|
||||
|
||||
Get() ~> Host("rest.company.com") ~> route ~> check {
|
||||
status shouldEqual OK
|
||||
responseAs[String] shouldEqual "Ok"
|
||||
}
|
||||
|
||||
Get() ~> Host("notallowed.company.com") ~> route ~> check {
|
||||
handled shouldBe false
|
||||
}
|
||||
}
|
||||
|
||||
"predicate" in {
|
||||
val shortOnly: String => Boolean = (hostname) => hostname.length < 10
|
||||
|
||||
val route =
|
||||
host(shortOnly) {
|
||||
complete("Ok")
|
||||
}
|
||||
|
||||
Get() ~> Host("short.com") ~> route ~> check {
|
||||
status shouldEqual OK
|
||||
responseAs[String] shouldEqual "Ok"
|
||||
}
|
||||
|
||||
Get() ~> Host("verylonghostname.com") ~> route ~> check {
|
||||
handled shouldBe false
|
||||
}
|
||||
}
|
||||
|
||||
"using-regex" in {
|
||||
val route =
|
||||
host("api|rest".r) { prefix =>
|
||||
complete(s"Extracted prefix: $prefix")
|
||||
} ~
|
||||
host("public.(my|your)company.com".r) { captured =>
|
||||
complete(s"You came through $captured company")
|
||||
}
|
||||
|
||||
Get() ~> Host("api.company.com") ~> route ~> check {
|
||||
status shouldEqual OK
|
||||
responseAs[String] shouldEqual "Extracted prefix: api"
|
||||
}
|
||||
|
||||
Get() ~> Host("public.mycompany.com") ~> route ~> check {
|
||||
status shouldEqual OK
|
||||
responseAs[String] shouldEqual "You came through my company"
|
||||
}
|
||||
}
|
||||
|
||||
"failing-regex" in {
|
||||
{
|
||||
host("server-([0-9]).company.(com|net|org)".r) { target =>
|
||||
complete("Will never complete :'(")
|
||||
}
|
||||
} must throwAn[IllegalArgumentException]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
||||
import akka.http.scaladsl.model._
|
||||
import spray.json.DefaultJsonProtocol
|
||||
import headers._
|
||||
import StatusCodes._
|
||||
|
||||
//# person-case-class
|
||||
case class Person(name: String, favoriteNumber: Int)
|
||||
|
||||
//# person-json-support
|
||||
object PersonJsonSupport extends DefaultJsonProtocol with SprayJsonSupport {
|
||||
implicit val PortofolioFormats = jsonFormat2(Person)
|
||||
}
|
||||
//#
|
||||
|
||||
class MarshallingDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
"example-entity-with-json" in {
|
||||
import PersonJsonSupport._
|
||||
|
||||
val route = post {
|
||||
entity(as[Person]) { person =>
|
||||
complete(s"Person: ${person.name} - favorite number: ${person.favoriteNumber}")
|
||||
}
|
||||
}
|
||||
|
||||
Post("/", HttpEntity(`application/json`, """{ "name": "Jane", "favoriteNumber" : 42 }""")) ~>
|
||||
route ~> check {
|
||||
responseAs[String] shouldEqual "Person: Jane - favorite number: 42"
|
||||
}
|
||||
}
|
||||
|
||||
"example-produce-with-json" in {
|
||||
import PersonJsonSupport._
|
||||
|
||||
val findPerson = (f: Person => Unit) => {
|
||||
|
||||
//... some processing logic...
|
||||
|
||||
//complete the request
|
||||
f(Person("Jane", 42))
|
||||
}
|
||||
|
||||
val route = get {
|
||||
produce(instanceOf[Person]) { completionFunction => ctx => findPerson(completionFunction) }
|
||||
}
|
||||
|
||||
Get("/") ~> route ~> check {
|
||||
mediaType shouldEqual `application/json`
|
||||
responseAs[String] must contain(""""name": "Jane"""")
|
||||
responseAs[String] must contain(""""favoriteNumber": 42""")
|
||||
}
|
||||
}
|
||||
|
||||
"example-handleWith-with-json" in {
|
||||
import PersonJsonSupport._
|
||||
|
||||
val updatePerson = (person: Person) => {
|
||||
|
||||
//... some processing logic...
|
||||
|
||||
//return the person
|
||||
person
|
||||
}
|
||||
|
||||
val route = post {
|
||||
handleWith(updatePerson)
|
||||
}
|
||||
|
||||
Post("/", HttpEntity(`application/json`, """{ "name": "Jane", "favoriteNumber" : 42 }""")) ~>
|
||||
route ~> check {
|
||||
mediaType shouldEqual `application/json`
|
||||
responseAs[String] must contain(""""name": "Jane"""")
|
||||
responseAs[String] must contain(""""favoriteNumber": 42""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
class MethodDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
"delete-method" in {
|
||||
val route = delete { complete("This is a DELETE request.") }
|
||||
|
||||
Delete("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is a DELETE request."
|
||||
}
|
||||
}
|
||||
|
||||
"get-method" in {
|
||||
val route = get { complete("This is a GET request.") }
|
||||
|
||||
Get("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is a GET request."
|
||||
}
|
||||
}
|
||||
|
||||
"head-method" in {
|
||||
val route = head { complete("This is a HEAD request.") }
|
||||
|
||||
Head("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is a HEAD request."
|
||||
}
|
||||
}
|
||||
|
||||
"options-method" in {
|
||||
val route = options { complete("This is an OPTIONS request.") }
|
||||
|
||||
Options("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is an OPTIONS request."
|
||||
}
|
||||
}
|
||||
|
||||
"patch-method" in {
|
||||
val route = patch { complete("This is a PATCH request.") }
|
||||
|
||||
Patch("/", "patch content") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is a PATCH request."
|
||||
}
|
||||
}
|
||||
|
||||
"post-method" in {
|
||||
val route = post { complete("This is a POST request.") }
|
||||
|
||||
Post("/", "post content") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is a POST request."
|
||||
}
|
||||
}
|
||||
|
||||
"put-method" in {
|
||||
val route = put { complete("This is a PUT request.") }
|
||||
|
||||
Put("/", "put content") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is a PUT request."
|
||||
}
|
||||
}
|
||||
|
||||
"method-example" in {
|
||||
val route = method(HttpMethods.PUT) { complete("This is a PUT request.") }
|
||||
|
||||
Put("/", "put content") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "This is a PUT request."
|
||||
}
|
||||
|
||||
Get("/") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.MethodNotAllowed
|
||||
responseAs[String] shouldEqual "HTTP method not allowed, supported methods: PUT"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server._
|
||||
import headers._
|
||||
|
||||
class MiscDirectivesExamplesSpec extends RoutingSpec {
|
||||
"cancelAllRejections-example" in {
|
||||
def isMethodRejection: Rejection => Boolean = {
|
||||
case MethodRejection(_) => true
|
||||
case _ => false
|
||||
}
|
||||
|
||||
val route =
|
||||
cancelAllRejections(isMethodRejection) {
|
||||
post {
|
||||
complete("Result")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/") ~> route ~> check {
|
||||
rejections shouldEqual Nil
|
||||
handled shouldEqual false
|
||||
}
|
||||
}
|
||||
"cancelRejection-example" in {
|
||||
val route =
|
||||
cancelRejection(MethodRejection(HttpMethods.POST)) {
|
||||
post {
|
||||
complete("Result")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/") ~> route ~> check {
|
||||
rejections shouldEqual Nil
|
||||
handled shouldEqual false
|
||||
}
|
||||
}
|
||||
"clientIP-example" in {
|
||||
val route = clientIP { ip =>
|
||||
complete("Client's ip is " + ip.toOption.map(_.getHostAddress).getOrElse("unknown"))
|
||||
}
|
||||
|
||||
Get("/").withHeaders(`Remote-Address`("192.168.3.12")) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Client's ip is 192.168.3.12"
|
||||
}
|
||||
}
|
||||
"jsonpWithParameter-example" in {
|
||||
case class Test(abc: Int)
|
||||
object TestProtocol {
|
||||
import spray.json.DefaultJsonProtocol._
|
||||
implicit val testFormat = jsonFormat(Test, "abc")
|
||||
}
|
||||
val route =
|
||||
jsonpWithParameter("jsonp") {
|
||||
import TestProtocol._
|
||||
import spray.httpx.SprayJsonSupport._
|
||||
complete(Test(456))
|
||||
}
|
||||
|
||||
Get("/?jsonp=result") ~> route ~> check {
|
||||
responseAs[String] shouldEqual
|
||||
"""result({
|
||||
| "abc": 456
|
||||
|})""".stripMarginWithNewline("\n")
|
||||
contentType shouldEqual MediaTypes.`application/javascript`.withCharset(HttpCharsets.`UTF-8`)
|
||||
}
|
||||
Get("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual
|
||||
"""{
|
||||
| "abc": 456
|
||||
|}""".stripMarginWithNewline("\n")
|
||||
contentType shouldEqual ContentTypes.`application/json`
|
||||
}
|
||||
}
|
||||
"rejectEmptyResponse-example" in {
|
||||
val route = rejectEmptyResponse {
|
||||
path("even" / IntNumber) { i =>
|
||||
complete {
|
||||
// returns Some(evenNumberDescription) or None
|
||||
Option(i).filter(_ % 2 == 0).map { num =>
|
||||
s"Number $num is even."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Get("/even/23") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.NotFound
|
||||
}
|
||||
Get("/even/28") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Number 28 is even."
|
||||
}
|
||||
}
|
||||
"requestEntityEmptyPresent-example" in {
|
||||
val route =
|
||||
requestEntityEmpty {
|
||||
complete("request entity empty")
|
||||
} ~
|
||||
requestEntityPresent {
|
||||
complete("request entity present")
|
||||
}
|
||||
|
||||
Post("/", "text") ~> Route.seal(route) ~> check {
|
||||
responseAs[String] shouldEqual "request entity present"
|
||||
}
|
||||
Post("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "request entity empty"
|
||||
}
|
||||
}
|
||||
"requestInstance-example" in {
|
||||
val route =
|
||||
requestInstance { request =>
|
||||
complete(s"Request method is ${request.method} and length is ${request.entity.data.length}")
|
||||
}
|
||||
|
||||
Post("/", "text") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Request method is POST and length is 4"
|
||||
}
|
||||
Get("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Request method is GET and length is 0"
|
||||
}
|
||||
}
|
||||
"requestUri-example" in {
|
||||
val route =
|
||||
requestUri { uri =>
|
||||
complete(s"Full URI: $uri")
|
||||
}
|
||||
|
||||
Get("/") ~> route ~> check {
|
||||
// tests are executed with the host assumed to be "example.com"
|
||||
responseAs[String] shouldEqual "Full URI: http://example.com/"
|
||||
}
|
||||
Get("/test") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Full URI: http://example.com/test"
|
||||
}
|
||||
}
|
||||
"rewriteUnmatchedPath-example" in {
|
||||
def ignore456(path: Uri.Path) = path match {
|
||||
case s @ Uri.Path.Segment(head, tail) if head.startsWith("456") =>
|
||||
val newHead = head.drop(3)
|
||||
if (newHead.isEmpty) tail
|
||||
else s.copy(head = head.drop(3))
|
||||
case _ => path
|
||||
}
|
||||
val ignoring456 = rewriteUnmatchedPath(ignore456)
|
||||
|
||||
val route =
|
||||
pathPrefix("123") {
|
||||
ignoring456 {
|
||||
path("abc") {
|
||||
complete(s"Content")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Get("/123/abc") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Content"
|
||||
}
|
||||
Get("/123456/abc") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Content"
|
||||
}
|
||||
}
|
||||
"unmatchedPath-example" in {
|
||||
val route =
|
||||
pathPrefix("abc") {
|
||||
unmatchedPath { remaining =>
|
||||
complete(s"Unmatched: '$remaining'")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/abc") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Unmatched: ''"
|
||||
}
|
||||
Get("/abc/456") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Unmatched: '/456'"
|
||||
}
|
||||
}
|
||||
"validate-example" in {
|
||||
val route =
|
||||
requestUri { uri =>
|
||||
validate(uri.path.toString.size < 5, s"Path too long: '${uri.path.toString}'") {
|
||||
complete(s"Full URI: $uri")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/234") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Full URI: http://example.com/234"
|
||||
}
|
||||
Get("/abcdefghijkl") ~> route ~> check {
|
||||
rejection shouldEqual ValidationRejection("Path too long: '/abcdefghijkl'", None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import spray.http.StatusCodes
|
||||
|
||||
class ParameterDirectivesExamplesSpec extends RoutingSpec {
|
||||
"example-1" in {
|
||||
val route =
|
||||
parameter('color) { color =>
|
||||
complete(s"The color is '$color'")
|
||||
}
|
||||
|
||||
Get("/?color=blue") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue'"
|
||||
}
|
||||
|
||||
Get("/") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.NotFound
|
||||
responseAs[String] shouldEqual "Request is missing required query parameter 'color'"
|
||||
}
|
||||
}
|
||||
"required-1" in {
|
||||
val route =
|
||||
parameters('color, 'backgroundColor) { (color, backgroundColor) =>
|
||||
complete(s"The color is '$color' and the background is '$backgroundColor'")
|
||||
}
|
||||
|
||||
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
|
||||
}
|
||||
Get("/?color=blue") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.NotFound
|
||||
responseAs[String] shouldEqual "Request is missing required query parameter 'backgroundColor'"
|
||||
}
|
||||
}
|
||||
"optional" in {
|
||||
val route =
|
||||
parameters('color, 'backgroundColor.?) { (color, backgroundColor) =>
|
||||
val backgroundStr = backgroundColor.getOrElse("<undefined>")
|
||||
complete(s"The color is '$color' and the background is '$backgroundStr'")
|
||||
}
|
||||
|
||||
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
|
||||
}
|
||||
Get("/?color=blue") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue' and the background is '<undefined>'"
|
||||
}
|
||||
}
|
||||
"optional-with-default" in {
|
||||
val route =
|
||||
parameters('color, 'backgroundColor ? "white") { (color, backgroundColor) =>
|
||||
complete(s"The color is '$color' and the background is '$backgroundColor'")
|
||||
}
|
||||
|
||||
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
|
||||
}
|
||||
Get("/?color=blue") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue' and the background is 'white'"
|
||||
}
|
||||
}
|
||||
"required-value" in {
|
||||
val route =
|
||||
parameters('color, 'action ! "true") { (color) =>
|
||||
complete(s"The color is '$color'.")
|
||||
}
|
||||
|
||||
Get("/?color=blue&action=true") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue'."
|
||||
}
|
||||
|
||||
Get("/?color=blue&action=false") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.NotFound
|
||||
responseAs[String] shouldEqual "The requested resource could not be found."
|
||||
}
|
||||
}
|
||||
"mapped-value" in {
|
||||
val route =
|
||||
parameters('color, 'count.as[Int]) { (color, count) =>
|
||||
complete(s"The color is '$color' and you have $count of it.")
|
||||
}
|
||||
|
||||
Get("/?color=blue&count=42") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The color is 'blue' and you have 42 of it."
|
||||
}
|
||||
|
||||
Get("/?color=blue&count=blub") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.BadRequest
|
||||
responseAs[String] shouldEqual "The query parameter 'count' was malformed:\n'blub' is not a valid 32-bit integer value"
|
||||
}
|
||||
}
|
||||
"parameterMap" in {
|
||||
val route =
|
||||
parameterMap { params =>
|
||||
def paramString(param: (String, String)): String = s"""${param._1} = '${param._2}'"""
|
||||
complete(s"The parameters are ${params.map(paramString).mkString(", ")}")
|
||||
}
|
||||
|
||||
Get("/?color=blue&count=42") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The parameters are color = 'blue', count = '42'"
|
||||
}
|
||||
Get("/?x=1&x=2") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The parameters are x = '2'"
|
||||
}
|
||||
}
|
||||
"parameterMultiMap" in {
|
||||
val route =
|
||||
parameterMultiMap { params =>
|
||||
complete(s"There are parameters ${params.map(x => x._1 + " -> " + x._2.size).mkString(", ")}")
|
||||
}
|
||||
|
||||
Get("/?color=blue&count=42") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "There are parameters color -> 1, count -> 1"
|
||||
}
|
||||
Get("/?x=23&x=42") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "There are parameters x -> 2"
|
||||
}
|
||||
}
|
||||
"parameterSeq" in {
|
||||
val route =
|
||||
parameterSeq { params =>
|
||||
def paramString(param: (String, String)): String = s"""${param._1} = '${param._2}'"""
|
||||
complete(s"The parameters are ${params.map(paramString).mkString(", ")}")
|
||||
}
|
||||
|
||||
Get("/?color=blue&count=42") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The parameters are color = 'blue', count = '42'"
|
||||
}
|
||||
Get("/?x=1&x=2") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The parameters are x = '1', x = '2'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.server._
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
import headers._
|
||||
|
||||
class RangeDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
"withRangeSupport" in {
|
||||
val route =
|
||||
withRangeSupport(4, 2L) {
|
||||
complete("ABCDEFGH")
|
||||
}
|
||||
|
||||
Get() ~> addHeader(Range(ByteRange(3, 4))) ~> route ~> check {
|
||||
headers must contain(`Content-Range`(ContentRange(3, 4, 8)))
|
||||
status shouldEqual StatusCodes.PartialContent
|
||||
responseAs[String] shouldEqual "DE"
|
||||
}
|
||||
|
||||
Get() ~> addHeader(Range(ByteRange(0, 1), ByteRange(1, 2), ByteRange(6, 7))) ~> route ~> check {
|
||||
headers must not(contain(like[HttpHeader] { case `Content-Range`(_, _) ⇒ ok }))
|
||||
responseAs[MultipartByteRanges] must beLike {
|
||||
case MultipartByteRanges(
|
||||
BodyPart(entity1, `Content-Range`(RangeUnit.Bytes, range1) +: _) +:
|
||||
BodyPart(entity2, `Content-Range`(RangeUnit.Bytes, range2) +: _) +: Seq()
|
||||
) ⇒ entity1.asString shouldEqual "ABC" and range1 shouldEqual ContentRange(0, 2, 8) and
|
||||
entity2.asString shouldEqual "GH" and range2 shouldEqual ContentRange(6, 7, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.server.UnacceptedResponseContentTypeRejection
|
||||
import akka.http.scaladsl.model._
|
||||
import headers._
|
||||
|
||||
class RespondWithDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
"respondWithHeader-examples" in {
|
||||
val route =
|
||||
path("foo") {
|
||||
respondWithHeader(RawHeader("Funky-Muppet", "gonzo")) {
|
||||
complete("beep")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/foo") ~> route ~> check {
|
||||
header("Funky-Muppet") shouldEqual Some(RawHeader("Funky-Muppet", "gonzo"))
|
||||
responseAs[String] shouldEqual "beep"
|
||||
}
|
||||
}
|
||||
|
||||
"respondWithHeaders-examples" in {
|
||||
val route =
|
||||
path("foo") {
|
||||
respondWithHeaders(RawHeader("Funky-Muppet", "gonzo"), Origin(Seq(HttpOrigin("http://spray.io")))) {
|
||||
complete("beep")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/foo") ~> route ~> check {
|
||||
header("Funky-Muppet") shouldEqual Some(RawHeader("Funky-Muppet", "gonzo"))
|
||||
header[Origin] shouldEqual Some(Origin(Seq(HttpOrigin("http://spray.io"))))
|
||||
responseAs[String] shouldEqual "beep"
|
||||
}
|
||||
}
|
||||
|
||||
"respondWithMediaType-examples" in {
|
||||
import MediaTypes._
|
||||
|
||||
val route =
|
||||
path("foo") {
|
||||
respondWithMediaType(`application/json`) {
|
||||
complete("[]") // marshalled to `text/plain` here
|
||||
}
|
||||
}
|
||||
|
||||
Get("/foo") ~> route ~> check {
|
||||
mediaType shouldEqual `application/json`
|
||||
responseAs[String] shouldEqual "[]"
|
||||
}
|
||||
|
||||
Get("/foo") ~> Accept(MediaRanges.`text/*`) ~> route ~> check {
|
||||
rejection shouldEqual UnacceptedResponseContentTypeRejection(ContentType(`application/json`) :: Nil)
|
||||
}
|
||||
}
|
||||
|
||||
"respondWithSingletonHeader-examples" in {
|
||||
val respondWithMuppetHeader =
|
||||
respondWithSingletonHeader(RawHeader("Funky-Muppet", "gonzo"))
|
||||
|
||||
val route =
|
||||
path("foo") {
|
||||
respondWithMuppetHeader {
|
||||
complete("beep")
|
||||
}
|
||||
} ~
|
||||
path("bar") {
|
||||
respondWithMuppetHeader {
|
||||
respondWithHeader(RawHeader("Funky-Muppet", "kermit")) {
|
||||
complete("beep")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Get("/foo") ~> route ~> check {
|
||||
headers.filter(_.is("funky-muppet")) shouldEqual List(RawHeader("Funky-Muppet", "gonzo"))
|
||||
responseAs[String] shouldEqual "beep"
|
||||
}
|
||||
|
||||
Get("/bar") ~> route ~> check {
|
||||
headers.filter(_.is("funky-muppet")) shouldEqual List(RawHeader("Funky-Muppet", "kermit"))
|
||||
responseAs[String] shouldEqual "beep"
|
||||
}
|
||||
}
|
||||
|
||||
"respondWithStatus-examples" in {
|
||||
val route =
|
||||
path("foo") {
|
||||
respondWithStatus(201) {
|
||||
complete("beep")
|
||||
}
|
||||
}
|
||||
|
||||
Get("/foo") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.Created
|
||||
responseAs[String] shouldEqual "beep"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import spray.http.{ RequestProcessingException, HttpResponse, StatusCodes }
|
||||
import spray.routing.ValidationRejection
|
||||
|
||||
class RouteDirectivesExamplesSpec extends RoutingSpec {
|
||||
|
||||
"complete-examples" in {
|
||||
val route =
|
||||
path("a") {
|
||||
complete(HttpResponse(entity = "foo"))
|
||||
} ~
|
||||
path("b") {
|
||||
complete(StatusCodes.Created, "bar")
|
||||
} ~
|
||||
(path("c") & complete("baz")) // `&` also works with `complete` as the 2nd argument
|
||||
|
||||
Get("/a") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
responseAs[String] shouldEqual "foo"
|
||||
}
|
||||
|
||||
Get("/b") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.Created
|
||||
responseAs[String] shouldEqual "bar"
|
||||
}
|
||||
|
||||
Get("/c") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
responseAs[String] shouldEqual "baz"
|
||||
}
|
||||
}
|
||||
|
||||
"reject-examples" in {
|
||||
val route =
|
||||
path("a") {
|
||||
reject // don't handle here, continue on
|
||||
} ~
|
||||
path("a") {
|
||||
complete("foo")
|
||||
} ~
|
||||
path("b") {
|
||||
// trigger a ValidationRejection explicitly
|
||||
// rather than through the `validate` directive
|
||||
reject(ValidationRejection("Restricted!"))
|
||||
}
|
||||
|
||||
Get("/a") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "foo"
|
||||
}
|
||||
|
||||
Get("/b") ~> route ~> check {
|
||||
rejection shouldEqual ValidationRejection("Restricted!")
|
||||
}
|
||||
}
|
||||
|
||||
"redirect-examples" in {
|
||||
val route =
|
||||
pathPrefix("foo") {
|
||||
pathSingleSlash {
|
||||
complete("yes")
|
||||
} ~
|
||||
pathEnd {
|
||||
redirect("/foo/", StatusCodes.PermanentRedirect)
|
||||
}
|
||||
}
|
||||
|
||||
Get("/foo/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "yes"
|
||||
}
|
||||
|
||||
Get("/foo") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.PermanentRedirect
|
||||
responseAs[String] shouldEqual """The request, and all future requests should be repeated using <a href="/foo/">this URI</a>."""
|
||||
}
|
||||
}
|
||||
|
||||
"failwith-examples" in {
|
||||
val route =
|
||||
path("foo") {
|
||||
failWith(new RequestProcessingException(StatusCodes.BandwidthLimitExceeded))
|
||||
}
|
||||
|
||||
Get("/foo") ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.BandwidthLimitExceeded
|
||||
responseAs[String] shouldEqual "Bandwidth limit has been exceeded."
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
class SchemeDirectivesExamplesSpec extends RoutingSpec {
|
||||
"example-1" in {
|
||||
val route =
|
||||
schemeName { scheme =>
|
||||
complete(s"The scheme is '${scheme}'")
|
||||
}
|
||||
|
||||
Get("https://www.example.com/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "The scheme is 'https'"
|
||||
}
|
||||
}
|
||||
|
||||
"example-2" in {
|
||||
val route =
|
||||
scheme("http") {
|
||||
extract(_.request.uri) { uri ⇒
|
||||
redirect(uri.copy(scheme = "https"), MovedPermanently)
|
||||
}
|
||||
} ~
|
||||
scheme("https") {
|
||||
complete(s"Safe and secure!")
|
||||
}
|
||||
|
||||
Get("http://www.example.com/hello") ~> route ~> check {
|
||||
status shouldEqual MovedPermanently
|
||||
header[headers.Location] shouldEqual Some(headers.Location(Uri("https://www.example.com/hello")))
|
||||
}
|
||||
|
||||
Get("https://www.example.com/hello") ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Safe and secure!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import scala.concurrent.Future
|
||||
import akka.http.scaladsl.model._
|
||||
import headers._
|
||||
|
||||
class SecurityDirectivesExamplesSpec extends RoutingSpec {
|
||||
"authenticate-custom-user-pass-authenticator" in {
|
||||
def myUserPassAuthenticator(userPass: Option[UserPass]): Future[Option[String]] =
|
||||
Future {
|
||||
if (userPass.exists(up => up.user == "John" && up.pass == "p4ssw0rd")) Some("John")
|
||||
else None
|
||||
}
|
||||
|
||||
val route =
|
||||
Route.seal {
|
||||
path("secured") {
|
||||
authenticateBasic(BasicAuth(myUserPassAuthenticator _, realm = "secure site")) { userName =>
|
||||
complete(s"The user is '$userName'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Get("/secured") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request"
|
||||
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
|
||||
}
|
||||
|
||||
val validCredentials = BasicHttpCredentials("John", "p4ssw0rd")
|
||||
Get("/secured") ~>
|
||||
addCredentials(validCredentials) ~> // adds Authorization header
|
||||
route ~> check {
|
||||
responseAs[String] shouldEqual "The user is 'John'"
|
||||
}
|
||||
|
||||
val invalidCredentials = BasicHttpCredentials("Peter", "pan")
|
||||
Get("/secured") ~>
|
||||
addCredentials(invalidCredentials) ~> // adds Authorization header
|
||||
route ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The supplied authentication is invalid"
|
||||
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
|
||||
}
|
||||
}
|
||||
|
||||
"authenticate-from-config" in {
|
||||
def extractUser(userPass: UserPass): String = userPass.user
|
||||
val config = ConfigFactory.parseString("John = p4ssw0rd")
|
||||
|
||||
val route =
|
||||
Route.seal {
|
||||
path("secured") {
|
||||
authenticateBasic(BasicAuth(realm = "secure site", config = config, createUser = extractUser _)) { userName =>
|
||||
complete(s"The user is '$userName'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Get("/secured") ~> route ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request"
|
||||
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
|
||||
}
|
||||
|
||||
val validCredentials = BasicHttpCredentials("John", "p4ssw0rd")
|
||||
Get("/secured") ~>
|
||||
addCredentials(validCredentials) ~> // adds Authorization header
|
||||
route ~> check {
|
||||
responseAs[String] shouldEqual "The user is 'John'"
|
||||
}
|
||||
|
||||
val invalidCredentials = BasicHttpCredentials("Peter", "pan")
|
||||
Get("/secured") ~>
|
||||
addCredentials(invalidCredentials) ~> // adds Authorization header
|
||||
route ~> check {
|
||||
status shouldEqual StatusCodes.Unauthorized
|
||||
responseAs[String] shouldEqual "The supplied authentication is invalid"
|
||||
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
|
||||
}
|
||||
}
|
||||
|
||||
"authorize-1" in {
|
||||
def extractUser(userPass: UserPass): String = userPass.user
|
||||
val config = ConfigFactory.parseString("John = p4ssw0rd\nPeter = pan")
|
||||
def hasPermissionToPetersLair(userName: String) = userName == "Peter"
|
||||
|
||||
val route =
|
||||
Route.seal {
|
||||
authenticateBasic(BasicAuth(realm = "secure site", config = config, createUser = extractUser _)) { userName =>
|
||||
path("peters-lair") {
|
||||
authorize(hasPermissionToPetersLair(userName)) {
|
||||
complete(s"'$userName' visited Peter's lair")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val johnsCred = BasicHttpCredentials("John", "p4ssw0rd")
|
||||
Get("/peters-lair") ~>
|
||||
addCredentials(johnsCred) ~> // adds Authorization header
|
||||
route ~> check {
|
||||
status shouldEqual StatusCodes.Forbidden
|
||||
responseAs[String] shouldEqual "The supplied authentication is not authorized to access this resource"
|
||||
}
|
||||
|
||||
val petersCred = BasicHttpCredentials("Peter", "pan")
|
||||
Get("/peters-lair") ~>
|
||||
addCredentials(petersCred) ~> // adds Authorization header
|
||||
route ~> check {
|
||||
responseAs[String] shouldEqual "'Peter' visited Peter's lair"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.server.Route
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
|
||||
object MyHandler {
|
||||
//# example-1
|
||||
import akka.http.scaladsl.model.HttpResponse
|
||||
import akka.http.scaladsl.model.StatusCodes._
|
||||
import akka.http.scaladsl.server._
|
||||
import Directives._
|
||||
|
||||
implicit def myExceptionHandler =
|
||||
ExceptionHandler {
|
||||
case e: ArithmeticException =>
|
||||
extractUri { uri =>
|
||||
logWarning(s"Request to $uri could not be handled normally")
|
||||
complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!"))
|
||||
}
|
||||
}
|
||||
|
||||
object MyApp {
|
||||
implicit val system = ActorSystem()
|
||||
import system.dispatcher
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
def handler = Route.handlerFlow(`<my-route-definition>`)
|
||||
}
|
||||
//#
|
||||
|
||||
def `<my-route-definition>`: Route = null
|
||||
def logWarning(str: String): Unit = {}
|
||||
}
|
||||
|
||||
class ExceptionHandlerExamplesSpec extends RoutingSpec {
|
||||
import MyHandler._
|
||||
|
||||
"example" in {
|
||||
Get() ~> Route.seal(ctx => ctx.complete((1 / 0).toString)) ~> check {
|
||||
responseAs[String] === "Bad numbers, bad result!!!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.server
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorFlowMaterializer
|
||||
|
||||
import akka.http.scaladsl.server.{ Route, MissingCookieRejection }
|
||||
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
object MyRejectionHandler {
|
||||
//# example-1
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server._
|
||||
import StatusCodes._
|
||||
import Directives._
|
||||
|
||||
implicit val myRejectionHandler = RejectionHandler.newBuilder()
|
||||
.handle {
|
||||
case MissingCookieRejection(cookieName) =>
|
||||
complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!"))
|
||||
}
|
||||
.result()
|
||||
|
||||
object MyApp {
|
||||
implicit val system = ActorSystem()
|
||||
import system.dispatcher
|
||||
implicit val materializer = ActorFlowMaterializer()
|
||||
|
||||
def handler = Route.handlerFlow(`<my-route-definition>`)
|
||||
}
|
||||
//#
|
||||
|
||||
def `<my-route-definition>`: Route = null
|
||||
}
|
||||
|
||||
class RejectionHandlerExamplesSpec extends RoutingSpec {
|
||||
import MyRejectionHandler._
|
||||
|
||||
"example" in {
|
||||
Get() ~> Route.seal(reject(MissingCookieRejection("abc"))) ~> check {
|
||||
responseAs[String] === "No cookies, no service!!!"
|
||||
}
|
||||
}
|
||||
|
||||
"example-2" in {
|
||||
import akka.http.scaladsl.coding.Gzip
|
||||
|
||||
val route =
|
||||
path("order") {
|
||||
get {
|
||||
complete("Received GET")
|
||||
} ~
|
||||
post {
|
||||
decodeRequestWith(Gzip) {
|
||||
complete("Received POST")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
.. _ConnectionLevelApi:
|
||||
|
||||
Connection-Level Client-Side API
|
||||
================================
|
||||
|
||||
The connection-level API is the lowest-level client-side API Akka HTTP provides. It gives you full control over when
|
||||
HTTP connections are opened and closed and how requests are to be send across which connection. As such it offers the
|
||||
highest flexibility at the cost of providing the least convenience.
|
||||
|
||||
|
||||
Opening HTTP Connections
|
||||
------------------------
|
||||
|
||||
With the connection-level API you open a new HTTP connection by materializing a ``Flow`` returned by the
|
||||
``Http.outgoingConnection(...)`` method. The target endpoint to which a connection is to be opened needs to be
|
||||
specified as an argument to ``Http.outgoingConnection(...)``. Here is an example:
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala
|
||||
:include: outgoing-connection-example
|
||||
|
||||
Apart from the host name and port the ``Http.outgoingConnection(...)`` method also allows you to specify socket options
|
||||
and a number of configuration settings for the connection.
|
||||
|
||||
Note that no connection is attempted until the returned flow is actually materialized! If the flow is materialized
|
||||
several times then several independent connections will be opened (one per materialization).
|
||||
If the connection attempt fails, for whatever reason, the materialized flow will be immediately terminated with a
|
||||
respective exception.
|
||||
|
||||
|
||||
Request-Response Cycle
|
||||
----------------------
|
||||
|
||||
Once the connection flow has been materialized it is ready to consume ``HttpRequest`` instances from the source it is
|
||||
attached to. Each request is sent across the connection and incoming responses dispatched to the downstream pipeline.
|
||||
Of course and as always, back-pressure is adequately maintained across all parts of the
|
||||
connection. This means that, if the downstream pipeline consuming the HTTP responses is slow, the request source will
|
||||
eventually be slowed down in sending requests.
|
||||
|
||||
Any errors occurring on the underlying connection are surfaced as exceptions terminating the response stream (and
|
||||
canceling the request source).
|
||||
|
||||
Note that, if the source produces subsequent requests before the prior responses have arrived, these requests will be
|
||||
pipelined__ across the connection, which is something that is not supported by all HTTP servers.
|
||||
Also, if the server closes the connection before responses to all requests have been received this will result in the
|
||||
response stream being terminated with a truncation error.
|
||||
|
||||
__ http://en.wikipedia.org/wiki/HTTP_pipelining
|
||||
|
||||
|
||||
Closing Connections
|
||||
-------------------
|
||||
|
||||
Akka HTTP actively closes an established connection upon reception of a response containing ``Connection: close`` header.
|
||||
Of course the connection can also be closed by the server.
|
||||
|
||||
An application can actively trigger the closing of the connection by completing the request stream. In this case the
|
||||
underlying TCP connection will be closed when the last pending response has been received.
|
||||
|
||||
|
||||
Timeouts
|
||||
--------
|
||||
|
||||
Currently Akka HTTP doesn't implement client-side request timeout checking itself as this functionality can be regarded
|
||||
as a more general purpose streaming infrastructure feature.
|
||||
However, akka-stream should soon provide such a feature.
|
||||
|
||||
147
akka-docs-dev/rst/scala/http/client-side/host-level.rst
Normal file
147
akka-docs-dev/rst/scala/http/client-side/host-level.rst
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
.. _HostLevelApi:
|
||||
|
||||
Host-Level Client-Side API
|
||||
==========================
|
||||
|
||||
As opposed to the :ref:`ConnectionLevelApi` the host-level API relieves you from manually managing individual HTTP
|
||||
connections. It autonomously manages a configurable pool of connections to *one particular target endpoint* (i.e.
|
||||
host/port combination).
|
||||
|
||||
|
||||
Requesting a Host Connection Pool
|
||||
---------------------------------
|
||||
|
||||
The best way to get a hold of a connection pool to a given target endpoint is the ``Http.cachedHostConnectionPool(...)``
|
||||
method, which returns a ``Flow`` that can be "baked" into an application-level stream setup. This flow is also called
|
||||
a "pool client flow".
|
||||
|
||||
The connection pool underlying a pool client flow is cached. For every ``ActorSystem``, target endpoint and pool
|
||||
configuration there will never be more than a single pool live at any time.
|
||||
|
||||
Also, the HTTP layer transparently manages idle shutdown and restarting of connection pools as configured.
|
||||
The client flow instances therefore remain valid throughout the lifetime of the application, i.e. they can be
|
||||
materialized as often as required and the time between individual materialization is of no importance.
|
||||
|
||||
When you request a pool client flow with ``Http.cachedHostConnectionPool(...)`` Akka HTTP will immediately start
|
||||
the pool, even before the first client flow materialization. However, this running pool will not actually open the
|
||||
first connection to the target endpoint until the first request has arrived.
|
||||
|
||||
|
||||
Configuring a Host Connection Pool
|
||||
----------------------------------
|
||||
|
||||
Apart from the connection-level config settings and socket options there are a number of settings that allow you to
|
||||
influence the behavior of the connection pool logic itself.
|
||||
Check out the ``akka.http.client.host-connection-pool`` section of the Akka HTTP :ref:`akka-http-configuration` for
|
||||
more information about which settings are available and what they mean.
|
||||
|
||||
Note that, if you request pools with different configurations for the same target host you will get *independent* pools.
|
||||
This means that, in total, your application might open more concurrent HTTP connections to the target endpoint than any
|
||||
of the individual pool's ``max-connections`` settings allow!
|
||||
|
||||
There is one setting that likely deserves a bit deeper explanation: ``max-open-requests``.
|
||||
This setting limits the maximum number of requests that can be open at any time for a single connection pool.
|
||||
If an application calls ``Http.cachedHostConnectionPool(...)`` 3 times (with the same endpoint and settings) it will get
|
||||
back 3 different client flow instances for the same pool. If each of these client flows is then materialized 4 times
|
||||
(concurrently) the application will have 12 concurrently running client flow materializations.
|
||||
All of these share the resources of the single pool.
|
||||
|
||||
This means that, if the pool's ``pipelining-limit`` is left at ``1``, no more than 12 requests can be open at any time.
|
||||
With a ``pipelining-limit`` of ``8`` and 12 concurrent client flow materializations the theoretical open requests
|
||||
maximum is 96.
|
||||
|
||||
The ``max-open-requests`` config setting allows for applying a hard limit which serves mainly as a protection against
|
||||
erroneous connection pool use, e.g. because the application is materializing too many client flows that all compete for
|
||||
the same pooled connections.
|
||||
|
||||
.. _using-a-host-connection-pool:
|
||||
|
||||
Using a Host Connection Pool
|
||||
----------------------------
|
||||
|
||||
The "pool client flow" returned by ``Http.cachedHostConnectionPool(...)`` has the following type::
|
||||
|
||||
Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool]
|
||||
|
||||
This means it consumes tuples of type ``(HttpRequest, T)`` and produces tuples of type ``(Try[HttpResponse], T)``
|
||||
which might appear more complicated than necessary on first sight.
|
||||
The reason why the pool API includes objects of custom type ``T`` on both ends lies in the fact that the underlying
|
||||
transport usually comprises more than a single connection and as such the pool client flow often generates responses in
|
||||
an order that doesn't directly match the consumed requests.
|
||||
We could have built the pool logic in a way that reorders responses according to their requests before dispatching them
|
||||
to the application, but this would have meant that a single slow response could block the delivery of potentially many
|
||||
responses that would otherwise be ready for consumption by the application.
|
||||
|
||||
In order to prevent unnecessary head-of-line blocking the pool client-flow is allowed to dispatch responses as soon as
|
||||
they arrive, independently of the request order. Of course this means that there needs to be another way to associate a
|
||||
response with its respective request. The way that this is done is by allowing the application to pass along a custom
|
||||
"context" object with the request, which is then passed back to the application with the respective response.
|
||||
This context object of type ``T`` is completely opaque to Akka HTTP, i.e. you can pick whatever works best for your
|
||||
particular application scenario.
|
||||
|
||||
|
||||
Connection Allocation Logic
|
||||
---------------------------
|
||||
|
||||
This is how Akka HTTP allocates incoming requests to the available connection "slots":
|
||||
|
||||
1. If there is a connection alive and currently idle then schedule the request across this connection.
|
||||
2. If no connection is idle and there is still an unconnected slot then establish a new connection.
|
||||
3. If all connections are already established and "loaded" with other requests then pick the connection with the least
|
||||
open requests (< the configured ``pipelining-limit``) that only has requests with idempotent methods scheduled to it,
|
||||
if there is one.
|
||||
4. Otherwise apply back-pressure to the request source, i.e. stop accepting new requests.
|
||||
|
||||
For more information about scheduling more than one request at a time across a single connection see
|
||||
`this wikipedia entry on HTTP pipelining`__.
|
||||
|
||||
__ http://en.wikipedia.org/wiki/HTTP_pipelining
|
||||
|
||||
|
||||
|
||||
Retrying a Request
|
||||
------------------
|
||||
|
||||
If the ``max-retries`` pool config setting is greater than zero the pool retries idempotent requests for which
|
||||
a response could not be successfully retrieved. Idempotent requests are those whose HTTP method is defined to be
|
||||
idempotent by the HTTP spec, which are all the ones currently modelled by Akka HTTP except for the ``POST``, ``PATCH``
|
||||
and ``CONNECT`` methods.
|
||||
|
||||
When a response could not be received for a certain request there are essentially three possible error scenarios:
|
||||
|
||||
1. The request got lost on the way to the server.
|
||||
2. The server experiences a problem while processing the request.
|
||||
3. The response from the server got lost on the way back.
|
||||
|
||||
Since the host connector cannot know which one of these possible reasons caused the problem and therefore ``PATCH`` and
|
||||
``POST`` requests could have already triggered a non-idempotent action on the server these requests cannot be retried.
|
||||
|
||||
In these cases, as well as when all retries have not yielded a proper response, the pool produces a failed ``Try``
|
||||
(i.e. a ``scala.util.Failure``) together with the custom request context.
|
||||
|
||||
|
||||
Pool Shutdown
|
||||
-------------
|
||||
|
||||
Completing a pool client flow will simply detach the flow from the pool. The connection pool itself will continue to run
|
||||
as it may be serving other client flows concurrently or in the future. Only after the configured ``idle-timeout`` for
|
||||
the pool has expired will Akka HTTP automatically terminate the pool and free all its resources.
|
||||
|
||||
If a new client flow is requested with ``Http.cachedHostConnectionPool(...)`` or if an already existing client flow is
|
||||
re-materialized the respective pool is automatically and transparently restarted.
|
||||
|
||||
In addition to the automatic shutdown via the configured idle timeouts it's also possible to trigger the immediate
|
||||
shutdown of specific pool by calling ``shutdown()`` on the ``Http.HostConnectionPool`` instance that a pool client
|
||||
flow materializes into. This ``shutdown()`` call produces a ``Future[Unit]`` which is fulfilled when the pool
|
||||
termination has been completed.
|
||||
|
||||
It's also possible to trigger the immediate termination of *all* connection pools in the ``ActorSystem`` at the same
|
||||
time by calling ``Http.shutdownAllConnectionPools()``. This call too produces a ``Future[Unit]`` which is fulfilled when
|
||||
all pools have terminated.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala
|
||||
:include: host-level-example
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
Client-Side HTTPS Support
|
||||
=========================
|
||||
|
||||
TODO
|
||||
31
akka-docs-dev/rst/scala/http/client-side/index.rst
Normal file
31
akka-docs-dev/rst/scala/http/client-side/index.rst
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
.. _http-client-side:
|
||||
|
||||
Consuming HTTP-based Services (Client-Side)
|
||||
===========================================
|
||||
|
||||
All client-side functionality of Akka HTTP, for consuming HTTP-based services offered by other systems, is currently
|
||||
provided by the ``akka-http-core`` module.
|
||||
|
||||
Depending your application's specific needs you can choose from three different API levels:
|
||||
|
||||
:ref:`ConnectionLevelApi`
|
||||
for full-control over when HTTP connections are opened/closed and how requests are scheduled across them
|
||||
|
||||
:ref:`HostLevelApi`
|
||||
for letting Akka HTTP manage a connection-pool to *one specific* host/port endpoint
|
||||
|
||||
:ref:`RequestLevelApi`
|
||||
for letting Akka HTTP perform all connection management
|
||||
|
||||
You can interact with different API levels at the same time and, independently of which API level you choose,
|
||||
Akka HTTP will happily handle many thousand concurrent connections to a single or many different hosts.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
connection-level
|
||||
host-level
|
||||
request-level
|
||||
https-support
|
||||
websocket-support
|
||||
48
akka-docs-dev/rst/scala/http/client-side/request-level.rst
Normal file
48
akka-docs-dev/rst/scala/http/client-side/request-level.rst
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
.. _RequestLevelApi:
|
||||
|
||||
Request-Level Client-Side API
|
||||
=============================
|
||||
|
||||
The request-level API is the most convenient way of using Akka HTTP's client-side. It internally builds upon the
|
||||
:ref:`HostLevelApi` to provide you with a simple and easy-to-use way of retrieving HTTP responses from remote servers.
|
||||
Depending on your preference you can pick the flow-based or the future-based variant.
|
||||
|
||||
|
||||
Flow-Based Variant
|
||||
------------------
|
||||
|
||||
The flow-based variant of the request-level client-side API is presented by the ``Http.superPool(...)`` method.
|
||||
It creates a new "super connection pool flow", which routes incoming requests to a (cached) host connection pool
|
||||
depending on their respective effective URI.
|
||||
|
||||
The ``Flow`` returned by ``Http.superPool(...)`` is very similar to the one from the :ref:`HostLevelApi`, so the
|
||||
:ref:`using-a-host-connection-pool` section also applies here.
|
||||
|
||||
However, there is one notable difference between a "host connection pool client flow" for the host-level API and a
|
||||
"super-pool flow":
|
||||
Since in the former case the flow has an implicit target host context the requests it takes don't need to have absolute
|
||||
URIs or a valid ``Host`` header. The host connection pool will automatically add a ``Host`` header if required.
|
||||
|
||||
For a super-pool flow this is not the case. All requests to a super-pool must either have an absolute URI or a valid
|
||||
``Host`` header, because otherwise it'd be impossible to find out which target endpoint to direct the request to.
|
||||
|
||||
|
||||
Future-Based Variant
|
||||
--------------------
|
||||
|
||||
Sometimes your HTTP client needs are very basic. You simply need the HTTP response for a certain request and don't
|
||||
want to bother with setting up a full-blown streaming infrastructure.
|
||||
|
||||
For these cases Akka HTTP offers the ``Http.singleRequest(...)`` method, which simply turns an ``HttpRequest`` instance
|
||||
into ``Future[HttpResponse]``. Internally the request is dispatched across the (cached) host connection pool for the
|
||||
request's effective URI.
|
||||
|
||||
Just like in the case of the super-pool flow described above the request must have either an absolute URI or a valid
|
||||
``Host`` header, otherwise the returned future will be completed with an error.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala
|
||||
:include: single-request-example
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
Client-Side WebSocket Support
|
||||
=============================
|
||||
|
||||
TODO
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Client API
|
||||
==========
|
||||
|
||||
(todo)
|
||||
4
akka-docs-dev/rst/scala/http/common/de-coding.rst
Normal file
4
akka-docs-dev/rst/scala/http/common/de-coding.rst
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Encoding / Decoding
|
||||
===================
|
||||
|
||||
...
|
||||
230
akka-docs-dev/rst/scala/http/common/http-model.rst
Normal file
230
akka-docs-dev/rst/scala/http/common/http-model.rst
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
.. _http-model-scala:
|
||||
|
||||
HTTP Model
|
||||
==========
|
||||
|
||||
Akka HTTP model contains a deeply structured, fully immutable, case-class based model of all the major HTTP data
|
||||
structures, like HTTP requests, responses and common headers.
|
||||
It lives in the *akka-http-core* module and forms the basis for most of Akka HTTP's APIs.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Since akka-http-core provides the central HTTP data structures you will find the following import in quite a
|
||||
few places around the code base (and probably your own code as well):
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
|
||||
:include: import-model
|
||||
|
||||
This brings all of the most relevant types in scope, mainly:
|
||||
|
||||
- ``HttpRequest`` and ``HttpResponse``, the central message model
|
||||
- ``headers``, the package containing all the predefined HTTP header models and supporting types
|
||||
- Supporting types like ``Uri``, ``HttpMethods``, ``MediaTypes``, ``StatusCodes``, etc.
|
||||
|
||||
A common pattern is that the model of a certain entity is represented by an immutable type (class or trait),
|
||||
while the actual instances of the entity defined by the HTTP spec live in an accompanying object carrying the name of
|
||||
the type plus a trailing plural 's'.
|
||||
|
||||
For example:
|
||||
|
||||
- Defined ``HttpMethod`` instances live in the ``HttpMethods`` object.
|
||||
- Defined ``HttpCharset`` instances live in the ``HttpCharsets`` object.
|
||||
- Defined ``HttpEncoding`` instances live in the ``HttpEncodings`` object.
|
||||
- Defined ``HttpProtocol`` instances live in the ``HttpProtocols`` object.
|
||||
- Defined ``MediaType`` instances live in the ``MediaTypes`` object.
|
||||
- Defined ``StatusCode`` instances live in the ``StatusCodes`` object.
|
||||
|
||||
HttpRequest
|
||||
-----------
|
||||
|
||||
``HttpRequest`` and ``HttpResponse`` are the basic case classes representing HTTP messages.
|
||||
|
||||
An ``HttpRequest`` consists of
|
||||
|
||||
- a method (GET, POST, etc.)
|
||||
- a URI
|
||||
- a seq of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
|
||||
Here are some examples how to construct an ``HttpRequest``:
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
|
||||
:include: construct-request
|
||||
|
||||
All parameters of ``HttpRequest.apply`` have default values set, so ``headers`` for example don't need to be specified
|
||||
if there are none. Many of the parameters types (like ``HttpEntity`` and ``Uri``) define implicit conversions
|
||||
for common use cases to simplify the creation of request and response instances.
|
||||
|
||||
HttpResponse
|
||||
------------
|
||||
|
||||
An ``HttpResponse`` consists of
|
||||
|
||||
- a status code
|
||||
- a seq of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
|
||||
Here are some examples how to construct an ``HttpResponse``:
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
|
||||
:include: construct-response
|
||||
|
||||
In addition to the simple ``HttpEntity`` constructors create an entity from a fixed ``String`` or ``ByteString`` shown
|
||||
as here the Akka HTTP model defines a number of subclasses of ``HttpEntity`` which allow body data to be specified as a
|
||||
stream of bytes.
|
||||
|
||||
|
||||
.. _HttpEntity:
|
||||
|
||||
HttpEntity
|
||||
----------
|
||||
|
||||
An ``HttpEntity`` carries the data bytes of a message together with its Content-Type and, if known, its Content-Length.
|
||||
In Akka HTTP there are five different kinds of entities which model the various ways that message content can be
|
||||
received or sent:
|
||||
|
||||
HttpEntity.Strict
|
||||
The simplest entity, which is used when all the entity are already available in memory.
|
||||
It wraps a plain ``ByteString`` and represents a standard, unchunked entity with a known ``Content-Length``.
|
||||
|
||||
|
||||
HttpEntity.Default
|
||||
The general, unchunked HTTP/1.1 message entity.
|
||||
It has a known length and presents its data as a ``Source[ByteString]`` which can only materialized once.
|
||||
It is an error if the provided source doesn't produce exactly as many bytes as specified.
|
||||
On the wire, a ``Strict`` entity and a ``Default`` entity cannot be distinguished.
|
||||
|
||||
|
||||
HttpEntity.Chunked
|
||||
The model for HTTP/1.1 `chunked content`__ (i.e. sent with ``Transfer-Encoding: chunked``).
|
||||
The content length is unknown and the individual chunks are presented as a ``Source[HttpEntity.ChunkStreamPart]``.
|
||||
A ``ChunkStreamPart`` is either a non-empty ``Chunk`` or a ``LastChunk`` containing optional trailer headers.
|
||||
The stream consists of zero or more ``Chunked`` parts and can be terminated by an optional ``LastChunk`` part.
|
||||
|
||||
|
||||
HttpEntity.CloseDelimited
|
||||
An unchunked entity of unknown length that is implicitly delimited by closing the connection (``Connection: close``).
|
||||
The content data are presented as a ``Source[ByteString]``.
|
||||
Since the connection must be closed after sending an entity of this type it can only be used on the server-side for
|
||||
sending a response.
|
||||
Also, the main purpose of ``CloseDelimited`` entities is compatibility with HTTP/1.0 peers, which do not support
|
||||
chunked transfer encoding. If you are building a new application and are not constrained by legacy requirements you
|
||||
shouldn't rely on ``CloseDelimited`` entities, since implicit terminate-by-connection-close is not a robust way of
|
||||
signaling response end, especially in the presence of proxies. Additionally this type of entity prevents connection
|
||||
reuse which can seriously degrade performance. Use ``HttpEntity.Chunked`` instead!
|
||||
|
||||
|
||||
HttpEntity.IndefiniteLength
|
||||
A streaming entity of unspecified length for use in a ``Multipart.BodyPart``.
|
||||
|
||||
__ http://tools.ietf.org/html/rfc7230#section-4.1
|
||||
|
||||
Entity types ``Strict``, ``Default``, and ``Chunked`` are a subtype of ``HttpEntity.Regular`` which allows to use them
|
||||
for requests and responses. In contrast, ``HttpEntity.CloseDelimited`` can only be used for responses.
|
||||
|
||||
Streaming entity types (i.e. all but ``Strict``) cannot be shared or serialized. To create a strict, sharable copy of an
|
||||
entity or message use ``HttpEntity.toStrict`` or ``HttpMessage.toStrict`` which returns a ``Future`` of the object with
|
||||
the body data collected into a ``ByteString``.
|
||||
|
||||
The ``HttpEntity`` companion object contains several helper constructors to create entities from common types easily.
|
||||
|
||||
You can pattern match over the subtypes of ``HttpEntity`` if you want to provide special handling for each of the
|
||||
subtypes. However, in many cases a recipient of an ``HttpEntity`` doesn't care about of which subtype an entity is
|
||||
(and how data is transported exactly on the HTTP layer). Therefore, a general
|
||||
``HttpEntity::dataBytes: Source[ByteString, Any]`` is provided which allows access to the data of an entity regardless
|
||||
of its concrete subtype.
|
||||
|
||||
.. note::
|
||||
|
||||
When to use which subtype?
|
||||
- Use ``Strict`` if the amount of data is "small" and already available in memory (e.g. as a ``String`` or ``ByteString``)
|
||||
- Use ``Default`` if the data is generated by a streaming data source and the size of the data is known
|
||||
- Use ``Chunked`` for an entity of unknown length
|
||||
- Use ``CloseDelimited`` for a response as a legacy alternative to ``Chunked`` if the client doesn't support
|
||||
chunked transfer encoding. Otherwise use ``Chunked``!
|
||||
- In a ``Multipart.Bodypart`` use ``IndefiniteLength`` for content of unknown length.
|
||||
|
||||
.. caution::
|
||||
|
||||
When you receive a non-strict message from a connection then additional data are only read from the network when you
|
||||
request them by consuming the entity data stream. This means that, if you *don't* consume the entity stream then the
|
||||
connection will effectively be stalled. In particular no subsequent message (request or response) will be read from
|
||||
the connection as the entity of the current message "blocks" the stream.
|
||||
Therefore you must make sure that you always consume the entity data, even in the case that you are not actually
|
||||
interested in it!
|
||||
|
||||
|
||||
Header Model
|
||||
------------
|
||||
|
||||
Akka HTTP contains a rich model of the most common HTTP headers. Parsing and rendering is done automatically so that
|
||||
applications don't need to care for the actual syntax of headers. Headers not modelled explicitly are represented
|
||||
as a ``RawHeader`` (which is essentially a String/String name/value pair).
|
||||
|
||||
See these examples of how to deal with headers:
|
||||
|
||||
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
|
||||
:include: headers
|
||||
|
||||
|
||||
HTTP Headers
|
||||
------------
|
||||
|
||||
When the Akka HTTP server receives an HTTP request it tries to parse all its headers into their respective
|
||||
model classes. Independently of whether this succeeds or not, the HTTP layer will
|
||||
always pass on all received headers to the application. Unknown headers as well as ones with invalid syntax (according
|
||||
to the header parser) will be made available as ``RawHeader`` instances. For the ones exhibiting parsing errors a
|
||||
warning message is logged depending on the value of the ``illegal-header-warnings`` config setting.
|
||||
|
||||
Some headers have special status in HTTP and are therefore treated differently from "regular" headers:
|
||||
|
||||
Content-Type
|
||||
The Content-Type of an HTTP message is modeled as the ``contentType`` field of the ``HttpEntity``.
|
||||
The ``Content-Type`` header therefore doesn't appear in the ``headers`` sequence of a message.
|
||||
Also, a ``Content-Type`` header instance that is explicitly added to the ``headers`` of a request or response will
|
||||
not be rendered onto the wire and trigger a warning being logged instead!
|
||||
|
||||
Transfer-Encoding
|
||||
Messages with ``Transfer-Encoding: chunked`` are represented via the ``HttpEntity.Chunked`` entity.
|
||||
As such chunked messages that do not have another deeper nested transfer encoding will not have a ``Transfer-Encoding``
|
||||
header in their ``headers`` sequence.
|
||||
Similarly, a ``Transfer-Encoding`` header instance that is explicitly added to the ``headers`` of a request or
|
||||
response will not be rendered onto the wire and trigger a warning being logged instead!
|
||||
|
||||
Content-Length
|
||||
The content length of a message is modelled via its :ref:`HttpEntity`. As such no ``Content-Length`` header will ever
|
||||
be part of a message's ``header`` sequence.
|
||||
Similarly, a ``Content-Length`` header instance that is explicitly added to the ``headers`` of a request or
|
||||
response will not be rendered onto the wire and trigger a warning being logged instead!
|
||||
|
||||
Server
|
||||
A ``Server`` header is usually added automatically to any response and its value can be configured via the
|
||||
``akka.http.server.server-header`` setting. Additionally an application can override the configured header with a
|
||||
custom one by adding it to the response's ``header`` sequence.
|
||||
|
||||
User-Agent
|
||||
A ``User-Agent`` header is usually added automatically to any request and its value can be configured via the
|
||||
``akka.http.client.user-agent-header`` setting. Additionally an application can override the configured header with a
|
||||
custom one by adding it to the request's ``header`` sequence.
|
||||
|
||||
Date
|
||||
The ``Date`` response header is added automatically but can be overridden by supplying it manually.
|
||||
|
||||
Connection
|
||||
On the server-side Akka HTTP watches for explicitly added ``Connection: close`` response headers and as such honors
|
||||
the potential wish of the application to close the connection after the respective response has been sent out.
|
||||
The actual logic for determining whether to close the connection is quite involved. It takes into account the
|
||||
request's method, protocol and potential ``Connection`` header as well as the response's protocol, entity and
|
||||
potential ``Connection`` header. See `this test`__ for a full table of what happens when.
|
||||
|
||||
__ @github@/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/ResponseRendererSpec.scala#L422
|
||||
|
||||
|
||||
Parsing / Rendering
|
||||
-------------------
|
||||
|
||||
Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API
|
||||
provided to parse (or render to) Strings or byte arrays.
|
||||
21
akka-docs-dev/rst/scala/http/common/index.rst
Normal file
21
akka-docs-dev/rst/scala/http/common/index.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
.. _http-scala-common:
|
||||
|
||||
Common Abstractions (Client- and Server-Side)
|
||||
=============================================
|
||||
|
||||
HTTP and related specifications define a great number of concepts and functionality that is not specific to either
|
||||
HTTP's client- or server-side since they are meaningful on both end of an HTTP connection.
|
||||
The documentation for their counterparts in Akka HTTP lives in this section rather than in the ones for the
|
||||
:ref:`Client-Side API <http-client-side>`, :ref:`http-low-level-server-side-api` or :ref:`http-high-level-server-side-api`,
|
||||
which are specific to one side only.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
http-model
|
||||
marshalling
|
||||
unmarshalling
|
||||
de-coding
|
||||
json-support
|
||||
xml-support
|
||||
4
akka-docs-dev/rst/scala/http/common/json-support.rst
Normal file
4
akka-docs-dev/rst/scala/http/common/json-support.rst
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
JSON Support
|
||||
============
|
||||
|
||||
...
|
||||
6
akka-docs-dev/rst/scala/http/common/marshalling.rst
Normal file
6
akka-docs-dev/rst/scala/http/common/marshalling.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. _http-marshalling-scala:
|
||||
|
||||
Marshalling
|
||||
===========
|
||||
|
||||
...
|
||||
6
akka-docs-dev/rst/scala/http/common/unmarshalling.rst
Normal file
6
akka-docs-dev/rst/scala/http/common/unmarshalling.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. _http-unmarshalling-scala:
|
||||
|
||||
Unmarshalling
|
||||
=============
|
||||
|
||||
...
|
||||
4
akka-docs-dev/rst/scala/http/common/xml-support.rst
Normal file
4
akka-docs-dev/rst/scala/http/common/xml-support.rst
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
XML Support
|
||||
===========
|
||||
|
||||
...
|
||||
28
akka-docs-dev/rst/scala/http/configuration.rst
Normal file
28
akka-docs-dev/rst/scala/http/configuration.rst
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
.. _akka-http-configuration:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Just like any other Akka module Akka HTTP is configured via `Typesafe Config`_.
|
||||
Usually this means that you provide an ``application.conf`` which contains all the application-specific settings that
|
||||
differ from the default ones provided by the reference configuration files from the individual Akka modules.
|
||||
|
||||
These are the relevant default configuration values for the Akka HTTP modules.
|
||||
|
||||
akka-http-core
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../../../akka-http-core/src/main/resources/reference.conf
|
||||
:language: none
|
||||
|
||||
|
||||
akka-http-core-scala
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../../../akka-http-scala/src/main/resources/reference.conf
|
||||
:language: none
|
||||
|
||||
|
||||
The other Akka HTTP modules do not offer any configuration via `Typesafe Config`_.
|
||||
|
||||
.. _Typesafe Config: https://github.com/typesafehub/config
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
.. _Directives:
|
||||
|
||||
Directives
|
||||
==========
|
||||
|
||||
A "Directive" is a small building block to construct arbitrarily complex route structures.
|
||||
Here is a simple example of a route built from directives:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-1
|
||||
|
||||
The general anatomy of a directive is as follows::
|
||||
|
||||
name(arguments) { extractions =>
|
||||
... // inner Route
|
||||
}
|
||||
|
||||
It has a name, zero or more arguments and optionally an inner Route. Additionally directives can "extract" a number of
|
||||
values and make them available to their inner routes as function arguments. When seen "from the outside" a directive
|
||||
with its inner Route form an expression of type ``Route``.
|
||||
|
||||
What Directives do
|
||||
------------------
|
||||
|
||||
A directive can do one or more of the following:
|
||||
|
||||
.. rst-class:: wide
|
||||
|
||||
* Transform the incoming ``RequestContext`` before passing it on to its inner Route
|
||||
* Filter the ``RequestContext`` according to some logic, i.e. only pass on certain requests and reject all others
|
||||
* Extract values from the ``RequestContext`` and make them available to its inner Route as "extractions"
|
||||
* Complete the request
|
||||
|
||||
The first point deserves some more discussion. The ``RequestContext`` is the central object that is passed on through a
|
||||
route structure (also see :ref:`RequestContext`). When a directive (or better the Route it built) receives a ``RequestContext``
|
||||
it can decide to pass this instance on unchanged to its inner Route or it can create a copy of the ``RequestContext`` instance,
|
||||
with one or more changes, and pass on this copy to its inner Route. Typically this is good for two things:
|
||||
|
||||
* Transforming the ``HttpRequest`` instance
|
||||
* "Hooking in" a response transformation function that changes the ``RouteResponse`` (and therefore the response).
|
||||
|
||||
This means a ``Directive`` completely wraps the functionality of its inner routes and can apply arbitrarily complex
|
||||
transformations, both (or either) on the request and on the response side.
|
||||
|
||||
Composing Directives
|
||||
--------------------
|
||||
|
||||
As you have seen from the examples presented so far the "normal" way of composing directives is nesting. Let's take
|
||||
another look at the example from above:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-2
|
||||
|
||||
Here the ``get`` and ``put`` directives are chained together with the ``~`` operator to form a higher-level route that
|
||||
serves as the inner Route of the ``path`` directive. To make this structure more explicit you could also write the whole
|
||||
thing like this:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-3
|
||||
|
||||
What you can't see from this snippet is that directives are not implemented as simple methods but rather as stand-alone
|
||||
objects of type ``Directive``. This gives you more flexibility when composing directives. For example you can
|
||||
also use the ``|`` operator on directives. Here is yet another way to write the example:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-4
|
||||
|
||||
If you have a larger route structure where the ``(get | put)`` snippet appears several times you could also factor it
|
||||
out like this:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-5
|
||||
|
||||
As an alternative to nesting you can also use the `&` operator:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-6
|
||||
|
||||
And once again, you can factor things out if you want:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-7
|
||||
|
||||
This type of combining directives with the ``|`` and ``&`` operators as well as "saving" more complex directive
|
||||
configurations as a ``val`` works across the board, with all directives taking inner routes.
|
||||
|
||||
There is one more "ugly" thing remaining in our snippet: we have to fall back to the lowest-level route definition,
|
||||
directly manipulating the ``RequestContext``, in order to get to the request method. It'd be nicer if we could somehow
|
||||
"extract" the method name in a special directive, so that we can express our inner-most route with a simple
|
||||
``complete``. As it turns out this is easy with the ``extract`` directive:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-8
|
||||
|
||||
Or differently:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-9
|
||||
|
||||
Now, pushing the "factoring out" of directive configurations to its extreme, we end up with this:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
|
||||
:snippet: example-A
|
||||
|
||||
Note that going this far with "compressing" several directives into a single one probably doesn't result in the most
|
||||
readable and therefore maintainable routing code. It might even be that the very first of this series of examples
|
||||
is in fact the most readable one.
|
||||
|
||||
Still, the purpose of the exercise presented here is to show you how flexible directives can be and how you can
|
||||
use their power to define your web service behavior at the level of abstraction that is right for **your** application.
|
||||
|
||||
|
||||
Type Safety of Directives
|
||||
-------------------------
|
||||
|
||||
When you combine directives with the ``|`` and ``&`` operators the routing DSL makes sure that all extractions work as
|
||||
expected and logical constraints are enforced at compile-time.
|
||||
|
||||
For example you cannot ``|`` a directive producing an extraction with one that doesn't::
|
||||
|
||||
val route = path("order" / IntNumber) | get // doesn't compile
|
||||
|
||||
Also the number of extractions and their types have to match up::
|
||||
|
||||
val route = path("order" / IntNumber) | path("order" / DoubleNumber) // doesn't compile
|
||||
val route = path("order" / IntNumber) | parameter('order.as[Int]) // ok
|
||||
|
||||
When you combine directives producing extractions with the ``&`` operator all extractions will be properly gathered up::
|
||||
|
||||
val order = path("order" / IntNumber) & parameters('oem, 'expired ?)
|
||||
val route =
|
||||
order { (orderId, oem, expired) =>
|
||||
...
|
||||
}
|
||||
|
||||
Directives offer a great way of constructing your web service logic from small building blocks in a plug and play
|
||||
fashion while maintaining DRYness and full type-safety. If the large range of :ref:`Predefined Directives` does not
|
||||
fully satisfy your needs you can also very easily create :ref:`Custom Directives`.
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
.. _Predefined Directives:
|
||||
|
||||
Predefined Directives (alphabetically)
|
||||
======================================
|
||||
|
||||
.. rst-class:: table table-striped
|
||||
|
||||
====================================== =================================================================================
|
||||
Directive Description
|
||||
====================================== =================================================================================
|
||||
:ref:`-cancelAllRejections-` Adds a ``TransformationRejection`` to rejections from its inner Route, which
|
||||
cancels other rejections according to a predicate function
|
||||
:ref:`-cancelRejection-` Adds a ``TransformationRejection`` cancelling all rejections equal to a given one
|
||||
:ref:`-clientIP-` Extracts the IP address of the client from either the ``X-Forwarded-For``,
|
||||
``Remote-Address`` or ``X-Real-IP`` request header
|
||||
:ref:`-complete-` Completes the request with a given response, several overloads
|
||||
:ref:`-compressResponse-` Compresses responses coming back from its inner Route using either ``Gzip`` or
|
||||
``Deflate`` unless the request explicitly sets ``Accept-Encoding`` to ``identity``.
|
||||
:ref:`-compressResponseIfRequested-` Compresses responses coming back from its inner Route using either ``Gzip`` or
|
||||
``Deflate``, but only when the request explicitly accepts one of them.
|
||||
:ref:`-conditional-` Depending on the given ETag and Last-Modified values responds with
|
||||
``304 Not Modified`` if the request comes with the respective conditional headers.
|
||||
:ref:`-cookie-` Extracts an ``HttpCookie`` with a given name or rejects if no such cookie is
|
||||
present in the request
|
||||
:ref:`-decodeRequest-` Decompresses incoming requests using a given Decoder
|
||||
:ref:`-decompressRequest-` Decompresses incoming requests using either ``Gzip``, ``Deflate``, or ``NoEncoding``
|
||||
:ref:`-delete-` Rejects all non-DELETE requests
|
||||
:ref:`-deleteCookie-` Adds a ``Set-Cookie`` header expiring the given cookie to all ``HttpResponse``
|
||||
replies of its inner Route
|
||||
:ref:`-encodeResponse-` Compresses responses coming back from its inner Route using a given Encoder
|
||||
:ref:`-entity-` Unmarshalls the requests entity according to a given definition, rejects in
|
||||
case of problems
|
||||
:ref:`-extract-` Extracts a single value from the ``RequestContext`` using a function
|
||||
``RequestContext => T``
|
||||
:ref:`-extractRequest-` Extracts the complete request
|
||||
:ref:`-failWith-` Bubbles the given error up the response chain, where it is dealt with by the
|
||||
closest :ref:`-handleExceptions-` directive and its ExceptionHandler
|
||||
:ref:`-formField-` Extracts the value of an HTTP form field, rejects if the request doesn't come
|
||||
with a field matching the definition
|
||||
:ref:`-formFields-` Same as :ref:`-formField-`, except for several fields at once
|
||||
:ref:`-get-` Rejects all non-GET requests
|
||||
:ref:`-getFromBrowseableDirectories-` Same as :ref:`-getFromBrowseableDirectory-`, but allows for serving the "union"
|
||||
of several directories as one single "virtual" one
|
||||
:ref:`-getFromBrowseableDirectory-` Completes GET requests with the content of a file underneath a given directory,
|
||||
renders directory contents as browsable listings
|
||||
:ref:`-getFromDirectory-` Completes GET requests with the content of a file underneath a given directory
|
||||
:ref:`-getFromFile-` Completes GET requests with the content of a given file
|
||||
:ref:`-getFromResource-` Completes GET requests with the content of a given resource
|
||||
:ref:`-getFromResourceDirectory-` Same as :ref:`-getFromDirectory-` except that the file is not fetched from the
|
||||
file system but rather from a "resource directory"
|
||||
:ref:`-handleExceptions-` Converts exceptions thrown during evaluation of its inner Route into
|
||||
``HttpResponse`` replies using a given ExceptionHandler
|
||||
:ref:`-handleRejections-` Converts rejections produced by its inner Route into ``HttpResponse`` replies
|
||||
using a given RejectionHandler
|
||||
:ref:`-handleWith-` Completes the request using a given function. Uses the in-scope ``Unmarshaller``
|
||||
and ``Marshaller`` for converting to and from the function
|
||||
:ref:`-head-` Rejects all non-HEAD requests
|
||||
:ref:`-headerValue-` Extracts an HTTP header value using a given function, rejects if no value can
|
||||
be extracted
|
||||
:ref:`-headerValueByName-` Extracts an HTTP header value by selecting a header by name
|
||||
:ref:`-headerValueByType-` Extracts an HTTP header value by selecting a header by type
|
||||
:ref:`-headerValuePF-` Same as :ref:`-headerValue-`, but with a ``PartialFunction``
|
||||
:ref:`-host-` Rejects all requests with a hostname different from a given definition,
|
||||
can extract the hostname using a regex pattern
|
||||
:ref:`-hostName-` Extracts the hostname part of the requests ``Host`` header value
|
||||
:ref:`-listDirectoryContents-` Completes GET requests with a unified listing of the contents of one or more
|
||||
given directories
|
||||
:ref:`-logRequest-` Produces a log entry for every incoming request
|
||||
:ref:`-logRequestResult-` Produces a log entry for every response or rejection coming back from its inner
|
||||
route, allowing for coalescing with the corresponding request
|
||||
:ref:`-logResult-` Produces a log entry for every response or rejection coming back from its inner
|
||||
route
|
||||
:ref:`-mapResponse-` Transforms the ``HttpResponse`` coming back from its inner Route
|
||||
:ref:`-mapResponseEntity-` Transforms the entity of the ``HttpResponse`` coming back from its inner Route
|
||||
:ref:`-mapResponseHeaders-` Transforms the headers of the ``HttpResponse`` coming back from its inner Route
|
||||
:ref:`-mapInnerRoute-` Transforms its inner Route with a ``Route => Route`` function
|
||||
:ref:`-mapRejections-` Transforms all rejections coming back from its inner Route
|
||||
:ref:`-mapRequest-` Transforms the incoming ``HttpRequest``
|
||||
:ref:`-mapRequestContext-` Transforms the ``RequestContext``
|
||||
:ref:`-mapRouteResult-` Transforms all responses coming back from its inner Route with a ``Any => Any``
|
||||
function
|
||||
:ref:`-mapRouteResultPF-` Same as :ref:`-mapRouteResult-`, but with a ``PartialFunction``
|
||||
:ref:`-method-` Rejects if the request method does not match a given one
|
||||
:ref:`-overrideMethodWithParameter-` Changes the HTTP method of the request to the value of the specified query string
|
||||
parameter
|
||||
:ref:`-onComplete-` "Unwraps" a ``Future[T]`` and runs its inner route after future completion with
|
||||
the future's value as an extraction of type ``Try[T]``
|
||||
:ref:`-onFailure-` "Unwraps" a ``Future[T]`` and runs its inner route when the future has failed
|
||||
with the future's failure exception as an extraction of type ``Throwable``
|
||||
:ref:`-onSuccess-` "Unwraps" a ``Future[T]`` and runs its inner route after future completion with
|
||||
the future's value as an extraction of type ``T``
|
||||
:ref:`-optionalCookie-` Extracts an ``HttpCookie`` with a given name, if the cookie is not present in the
|
||||
request extracts ``None``
|
||||
:ref:`-optionalHeaderValue-` Extracts an optional HTTP header value using a given function
|
||||
:ref:`-optionalHeaderValueByName-` Extracts an optional HTTP header value by selecting a header by name
|
||||
:ref:`-optionalHeaderValueByType-` Extracts an optional HTTP header value by selecting a header by type
|
||||
:ref:`-optionalHeaderValuePF-` Extracts an optional HTTP header value using a given partial function
|
||||
:ref:`-options-` Rejects all non-OPTIONS requests
|
||||
:ref:`-parameter-` Extracts the value of a request query parameter, rejects if the request doesn't
|
||||
come with a parameter matching the definition
|
||||
:ref:`-parameterMap-` Extracts the requests query parameters as a ``Map[String, String]``
|
||||
:ref:`-parameterMultiMap-` Extracts the requests query parameters as a ``Map[String, List[String]]``
|
||||
:ref:`-parameters-` Same as :ref:`-parameter-`, except for several parameters at once
|
||||
:ref:`-parameterSeq-` Extracts the requests query parameters as a ``Seq[(String, String)]``
|
||||
:ref:`-pass-` Does nothing, i.e. passes the ``RequestContext`` unchanged to its inner Route
|
||||
:ref:`-patch-` Rejects all non-PATCH requests
|
||||
:ref:`-path-` Extracts zero+ values from the ``unmatchedPath`` of the ``RequestContext``
|
||||
according to a given ``PathMatcher``, rejects if no match
|
||||
:ref:`-pathEnd-` Only passes on the request to its inner route if the request path has been
|
||||
matched completely, rejects otherwise
|
||||
:ref:`-pathEndOrSingleSlash-` Only passes on the request to its inner route if the request path has been matched
|
||||
completely or only consists of exactly one remaining slash, rejects otherwise
|
||||
:ref:`-pathPrefix-` Same as :ref:`-path-`, but also matches (and consumes) prefixes of the unmatched
|
||||
path (rather than only the complete unmatched path at once)
|
||||
:ref:`-pathPrefixTest-` Like :ref:`-pathPrefix-` but without "consumption" of the matched path (prefix).
|
||||
:ref:`-pathSingleSlash-` Only passes on the request to its inner route if the request path consists of
|
||||
exactly one remaining slash
|
||||
:ref:`-pathSuffix-` Like as :ref:`-pathPrefix-`, but for suffixes rather than prefixed of the
|
||||
unmatched path
|
||||
:ref:`-pathSuffixTest-` Like :ref:`-pathSuffix-` but without "consumption" of the matched path (suffix).
|
||||
:ref:`-post-` Rejects all non-POST requests
|
||||
:ref:`-produce-` Uses the in-scope marshaller to extract a function that can be used for
|
||||
completing the request with an instance of a custom type
|
||||
:ref:`-provide-` Injects a single value into a directive, which provides it as an extraction
|
||||
:ref:`-put-` Rejects all non-PUT requests
|
||||
:ref:`-rawPathPrefix-` Applies a given ``PathMatcher`` directly to the unmatched path of the
|
||||
``RequestContext``, i.e. without implicitly consuming a leading slash
|
||||
:ref:`-rawPathPrefixTest-` Checks whether the unmatchedPath of the ``RequestContext`` has a prefix matched
|
||||
by a ``PathMatcher``
|
||||
:ref:`-redirect-` Completes the request with redirection response of the given type to a given URI
|
||||
:ref:`-reject-` Rejects the request with a given set of rejections
|
||||
:ref:`-rejectEmptyResponse-` Converts responses with an empty entity into a rejection
|
||||
:ref:`-requestEncodedWith-` Rejects the request if its encoding doesn't match a given one
|
||||
:ref:`-requestEntityEmpty-` Rejects the request if its entity is not empty
|
||||
:ref:`-requestEntityPresent-` Rejects the request if its entity is empty
|
||||
:ref:`-requestUri-` Extracts the complete request URI
|
||||
:ref:`-respondWithHeader-` Adds a given response header to all ``HttpResponse`` replies from its inner
|
||||
Route
|
||||
:ref:`-respondWithHeaders-` Same as :ref:`-respondWithHeader-`, but for several headers at once
|
||||
:ref:`-respondWithMediaType-` Overrides the media-type of all ``HttpResponse`` replies from its inner Route,
|
||||
rejects if the media-type is not accepted by the client
|
||||
:ref:`-respondWithSingletonHeader-` Adds a given response header to all ``HttpResponse`` replies from its inner
|
||||
Route, if a header with the same name is not yet present
|
||||
:ref:`-respondWithSingletonHeaders-` Same as :ref:`-respondWithSingletonHeader-`, but for several headers at once
|
||||
:ref:`-respondWithStatus-` Overrides the response status of all ``HttpResponse`` replies coming back from
|
||||
its inner Route
|
||||
:ref:`-responseEncodingAccepted-` Rejects the request if the client doesn't accept a given encoding for the
|
||||
response
|
||||
:ref:`-mapUnmatchedPath-` Transforms the ``unmatchedPath`` of the ``RequestContext`` using a given function
|
||||
:ref:`-scheme-` Rejects a request if its Uri scheme does not match a given one
|
||||
:ref:`-schemeName-` Extracts the request Uri scheme
|
||||
:ref:`-setCookie-` Adds a ``Set-Cookie`` header to all ``HttpResponse`` replies of its inner Route
|
||||
:ref:`-textract-` Extracts a ``TupleN`` of values from the ``RequestContext`` using a function
|
||||
:ref:`-hprovide-` Injects a ``TupleN`` of values into a directive, which provides them as
|
||||
extractions
|
||||
:ref:`-unmatchedPath-` Extracts the unmatched path from the RequestContext
|
||||
:ref:`-validate-` Passes or rejects the request depending on evaluation of a given conditional
|
||||
expression
|
||||
:ref:`-withRangeSupport-` Transforms the response from its inner route into a ``206 Partial Content``
|
||||
response if the client requested only part of the resource with a ``Range`` header.
|
||||
====================================== =================================================================================
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
.. _BasicDirectives:
|
||||
|
||||
BasicDirectives
|
||||
===============
|
||||
|
||||
Basic directives are building blocks for building :ref:`Custom Directives`. As such they
|
||||
usually aren't used in a route directly but rather in the definition of new directives.
|
||||
|
||||
.. _ProvideDirectives:
|
||||
|
||||
Directives to provide values to inner routes
|
||||
--------------------------------------------
|
||||
|
||||
These directives allow to provide the inner routes with extractions. They can be distinguished
|
||||
on two axes: a) provide a constant value or extract a value from the ``RequestContext`` b) provide
|
||||
a single value or an HList of values.
|
||||
|
||||
* :ref:`-extract-`
|
||||
* :ref:`-textract-`
|
||||
* :ref:`-provide-`
|
||||
* :ref:`-tprovide-`
|
||||
|
||||
.. _Request Transforming Directives:
|
||||
|
||||
Directives transforming the request
|
||||
-----------------------------------
|
||||
|
||||
* :ref:`-mapRequestContext-`
|
||||
* :ref:`-mapRequest-`
|
||||
|
||||
.. _Response Transforming Directives:
|
||||
|
||||
Directives transforming the response
|
||||
------------------------------------
|
||||
|
||||
These directives allow to hook into the response path and transform the complete response or
|
||||
the parts of a response or the list of rejections:
|
||||
|
||||
* :ref:`-mapResponse-`
|
||||
* :ref:`-mapResponseEntity-`
|
||||
* :ref:`-mapResponseHeaders-`
|
||||
* :ref:`-mapRejections-`
|
||||
|
||||
|
||||
.. _Result Transformation Directives:
|
||||
|
||||
Directives transforming the RouteResult
|
||||
---------------------------------------
|
||||
|
||||
These directives allow to transform the RouteResult of the inner route.
|
||||
|
||||
* :ref:`-mapRouteResult-`
|
||||
* :ref:`-mapRouteResponsePF-`
|
||||
|
||||
Directives changing the execution of the inner route
|
||||
----------------------------------------------------
|
||||
|
||||
* :ref:`-mapInnerRoute-`
|
||||
|
||||
Directives alphabetically
|
||||
-------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
extract
|
||||
mapInnerRoute
|
||||
mapRejections
|
||||
mapRequest
|
||||
mapRequestContext
|
||||
mapResponse
|
||||
mapResponseEntity
|
||||
mapResponseHeaders
|
||||
mapRouteResult
|
||||
mapRouteResultPF
|
||||
noop
|
||||
pass
|
||||
provide
|
||||
textract
|
||||
tprovide
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
.. _-mapResponse-:
|
||||
|
||||
mapResponse
|
||||
===============
|
||||
|
||||
Changes the response that was generated by the inner route.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
:snippet: mapResponse
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``mapResponse`` directive is used as a building block for :ref:`Custom Directives` to transform a response that
|
||||
was generated by the inner route. This directive transforms only complete responses. Use :ref:`-mapHttpResponsePart-`,
|
||||
instead, to transform parts of chunked responses as well.
|
||||
|
||||
See :ref:`Response Transforming Directives` for similar directives.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapResponse
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
.. _-mapRouteResponsePF-:
|
||||
|
||||
mapRouteResponsePF
|
||||
==================
|
||||
|
||||
Changes the message the inner route sends to the responder.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRouteResponsePF
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``mapRouteResponsePF`` directive is used as a building block for :ref:`Custom Directives` to transform what
|
||||
the inner route sends to the responder (see :ref:`The Responder Chain`). It's similar to the :ref:`-mapRouteResult-`
|
||||
directive but allows to specify a partial function that doesn't have to handle all the incoming response messages.
|
||||
|
||||
See :ref:`Result Transformation Directives` for similar directives.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: mapRouteResultPF
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
.. _-compressResponse-:
|
||||
|
||||
compressResponse
|
||||
================
|
||||
|
||||
Uses the first of a given number of encodings that the client accepts. If none are accepted the request
|
||||
is rejected with an ``UnacceptedResponseEncodingRejection``.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
|
||||
:snippet: compressResponse
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``compressResponse`` directive allows to specify zero to three encoders to try in the specified order.
|
||||
If none are specified the tried list is ``Gzip``, ``Deflate``, and then ``NoEncoding``.
|
||||
|
||||
The ``compressResponse()`` directive (without an explicit list of encoders given) will therefore behave as follows:
|
||||
|
||||
========================================= ===============================
|
||||
``Accept-Encoding`` header resulting response
|
||||
========================================= ===============================
|
||||
``Accept-Encoding: gzip`` compressed with ``Gzip``
|
||||
``Accept-Encoding: deflate`` compressed with ``Deflate``
|
||||
``Accept-Encoding: deflate, gzip`` compressed with ``Gzip``
|
||||
``Accept-Encoding: identity`` uncompressed
|
||||
no ``Accept-Encoding`` header present compressed with ``Gzip``
|
||||
========================================= ===============================
|
||||
|
||||
For an overview of the different ``compressResponse`` directives see :ref:`WhenToUseWhichCompressResponseDirective`.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
This example shows the behavior of ``compressResponse`` without any encoders specified:
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
|
||||
:snippet: compressResponse-0
|
||||
|
||||
This example shows the behaviour of ``compressResponse(Gzip)``:
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
|
||||
:snippet: compressResponse-1
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
.. _-compressResponseIfRequested-:
|
||||
|
||||
compressResponseIfRequested
|
||||
===========================
|
||||
|
||||
Only compresses the response when specifically requested by the ``Accept-Encoding`` request header
|
||||
(i.e. the default is "no compression").
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
|
||||
:snippet: compressResponseIfRequested
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``compressResponseIfRequested`` directive is an alias for ``compressResponse(NoEncoding, Gzip, Deflate)`` and will
|
||||
behave as follows:
|
||||
|
||||
========================================= ===============================
|
||||
``Accept-Encoding`` header resulting response
|
||||
========================================= ===============================
|
||||
``Accept-Encoding: gzip`` compressed with ``Gzip``
|
||||
``Accept-Encoding: deflate`` compressed with ``Deflate``
|
||||
``Accept-Encoding: deflate, gzip`` compressed with ``Gzip``
|
||||
``Accept-Encoding: identity`` uncompressed
|
||||
no ``Accept-Encoding`` header present uncompressed
|
||||
========================================= ===============================
|
||||
|
||||
For an overview of the different ``compressResponse`` directives see :ref:`WhenToUseWhichCompressResponseDirective`.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
|
||||
:snippet: compressResponseIfRequested
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
.. _-decompressRequest-:
|
||||
|
||||
decompressRequest
|
||||
=================
|
||||
|
||||
Decompresses the request if it is can be decoded with one of the given decoders. Otherwise,
|
||||
the request is rejected with an ``UnsupportedRequestEncodingRejection(supportedEncoding)``.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
|
||||
:snippet: decompressRequest
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``decompressRequest`` directive allows either to specify a list of decoders or none at all. If
|
||||
no ``Decoder`` is specified ``Gzip``, ``Deflate``, or ``NoEncoding`` will be tried.
|
||||
|
||||
The ``decompressRequest`` directive will behave as follows:
|
||||
|
||||
========================================= ===============================
|
||||
``Content-Encoding`` header resulting request
|
||||
========================================= ===============================
|
||||
``Content-Encoding: gzip`` decompressed
|
||||
``Content-Encoding: deflate`` decompressed
|
||||
``Content-Encoding: identity`` unchanged
|
||||
no ``Content-Encoding`` header present unchanged
|
||||
========================================= ===============================
|
||||
|
||||
For an overview of the different ``decompressRequest`` directives and which one to use when,
|
||||
see :ref:`WhenToUseWhichDecompressRequestDirective`.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
This example shows the behavior of ``decompressRequest()`` without any decoders specified:
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
|
||||
:snippet: decompressRequest-0
|
||||
|
||||
This example shows the behaviour of ``decompressRequest(Gzip, NoEncoding)``:
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
|
||||
:snippet: decompressRequest-1
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
.. _CodingDirectives:
|
||||
|
||||
CodingDirectives
|
||||
================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
compressResponse
|
||||
compressResponseIfRequested
|
||||
decodeRequest
|
||||
decompressRequest
|
||||
encodeResponse
|
||||
requestEncodedWith
|
||||
responseEncodingAccepted
|
||||
|
||||
.. _WhenToUseWhichCompressResponseDirective:
|
||||
|
||||
When to use which compression directive?
|
||||
----------------------------------------
|
||||
|
||||
There are three different directives for performing response compressing with slightly different behavior:
|
||||
|
||||
:ref:`-encodeResponse-`
|
||||
Always compresses the response with the one given encoding, rejects the request with an
|
||||
``UnacceptedResponseEncodingRejection`` if the client doesn't accept the given encoding. The other
|
||||
compression directives are built upon this one. See its description for an overview how they
|
||||
relate exactly.
|
||||
|
||||
:ref:`-compressResponse-`
|
||||
Uses the first of a given number of encodings that the client accepts. If none are accepted the request
|
||||
is rejected.
|
||||
|
||||
:ref:`-compressResponseIfRequested-`
|
||||
Only compresses the response when specifically requested by the
|
||||
``Accept-Encoding`` request header (i.e. the default is "no compression").
|
||||
|
||||
See the individual directives for more detailed usage examples.
|
||||
|
||||
.. _WhenToUseWhichDecompressRequestDirective:
|
||||
|
||||
When to use which decompression directive?
|
||||
------------------------------------------
|
||||
|
||||
There are two different directives for performing request decompressing with slightly different behavior:
|
||||
|
||||
:ref:`-decodeRequest-`
|
||||
Attempts to decompress the request using **the one given decoder**, rejects the request with an
|
||||
``UnsupportedRequestEncodingRejection`` if the request is not encoded with the given encoder.
|
||||
|
||||
:ref:`-decompressRequest-`
|
||||
Decompresses the request if it is encoded with **one of the given encoders**.
|
||||
If the request's encoding doesn't match one of the given encoders it is rejected.
|
||||
|
||||
|
||||
Combining compression and decompression
|
||||
---------------------------------------
|
||||
|
||||
As with all Spray directives, the above single directives can be combined
|
||||
using ``&`` to produce compound directives that will decompress requests and
|
||||
compress responses in whatever combination required. Some examples:
|
||||
|
||||
.. includecode2:: /../spray-routing-tests/src/test/scala/spray/routing/EncodingDirectivesSpec.scala
|
||||
:snippet: decompress-compress-combination-example
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
.. _Exception Handling:
|
||||
|
||||
Exception Handling
|
||||
==================
|
||||
|
||||
Exceptions thrown during route execution bubble up through the route structure to the next enclosing
|
||||
:ref:`-handleExceptions-` directive, ``Route.seal`` or the ``onFailure`` callback of a
|
||||
future created by ``detach``.
|
||||
|
||||
Similarly to the way that :ref:`Rejections` are handled the :ref:`-handleExceptions-` directive delegates the actual job of
|
||||
converting a list of rejections to its argument, an ExceptionHandler__, which is defined like this::
|
||||
|
||||
trait ExceptionHandler extends PartialFunction[Throwable, Route]
|
||||
|
||||
__ @github@/akka-http/src/main/scala/akka/http/server/ExceptionHandler.scala
|
||||
|
||||
:ref:`runRoute` defined in :ref:`HttpService` does the same but gets its ``ExceptionHandler`` instance
|
||||
implicitly.
|
||||
|
||||
Since an ``ExceptionHandler`` is a partial function it can choose, which exceptions it would like to handle and
|
||||
which not. Unhandled exceptions will simply continue to bubble up in the route structure. The top-most
|
||||
``ExceptionHandler`` applied by :ref:`runRoute` will handle *all* exceptions that reach it.
|
||||
|
||||
So, if you'd like to customize the way certain exceptions are handled simply bring a custom ``ExceptionHandler`` into
|
||||
implicit scope of :ref:`runRoute` or pass it to an explicit :ref:`-handleExceptions-` directive that you
|
||||
have put somewhere into your route structure.
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/ExceptionHandlerExamplesSpec.scala
|
||||
:snippet: example-1
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
HTTPS
|
||||
=====
|
||||
|
||||
Is not yet supported.
|
||||
|
||||
(todo)
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
.. _http-client-server-scala:
|
||||
|
||||
HTTP Client & Server
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
model
|
||||
client
|
||||
server
|
||||
https
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
.. _http-routing-index-scala:
|
||||
|
||||
HTTP Routing
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
quick-start
|
||||
routing
|
||||
directives
|
||||
rejections
|
||||
exception-handling
|
||||
path-matchers
|
||||
marshalling
|
||||
custom-directives
|
||||
directives/alphabetically
|
||||
directives/by-trait
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _http-testkit-scala:
|
||||
|
||||
HTTP TestKit
|
||||
============
|
||||
|
||||
.. note::
|
||||
|
||||
This part of the documentation has not yet been written, please stay tuned for updates.
|
||||
|
|
@ -3,26 +3,13 @@
|
|||
Akka HTTP
|
||||
=========
|
||||
|
||||
The purpose of the Akka HTTP layer is to expose Actors to the web via HTTP and
|
||||
to enable them to consume HTTP services as a client. It is not an HTTP
|
||||
framework, it is an Actor-based toolkit for interacting with web services and
|
||||
clients. This toolkit is structured into several layers:
|
||||
|
||||
* ``akka-http-core``: A complete implementation of the HTTP standard, both as
|
||||
client and server.
|
||||
|
||||
* ``akka-http``: A convenient and powerful routing DSL for expressing web
|
||||
services.
|
||||
|
||||
* ``akka-http-testkit``: A test harness and set of utilities for verifying
|
||||
your web service implementations.
|
||||
|
||||
Additionally there are several integration modules with bindings for different
|
||||
serialization and deserialization schemes for HTTP entities.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
index-client-server
|
||||
index-routing
|
||||
index-testkit
|
||||
introduction
|
||||
configuration
|
||||
client-side/index
|
||||
low-level-server-side-api
|
||||
routing-dsl/index
|
||||
common/index
|
||||
migration-from-spray
|
||||
|
|
|
|||
32
akka-docs-dev/rst/scala/http/introduction.rst
Normal file
32
akka-docs-dev/rst/scala/http/introduction.rst
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
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 follows a rather open design and many times offers several different API levels for "doing the same thing".
|
||||
You get to pick the API level of abstraction that is most suitable for your application.
|
||||
This 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 do write more application code.
|
||||
|
||||
Akka HTTP is structured into several modules:
|
||||
|
||||
akka-http-core
|
||||
A complete, mostly low-level, server- and client-side implementation of HTTP (incl. WebSockets)
|
||||
|
||||
akka-http
|
||||
Higher-level functionality, like (un)marshalling, (de)compression as well as a powerful DSL
|
||||
for defining HTTP-based APIs on the server-side
|
||||
|
||||
akka-http-testkit
|
||||
A test harness and set of utilities for verifying server-side service implementations
|
||||
|
||||
akka-http-spray-json
|
||||
Predefined glue-code for (de)serializing custom types from/to JSON with spray-json_
|
||||
|
||||
akka-http-xml
|
||||
Predefined glue-code for (de)serializing custom types from/to XML with scala-xml_
|
||||
|
||||
.. _spray-json: https://github.com/spray/spray-json
|
||||
.. _scala-xml: https://github.com/scala/scala-xml
|
||||
135
akka-docs-dev/rst/scala/http/low-level-server-side-api.rst
Normal file
135
akka-docs-dev/rst/scala/http/low-level-server-side-api.rst
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
.. _http-low-level-server-side-api:
|
||||
|
||||
Low-Level Server-Side API
|
||||
=========================
|
||||
|
||||
Apart from the :ref:`HTTP Client <http-client-side>` Akka HTTP also provides an embedded,
|
||||
`Reactive-Streams`_-based, fully asynchronous HTTP/1.1 server implemented on top of :ref:`Akka Stream <streams-scala>`.
|
||||
|
||||
It sports the following features:
|
||||
|
||||
- Full support for `HTTP persistent connections`_
|
||||
- Full support for `HTTP pipelining`_
|
||||
- Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic API
|
||||
- Optional SSL/TLS encryption
|
||||
|
||||
.. _HTTP persistent connections: http://en.wikipedia.org/wiki/HTTP_persistent_connection
|
||||
.. _HTTP pipelining: http://en.wikipedia.org/wiki/HTTP_pipelining
|
||||
.. _Reactive-Streams: http://www.reactive-streams.org/
|
||||
|
||||
|
||||
The server-side components of Akka HTTP are split into two layers:
|
||||
|
||||
1. The basic low-level server implementation in the ``akka-http-core`` module
|
||||
2. Higher-level functionality in the ``akka-http`` module
|
||||
|
||||
The low-level server (1) is scoped with a clear focus on the essential functionality of an HTTP/1.1 server:
|
||||
|
||||
- Connection management
|
||||
- Parsing messages and headers
|
||||
- Timeout management (for requests and connections)
|
||||
- Response ordering (for transparent pipelining support)
|
||||
|
||||
All non-core features of typical HTTP servers (like request routing, file serving, compression, etc.) are left to
|
||||
the higher layers, they are not implemented by the ``akka-http-core``-level server itself.
|
||||
Apart from general focus this design keeps the server core small and light-weight as well as easy to understand and
|
||||
maintain.
|
||||
|
||||
Depending on your needs you can either use the low-level API directly or rely on the high-level
|
||||
:ref:`Routing DSL <http-high-level-server-side-api>` which can make the definition of more complex service logic much
|
||||
easier.
|
||||
|
||||
|
||||
Streams and HTTP
|
||||
----------------
|
||||
|
||||
The Akka HTTP server is implemented on top of :ref:`Akka Stream <streams-scala>` and makes heavy use of it - in its
|
||||
implementation as well as on all levels of its API.
|
||||
|
||||
On the connection level Akka HTTP offers basically the same kind of interface as :ref:`Akka Stream IO <stream-io-scala>`:
|
||||
A socket binding is represented as a stream of incoming connections. The application pulls connections from this stream
|
||||
source and, for each of them, provides a ``Flow[HttpRequest, HttpResponse, _]`` to "translate" requests into responses.
|
||||
|
||||
Apart from regarding a socket bound on the server-side as a ``Source[IncomingConnection]`` and each connection as a
|
||||
``Source[HttpRequest]`` with a ``Sink[HttpResponse]`` the stream abstraction is also present inside a single HTTP
|
||||
message: The entities of HTTP requests and responses are generally modeled as a ``Source[ByteString]``. See also
|
||||
the :ref:`http-model-scala` for more information on how HTTP messages are represented in Akka HTTP.
|
||||
|
||||
|
||||
Starting and Stopping
|
||||
---------------------
|
||||
|
||||
On the most basic level an Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.scaladsl.Http`_
|
||||
extension:
|
||||
|
||||
.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
||||
:snippet: binding-example
|
||||
|
||||
Arguments to the ``Http.bind`` method specify the interface and port to bind to and register interest in handling
|
||||
incoming HTTP connections. Additionally, the method also allows for the definition of socket options as well as a larger
|
||||
number of settings for configuring the server according to your needs.
|
||||
|
||||
The result of the ``bind`` method is a ``Source[Http.IncomingConnection]`` which must be drained by the application in
|
||||
order to accept incoming connections.
|
||||
The actual binding is not performed before this source is materialized as part of a processing pipeline. In
|
||||
case the bind fails (e.g. because the port is already busy) the materialized stream will immediately be terminated with
|
||||
a respective exception.
|
||||
The binding is released (i.e. the underlying socket unbound) when the subscriber of the incoming
|
||||
connection source has cancelled its subscription. Alternatively one can use the ``unbind()`` method of the
|
||||
``Http.ServerBinding`` instance that is created as part of the connection source's materialization process.
|
||||
The ``Http.ServerBinding`` also provides a way to get a hold of the actual local address of the bound socket, which is
|
||||
useful for example when binding to port zero (and thus letting the OS pick an available port).
|
||||
|
||||
.. _akka.http.scaladsl.Http: @github@/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala
|
||||
|
||||
|
||||
Request-Response Cycle
|
||||
----------------------
|
||||
|
||||
When a new connection has been accepted it will be published as an ``Http.IncomingConnection`` which consists
|
||||
of the remote address and methods to provide a ``Flow[HttpRequest, HttpResponse, _]`` to handle requests coming in over
|
||||
this connection.
|
||||
|
||||
Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be
|
||||
|
||||
- a ``Flow[HttpRequest, HttpResponse, _]`` for ``handleWith``,
|
||||
- a function ``HttpRequest => HttpResponse`` for ``handleWithSyncHandler``,
|
||||
- a function ``HttpRequest => Future[HttpResponse]`` for ``handleWithAsyncHandler``.
|
||||
|
||||
Here is a complete example:
|
||||
|
||||
.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
||||
:snippet: full-server-example
|
||||
|
||||
In this example, a request is handled by transforming the request stream with a function ``HttpRequest => HttpResponse``
|
||||
using ``handleWithSyncHandler`` (or equivalently, Akka Stream's ``map`` operator). Depending on the use case many
|
||||
other ways of providing a request handler are conceivable using Akka Stream's combinators.
|
||||
|
||||
If the application provides a ``Flow`` it is also the responsibility of the application to generate exactly one response
|
||||
for every request and that the ordering of responses matches the ordering of the associated requests (which is relevant
|
||||
if HTTP pipelining is enabled where processing of multiple incoming requests may overlap). When relying on
|
||||
``handleWithSyncHandler`` or ``handleWithAsyncHandler``, or the ``map`` or ``mapAsync`` stream operators, this
|
||||
requirement will be automatically fulfilled.
|
||||
|
||||
|
||||
Streaming Request/Response Entities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Streaming of HTTP message entities is supported through subclasses of ``HttpEntity``. The application needs to be able
|
||||
to deal with streamed entities when receiving a request as well as, in many cases, when constructing responses.
|
||||
See :ref:`HttpEntity` for a description of the alternatives.
|
||||
|
||||
If you rely on the :ref:`http-marshalling-scala` and/or :ref:`http-unmarshalling-scala` facilities provided by
|
||||
Akka HTTP then the conversion of custom types to and from streamed entities can be quite convenient.
|
||||
|
||||
|
||||
Closing a connection
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The HTTP connection will be closed when the handling ``Flow`` cancels its upstream subscription or the peer closes the
|
||||
connection. An often times more convenient alternative is to explicitly add a ``Connection: close`` header to an
|
||||
``HttpResponse``. This response will then be the last one on the connection and the server will actively close the
|
||||
connection when it has been sent out.
|
||||
|
||||
|
||||
// TODO: show an example of using the HTTP layer independently with a BidFlow join
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Marshalling
|
||||
===========
|
||||
|
||||
(todo)
|
||||
4
akka-docs-dev/rst/scala/http/migration-from-spray.rst
Normal file
4
akka-docs-dev/rst/scala/http/migration-from-spray.rst
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Migration Guide from spray
|
||||
==========================
|
||||
|
||||
TODO
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
.. _http-model-scala:
|
||||
|
||||
Model
|
||||
=====
|
||||
|
||||
The akka HTTP model contains a mostly immutable, case-class based model of the major HTTP data structures,
|
||||
like HTTP requests, responses and common headers. It also includes a parser for the latter, which is able to construct
|
||||
the more structured header models from raw unstructured header name/value pairs.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Since akka-http-core provides the central HTTP data structures you will find the following import in quite a
|
||||
few places around the code base (and probably your own code as well):
|
||||
|
||||
.. includecode:: ../code/docs/http/ModelSpec.scala
|
||||
:include: import-model
|
||||
|
||||
This brings in scope all of the relevant things that are defined here and that you’ll want to work with, mainly:
|
||||
|
||||
- ``HttpRequest`` and ``HttpResponse``, the central message model
|
||||
- ``headers``, the package containing all the predefined HTTP header models and supporting types
|
||||
- Supporting types like ``Uri``, ``HttpMethods``, ``MediaTypes``, ``StatusCodes``, etc.
|
||||
|
||||
A common pattern is that the model of a certain entity is represented by an immutable type (class or trait),
|
||||
while the actual instances of the entity defined by the HTTP spec live in an accompanying object carrying the name of
|
||||
the type plus a trailing plural 's'.
|
||||
|
||||
For example:
|
||||
|
||||
- Defined HttpMethod instances live in the HttpMethods object.
|
||||
- Defined HttpCharset instances live in the HttpCharsets object.
|
||||
- Defined HttpEncoding instances live in the HttpEncodings object.
|
||||
- Defined HttpProtocol instances live in the HttpProtocols object.
|
||||
- Defined MediaType instances live in the MediaTypes object.
|
||||
- Defined StatusCode instances live in the StatusCodes object.
|
||||
|
||||
HttpRequest / HttpResponse
|
||||
--------------------------
|
||||
|
||||
``HttpRequest`` and ``HttpResponse`` are the basic case classes representing HTTP messages.
|
||||
|
||||
An ``HttpRequest`` consists of
|
||||
|
||||
- method (GET, POST, etc.)
|
||||
- URI
|
||||
- protocol
|
||||
- headers
|
||||
- entity (body data)
|
||||
|
||||
Here are some examples how to construct an ``HttpRequest``:
|
||||
|
||||
.. includecode:: ../code/docs/http/ModelSpec.scala
|
||||
:include: construct-request
|
||||
|
||||
All parameters of ``HttpRequest`` have default values set, so e.g. ``headers`` don't need to be specified
|
||||
if there are none. Many of the parameters types (like ``HttpEntity`` and ``Uri``) define implicit conversions
|
||||
for common use cases to simplify the creation of request and response instances.
|
||||
|
||||
An ``HttpResponse`` consists of
|
||||
|
||||
- status code
|
||||
- protocol
|
||||
- headers
|
||||
- entity
|
||||
|
||||
Here are some examples how to construct an ``HttpResponse``:
|
||||
|
||||
.. includecode:: ../code/docs/http/ModelSpec.scala
|
||||
:include: construct-response
|
||||
|
||||
Aside from the simple ``HttpEntity`` constructors to create an entity from a fixed ``ByteString`` shown here,
|
||||
subclasses of ``HttpEntity`` allow data to be specified as a stream of data which is explained in the next section.
|
||||
|
||||
.. _HttpEntity:
|
||||
|
||||
HttpEntity
|
||||
----------
|
||||
|
||||
An ``HttpEntity`` carries the content of a request together with its content-type which is needed to interpret the raw
|
||||
byte data.
|
||||
|
||||
Akka HTTP provides several kinds of entities to support static and streaming data for the different kinds of ways
|
||||
to transport streaming data with HTTP. There are four subtypes of HttpEntity:
|
||||
|
||||
|
||||
HttpEntity.Strict
|
||||
An entity which wraps a static ``ByteString``. It represents a standard, non-chunked HTTP message with ``Content-Length``
|
||||
set.
|
||||
|
||||
|
||||
HttpEntity.Default
|
||||
A streaming entity which needs a predefined length and a ``Producer[ByteString]`` to produce the body data of
|
||||
the message. It represents a standard, non-chunked HTTP message with ``Content-Length`` set. It is an error if the
|
||||
provided ``Producer[ByteString]`` doesn't produce exactly as many bytes as specified. On the wire, a Strict entity
|
||||
and a Default entity cannot be distinguished. However, they offer a valuable alternative in the API to distinguish
|
||||
between strict and streamed data.
|
||||
|
||||
|
||||
HttpEntity.Chunked
|
||||
A streaming entity of unspecified length that uses `Chunked Transfer Coding`_ for transmitting data. Data is
|
||||
represented by a ``Producer[ChunkStreamPart]``. A ``ChunkStreamPart`` is either a non-empty ``Chunk`` or a ``LastChunk``
|
||||
containing optional trailer headers. The stream must consist of 0..n ``Chunked`` parts and can be terminated by an
|
||||
optional ``LastChunk`` part (which carries optional trailer headers).
|
||||
|
||||
|
||||
HttpEntity.CloseDelimited
|
||||
A streaming entity of unspecified length that is delimited by closing the connection ("Connection: close"). Note,
|
||||
that this entity type can only be used in an ``HttpResponse``.
|
||||
|
||||
HttpEntity.IndefiniteLength
|
||||
A streaming entity of unspecified length that can be used as a ``BodyPart`` entity.
|
||||
|
||||
Entity types ``Strict``, ``Default``, and ``Chunked`` are a subtype of ``HttpEntity.Regular`` which allows to use them for
|
||||
requests and responses. In contrast, ``HttpEntity.CloseDelimited`` can only be used for responses.
|
||||
|
||||
Streaming entity types (i.e. all but ``Strict``) cannot be shared or serialized. To create a strict, sharable copy of an
|
||||
entity or message use ``HttpEntity.toStrict`` or ``HttpMessage.toStrict`` which returns a Future of the object with the
|
||||
body data collected into a ``ByteString``.
|
||||
|
||||
.. _Chunked Transfer Coding: http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-26#section-4.1
|
||||
|
||||
The ``HttpEntity`` companion object contains several helper constructors to create entities from common types easily.
|
||||
|
||||
You can pattern match over the subtypes of ``HttpEntity`` if you want to provide special handling for each of the
|
||||
subtypes. However, in many cases a recipient of an `HttpEntity` doesn't care about of which subtype an entity is
|
||||
(and how data is transported exactly on the HTTP layer). Therefore, a general ``HttpEntity.dataBytes`` is provided
|
||||
which allows access to the data of an entity regardless of its concrete subtype.
|
||||
|
||||
.. note::
|
||||
|
||||
When to use which subtype?
|
||||
- Use Strict if the amount of data is small and it is already in the heap (or even available as a ``ByteString``)
|
||||
- Use Default if the data is generated by a streaming data source and the size of the data is fixed
|
||||
- Use Chunked to support a data stream of unknown length
|
||||
- Use CloseDelimited for a response as an alternative to Chunked e.g. if chunked transfer encoding isn't supported
|
||||
by a client.
|
||||
- Use IndefiniteLength instead of CloseDelimited in a BodyPart.
|
||||
|
||||
Header model
|
||||
------------
|
||||
|
||||
Akka HTTP contains a rich model of the common HTTP headers. Parsing and rendering is done automatically so that
|
||||
applications don't need to care for the actual syntax of headers. Headers not modelled explicitly are represented
|
||||
as a ``RawHeader``.
|
||||
|
||||
See these examples of how to deal with headers:
|
||||
|
||||
.. includecode:: ../code/docs/http/ModelSpec.scala
|
||||
:include: headers
|
||||
|
||||
Parsing / Rendering
|
||||
-------------------
|
||||
|
||||
Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API
|
||||
provided to parse (or render to) Strings or byte arrays.
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Quick-Start
|
||||
===========
|
||||
|
||||
(todo)
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
.. _Rejections:
|
||||
|
||||
Rejections
|
||||
==========
|
||||
|
||||
In the chapter about constructing :ref:`Routes` the ``~`` operator was introduced, which connects two routes in a way
|
||||
that allows a second route to get a go at a request if the first route "rejected" it. The concept of "rejections" is
|
||||
used by Akka HTTP for maintaining a more functional overall architecture and in order to be able to properly
|
||||
handle all kinds of error scenarios.
|
||||
|
||||
When a filtering directive, like the :ref:`-get-` directive, cannot let the request pass through to its inner Route because
|
||||
the filter condition is not satisfied (e.g. because the incoming request is not a GET request) the directive doesn't
|
||||
immediately complete the request with an error response. Doing so would make it impossible for other routes chained in
|
||||
after the failing filter to get a chance to handle the request.
|
||||
Rather, failing filters "reject" the request in the same way as by explicitly calling ``requestContext.reject(...)``.
|
||||
|
||||
After having been rejected by a route the request will continue to flow through the routing structure and possibly find
|
||||
another route that can complete it. If there are more rejections all of them will be picked up and collected.
|
||||
|
||||
If the request cannot be completed by (a branch of) the route structure an enclosing :ref:`-handleRejections-` directive
|
||||
can be used to convert a set of rejections into an ``HttpResponse`` (which, in most cases, will be an error response).
|
||||
``Route.seal`` internally wraps its argument route with the :ref:`-handleRejections-` directive in order to "catch"
|
||||
and handle any rejection.
|
||||
|
||||
|
||||
Predefined Rejections
|
||||
---------------------
|
||||
|
||||
A rejection encapsulates a specific reason why a Route was not able to handle a request. It is modeled as an object of
|
||||
type ``Rejection``. Akka HTTP comes with a set of `predefined rejections`__, which are used by various
|
||||
:ref:`predefined directives <Predefined Directives>`.
|
||||
|
||||
Rejections are gathered up over the course of a Route evaluation and finally converted to ``HttpResponse`` replies by
|
||||
the :ref:`-handleRejections-` directive if there was no way for the request to be completed.
|
||||
|
||||
__ @github@/akka-http/src/main/scala/akka/http/server/Rejection.scala
|
||||
|
||||
|
||||
.. _RejectionHandler:
|
||||
|
||||
RejectionHandler
|
||||
----------------
|
||||
|
||||
The :ref:`-handleRejections-` directive delegates the actual job of converting a list of rejections to its argument, a
|
||||
RejectionHandler__, which is defined like this::
|
||||
|
||||
trait RejectionHandler extends PartialFunction[List[Rejection], Route]
|
||||
|
||||
__ @github@/akka-http/src/main/scala/akka/http/server/RejectionHandler.scala
|
||||
|
||||
Since a ``RejectionHandler`` is a partial function it can choose, which rejections it would like to handle and
|
||||
which not. Unhandled rejections will simply continue to flow through the route structure. The top-most
|
||||
``RejectionHandler`` applied by :ref:`runRoute` will handle *all* rejections that reach it.
|
||||
|
||||
So, if you'd like to customize the way certain rejections are handled simply bring a custom ``RejectionHandler`` into
|
||||
implicit scope of :ref:`runRoute` or pass it to an explicit :ref:`-handleRejections-` directive that you
|
||||
have put somewhere into your route structure.
|
||||
|
||||
Here is an example:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/RejectionHandlerExamplesSpec.scala
|
||||
:snippet: example-1
|
||||
|
||||
|
||||
Rejection Cancellation
|
||||
----------------------
|
||||
|
||||
As you can see from its definition above the ``RejectionHandler`` handles not single rejections but a whole list of
|
||||
them. This is because some route structure produce several "reasons" why a request could not be handled.
|
||||
|
||||
Take this route structure for example:
|
||||
|
||||
.. includecode2:: ../code/docs/http/server/RejectionHandlerExamplesSpec.scala
|
||||
:snippet: example-2
|
||||
|
||||
For uncompressed POST requests this route structure could yield two rejections:
|
||||
|
||||
- a ``MethodRejection`` produced by the :ref:`-get-` directive (which rejected because the request is not a GET request)
|
||||
- an ``UnsupportedRequestEncodingRejection`` produced by the :ref:`-decodeRequest-` directive (which only accepts
|
||||
gzip-compressed requests)
|
||||
|
||||
In reality the route even generates one more rejection, a ``TransformationRejection`` produced by the :ref:`-post-`
|
||||
directive. It "cancels" all other potentially existing *MethodRejections*, since they are invalid after the
|
||||
:ref:`-post-` directive allowed the request to pass (after all, the route structure *can* deal with POST requests).
|
||||
These types of rejection cancellations are resolved *before* a ``RejectionHandler`` sees the rejection list.
|
||||
So, for the example above the ``RejectionHandler`` will be presented with only a single-element rejection list,
|
||||
containing nothing but the ``UnsupportedRequestEncodingRejection``.
|
||||
|
||||
.. _empty rejections:
|
||||
|
||||
Empty Rejections
|
||||
----------------
|
||||
|
||||
Since rejections are passed around in lists you might ask yourself what the semantics of an empty rejection list are.
|
||||
In fact, empty rejection lists have well defined semantics. They signal that a request was not handled because the
|
||||
respective resource could not be found. Akka HTTP reserves the special status of "empty rejection" to this most
|
||||
common failure a service is likely to produce.
|
||||
|
||||
So, for example, if the :ref:`-path-` directive rejects a request, it does so with an empty rejection list. The
|
||||
:ref:`-host-` directive behaves in the same way.
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
.. _Case Class Extraction:
|
||||
|
||||
Case Class Extraction
|
||||
=====================
|
||||
|
||||
The value extraction performed by :ref:`Directives` is a nice way of providing your route logic with interesting request
|
||||
properties, all with proper type-safety and error handling. However, in some case you might want even more.
|
||||
Consider this example:
|
||||
|
||||
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
|
||||
:snippet: example-1
|
||||
|
||||
Here the :ref:`-parameters-` directives is employed to extract three ``Int`` values, which are then used to construct an
|
||||
instance of the ``Color`` case class. So far so good. However, if the model classes we'd like to work with have more
|
||||
than just a few parameters the overhead introduced by capturing the arguments as extractions only to feed them into the
|
||||
model class constructor directly afterwards can somewhat clutter up your route definitions.
|
||||
|
||||
If your model classes are case classes, as in our example, Akka HTTP supports an even shorter and more concise
|
||||
syntax. You can also write the example above like this:
|
||||
|
||||
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
|
||||
:snippet: example-2
|
||||
|
||||
You can postfix any directive with extractions with an ``as(...)`` call. By simply passing the companion object of your
|
||||
model case class to the ``as`` modifier method the underlying directive is transformed into an equivalent one, which
|
||||
extracts only one value of the type of your model class. Note that there is no reflection involved and your case class
|
||||
does not have to implement any special interfaces. The only requirement is that the directive you attach the ``as``
|
||||
call to produces the right number of extractions, with the right types and in the right order.
|
||||
|
||||
If you'd like to construct a case class instance from extractions produced by *several* directives you can first join
|
||||
the directives with the ``&`` operator before using the ``as`` call:
|
||||
|
||||
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
|
||||
:snippet: example-3
|
||||
|
||||
Here the ``Color`` class has gotten another member, ``name``, which is supplied not as a parameter but as a path
|
||||
element. By joining the ``path`` and ``parameters`` directives with ``&`` you create a directive extracting 4 values,
|
||||
which directly fit the member list of the ``Color`` case class. Therefore you can use the ``as`` modifier to convert
|
||||
the directive into one extracting only a single ``Color`` instance.
|
||||
|
||||
Generally, when you have routes that work with, say, more than 3 extractions it's a good idea to introduce a case class
|
||||
for these and resort to case class extraction. Especially since it supports another nice feature: validation.
|
||||
|
||||
|
||||
.. caution:: There is one quirk to look out for when using case class extraction: If you create an explicit companion
|
||||
object for your case class, no matter whether you actually add any members to it or not, the syntax presented above
|
||||
will not (quite) work anymore. Instead of ``as(Color)`` you will then have to say ``as(Color.apply)``. This behavior
|
||||
appears as if it's not really intended, so this might be improved in future Scala versions.
|
||||
|
||||
|
||||
Case Class Validation
|
||||
---------------------
|
||||
|
||||
In many cases your web service needs to verify input parameters according to some logic before actually working with
|
||||
them. E.g. in the example above the restriction might be that all color component values must be between 0 and 255.
|
||||
You could get this done with a few :ref:`-validate-` directives but this would quickly become cumbersome and hard to
|
||||
read.
|
||||
|
||||
If you use case class extraction you can put the verification logic into the constructor of your case class, where it
|
||||
should be:
|
||||
|
||||
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
|
||||
:snippet: example-4
|
||||
|
||||
If you write your validations like this Akka HTTP's case class extraction logic will properly pick up all error
|
||||
messages and generate a ``ValidationRejection`` if something goes wrong. By default, ``ValidationRejections`` are
|
||||
converted into ``400 Bad Request`` error response by the default :ref:`RejectionHandler <The RejectionHandler>`, if no
|
||||
subsequent route successfully handles the request.
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
.. _Predefined Directives:
|
||||
|
||||
Predefined Directives (alphabetically)
|
||||
======================================
|
||||
|
||||
=========================================== ============================================================================
|
||||
Directive Description
|
||||
=========================================== ============================================================================
|
||||
:ref:`-authenticateBasic-` ...
|
||||
:ref:`-authenticateBasicAsync-` ...
|
||||
:ref:`-authenticateBasicPF-` ...
|
||||
:ref:`-authenticateBasicPFAsync-` ...
|
||||
:ref:`-authenticateOrRejectWithChallenge-` ...
|
||||
:ref:`-authorize-` ...
|
||||
:ref:`-cancelRejection-` ...
|
||||
:ref:`-cancelRejections-` ...
|
||||
:ref:`-complete-` ...
|
||||
:ref:`-completeOrRecoverWith-` ...
|
||||
:ref:`-completeWith-` ...
|
||||
:ref:`-conditional-` ...
|
||||
:ref:`-cookie-` ...
|
||||
:ref:`-decodeRequest-` ...
|
||||
:ref:`-decodeRequestWith-` ...
|
||||
:ref:`-delete-` ...
|
||||
:ref:`-deleteCookie-` ...
|
||||
:ref:`-encodeResponse-` ...
|
||||
:ref:`-encodeResponseWith-` ...
|
||||
:ref:`-entity-` ...
|
||||
:ref:`-extract-` ...
|
||||
:ref:`-extractClientIP-` ...
|
||||
:ref:`-extractCredentials-` ...
|
||||
:ref:`-extractExecutionContext-` ...
|
||||
:ref:`-extractFlowMaterializer-` ...
|
||||
:ref:`-extractHost-` ...
|
||||
:ref:`-extractLog-` ...
|
||||
:ref:`-extractMethod-` ...
|
||||
:ref:`-extractRequest-` ...
|
||||
:ref:`-extractRequestContext-` ...
|
||||
:ref:`-extractScheme-` ...
|
||||
:ref:`-extractSettings-` ...
|
||||
:ref:`-extractUnmatchedPath-` ...
|
||||
:ref:`-extractUri-` ...
|
||||
:ref:`-failWith-` ...
|
||||
:ref:`-formField-` ...
|
||||
:ref:`-formFields-` ...
|
||||
:ref:`-get-` ...
|
||||
:ref:`-getFromBrowseableDirectories-` ...
|
||||
:ref:`-getFromBrowseableDirectory-` ...
|
||||
:ref:`-getFromDirectory-` ...
|
||||
:ref:`-getFromFile-` ...
|
||||
:ref:`-getFromResource-` ...
|
||||
:ref:`-getFromResourceDirectory-` ...
|
||||
:ref:`-handleExceptions-` ...
|
||||
:ref:`-handleRejections-` ...
|
||||
:ref:`-handleWebsocketMessages-` ...
|
||||
:ref:`-handleWith-` ...
|
||||
:ref:`-head-` ...
|
||||
:ref:`-headerValue-` ...
|
||||
:ref:`-headerValueByName-` ...
|
||||
:ref:`-headerValueByType-` ...
|
||||
:ref:`-headerValuePF-` ...
|
||||
:ref:`-host-` ...
|
||||
:ref:`-listDirectoryContents-` ...
|
||||
:ref:`-logRequest-` ...
|
||||
:ref:`-logRequestResult-` ...
|
||||
:ref:`-logResult-` ...
|
||||
:ref:`-mapInnerRoute-` ...
|
||||
:ref:`-mapRejections-` ...
|
||||
:ref:`-mapRequest-` ...
|
||||
:ref:`-mapRequestContext-` ...
|
||||
:ref:`-mapResponse-` ...
|
||||
:ref:`-mapResponseEntity-` ...
|
||||
:ref:`-mapResponseHeaders-` ...
|
||||
:ref:`-mapRouteResult-` ...
|
||||
:ref:`-mapRouteResultFuture-` ...
|
||||
:ref:`-mapRouteResultPF-` ...
|
||||
:ref:`-mapRouteResultWith-` ...
|
||||
:ref:`-mapRouteResultWithPF-` ...
|
||||
:ref:`-mapSettings-` ...
|
||||
:ref:`-mapUnmatchedPath-` ...
|
||||
:ref:`-method-` ...
|
||||
:ref:`-onComplete-` ...
|
||||
:ref:`-onSuccess-` ...
|
||||
:ref:`-optionalCookie-` ...
|
||||
:ref:`-optionalHeaderValue-` ...
|
||||
:ref:`-optionalHeaderValueByName-` ...
|
||||
:ref:`-optionalHeaderValueByType-` ...
|
||||
:ref:`-optionalHeaderValuePF-` ...
|
||||
:ref:`-options-` ...
|
||||
:ref:`-overrideMethodWithParameter-` ...
|
||||
:ref:`-overrideStatusCode-` ...
|
||||
:ref:`-parameter-` ...
|
||||
:ref:`-parameterMap-` ...
|
||||
:ref:`-parameterMultiMap-` ...
|
||||
:ref:`-parameters-` ...
|
||||
:ref:`-parameterSeq-` ...
|
||||
:ref:`-pass-` ...
|
||||
:ref:`-patch-` ...
|
||||
:ref:`-path-` ...
|
||||
:ref:`-pathEnd-` ...
|
||||
:ref:`-pathEndOrSingleSlash-` ...
|
||||
:ref:`-pathPrefix-` ...
|
||||
:ref:`-pathPrefixTest-` ...
|
||||
:ref:`-pathSingleSlash-` ...
|
||||
:ref:`-pathSuffix-` ...
|
||||
:ref:`-pathSuffixTest-` ...
|
||||
:ref:`-post-` ...
|
||||
:ref:`-provide-` ...
|
||||
:ref:`-put-` ...
|
||||
:ref:`-rawPathPrefix-` ...
|
||||
:ref:`-rawPathPrefixTest-` ...
|
||||
:ref:`-recoverRejections-` ...
|
||||
:ref:`-recoverRejectionsWith-` ...
|
||||
:ref:`-redirect-` ...
|
||||
:ref:`-redirectToNoTrailingSlashIfPresent-` ...
|
||||
:ref:`-redirectToTrailingSlashIfMissing-` ...
|
||||
:ref:`-reject-` ...
|
||||
:ref:`-rejectEmptyResponse-` ...
|
||||
:ref:`-requestEncodedWith-` ...
|
||||
:ref:`-requestEntityEmpty-` ...
|
||||
:ref:`-requestEntityPresent-` ...
|
||||
:ref:`-respondWithDefaultHeader-` ...
|
||||
:ref:`-respondWithDefaultHeaders-` ...
|
||||
:ref:`-respondWithHeader-` ...
|
||||
:ref:`-respondWithHeaders-` ...
|
||||
:ref:`-respondWithHeaders-` ...
|
||||
:ref:`-responseEncodingAccepted-` ...
|
||||
:ref:`-scheme-` ...
|
||||
:ref:`-setCookie-` ...
|
||||
:ref:`-textract-` ...
|
||||
:ref:`-tprovide-` ...
|
||||
:ref:`-validate-` ...
|
||||
:ref:`-withExecutionContext-` ...
|
||||
:ref:`-withFlowMaterializer-` ...
|
||||
:ref:`-withLog-` ...
|
||||
:ref:`-withRangeSupport-` ...
|
||||
:ref:`-withSettings-` ...
|
||||
=========================================== ============================================================================
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-cancelRejection-:
|
||||
|
||||
cancelRejection
|
||||
===============
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: cancelRejection
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0cancelRejection
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-cancelRejections-:
|
||||
|
||||
cancelRejections
|
||||
================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: cancelRejections
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0cancelRejections
|
||||
|
|
@ -8,7 +8,7 @@ Calculates a value from the request context and provides the value to the inner
|
|||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extract
|
||||
|
||||
Description
|
||||
|
|
@ -23,5 +23,5 @@ See :ref:`ProvideDirectives` for an overview of similar directives.
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extract
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractExecutionContext-:
|
||||
|
||||
extractExecutionContext
|
||||
=======================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractExecutionContext
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractExecutionContext
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractFlowMaterializer-:
|
||||
|
||||
extractFlowMaterializer
|
||||
=======================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractFlowMaterializer
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractFlowMaterializer
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractLog-:
|
||||
|
||||
extractLog
|
||||
==========
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractLog
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractLog
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractRequest-:
|
||||
|
||||
extractRequest
|
||||
==============
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractRequest
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractRequest
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractRequestContext-:
|
||||
|
||||
extractRequestContext
|
||||
=====================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractRequestContext
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractRequestContext
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractSettings-:
|
||||
|
||||
extractSettings
|
||||
===============
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractSettings
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractSettings
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractUnmatchedPath-:
|
||||
|
||||
extractUnmatchedPath
|
||||
====================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractUnmatchedPath
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractUnmatchedPath
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-extractUri-:
|
||||
|
||||
extractUri
|
||||
==========
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: extractUri
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0extractUri
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
.. _BasicDirectives:
|
||||
|
||||
BasicDirectives
|
||||
===============
|
||||
|
||||
Basic directives are building blocks for building :ref:`Custom Directives`. As such they
|
||||
usually aren't used in a route directly but rather in the definition of new directives.
|
||||
|
||||
|
||||
.. _ProvideDirectives:
|
||||
|
||||
Providing Values to Inner Routes
|
||||
--------------------------------
|
||||
|
||||
These directives provide values to the inner routes with extractions. They can be distinguished
|
||||
on two axes: a) provide a constant value or extract a value from the ``RequestContext`` b) provide
|
||||
a single value or a tuple of values.
|
||||
|
||||
* :ref:`-extract-`
|
||||
* :ref:`-extractExecutionContext-`
|
||||
* :ref:`-extractFlowMaterializer-`
|
||||
* :ref:`-extractLog-`
|
||||
* :ref:`-extractRequest-`
|
||||
* :ref:`-extractRequestContext-`
|
||||
* :ref:`-extractSettings-`
|
||||
* :ref:`-extractUnmatchedPath-`
|
||||
* :ref:`-extractUri-`
|
||||
* :ref:`-textract-`
|
||||
* :ref:`-provide-`
|
||||
* :ref:`-tprovide-`
|
||||
|
||||
|
||||
.. _Request Transforming Directives:
|
||||
|
||||
Transforming the Request(Context)
|
||||
---------------------------------
|
||||
|
||||
* :ref:`-mapRequest-`
|
||||
* :ref:`-mapRequestContext-`
|
||||
* :ref:`-mapSettings-`
|
||||
* :ref:`-mapUnmatchedPath-`
|
||||
* :ref:`-withExecutionContext-`
|
||||
* :ref:`-withFlowMaterializer-`
|
||||
* :ref:`-withLog-`
|
||||
* :ref:`-withSettings-`
|
||||
|
||||
|
||||
.. _Response Transforming Directives:
|
||||
|
||||
Transforming the Response
|
||||
-------------------------
|
||||
|
||||
These directives allow to hook into the response path and transform the complete response or
|
||||
the parts of a response or the list of rejections:
|
||||
|
||||
* :ref:`-mapResponse-`
|
||||
* :ref:`-mapResponseEntity-`
|
||||
* :ref:`-mapResponseHeaders-`
|
||||
|
||||
|
||||
.. _Result Transformation Directives:
|
||||
|
||||
Transforming the RouteResult
|
||||
----------------------------
|
||||
|
||||
These directives allow to transform the RouteResult of the inner route.
|
||||
|
||||
* :ref:`-cancelRejection-`
|
||||
* :ref:`-cancelRejections-`
|
||||
* :ref:`-mapRejections-`
|
||||
* :ref:`-mapRouteResult-`
|
||||
* :ref:`-mapRouteResultFuture-`
|
||||
* :ref:`-mapRouteResultPF-`
|
||||
* :ref:`-mapRouteResultWith-`
|
||||
* :ref:`-mapRouteResultWithPF-`
|
||||
* :ref:`-recoverRejections-`
|
||||
* :ref:`-recoverRejectionsWith-`
|
||||
|
||||
|
||||
Other
|
||||
-----
|
||||
|
||||
* :ref:`-mapInnerRoute-`
|
||||
* :ref:`-pass-`
|
||||
|
||||
|
||||
Alphabetically
|
||||
--------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
cancelRejection
|
||||
cancelRejections
|
||||
extract
|
||||
extractExecutionContext
|
||||
extractFlowMaterializer
|
||||
extractLog
|
||||
extractRequest
|
||||
extractRequestContext
|
||||
extractSettings
|
||||
extractUnmatchedPath
|
||||
extractUri
|
||||
mapInnerRoute
|
||||
mapRejections
|
||||
mapRequest
|
||||
mapRequestContext
|
||||
mapResponse
|
||||
mapResponseEntity
|
||||
mapResponseHeaders
|
||||
mapRouteResult
|
||||
mapRouteResultFuture
|
||||
mapRouteResultPF
|
||||
mapRouteResultWith
|
||||
mapRouteResultWithPF
|
||||
mapSettings
|
||||
mapUnmatchedPath
|
||||
pass
|
||||
provide
|
||||
recoverRejections
|
||||
recoverRejectionsWith
|
||||
textract
|
||||
tprovide
|
||||
withExecutionContext
|
||||
withFlowMaterializer
|
||||
withLog
|
||||
withSettings
|
||||
|
|
@ -8,7 +8,7 @@ Changes the execution model of the inner route by wrapping it with arbitrary log
|
|||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapInnerRoute
|
||||
|
||||
Description
|
||||
|
|
@ -20,5 +20,5 @@ with any other route. Usually, the returned route wraps the original one with cu
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: mapInnerRoute
|
||||
|
|
@ -8,7 +8,7 @@ Transforms the list of rejections the inner route produced.
|
|||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRejections
|
||||
|
||||
Description
|
||||
|
|
@ -22,5 +22,5 @@ See :ref:`Response Transforming Directives` for similar directives.
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: mapRejections
|
||||
|
|
@ -8,7 +8,7 @@ Transforms the request before it is handled by the inner route.
|
|||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRequest
|
||||
|
||||
Description
|
||||
|
|
@ -24,5 +24,5 @@ See :ref:`Request Transforming Directives` for an overview of similar directives
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapRequest
|
||||
|
|
@ -8,7 +8,7 @@ Transforms the ``RequestContext`` before it is passed to the inner route.
|
|||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRequestContext
|
||||
|
||||
Description
|
||||
|
|
@ -23,5 +23,5 @@ See :ref:`Request Transforming Directives` for an overview of similar directives
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: mapRequestContext
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
.. _-mapResponse-:
|
||||
|
||||
mapResponse
|
||||
===========
|
||||
|
||||
Changes the response that was generated by the inner route.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapResponse
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``mapResponse`` directive is used as a building block for :ref:`Custom Directives` to transform a response that
|
||||
was generated by the inner route. This directive transforms complete responses.
|
||||
|
||||
See also :ref:`-mapResponseHeaders-` or :ref:`-mapResponseEntity-` for more specialized variants and
|
||||
:ref:`Response Transforming Directives` for similar directives.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapResponse
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
.. _-mapResponseEntity-:
|
||||
|
||||
mapResponseEntity
|
||||
=====================
|
||||
=================
|
||||
|
||||
Changes the response entity that was generated by the inner route.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapResponseEntity
|
||||
|
||||
Description
|
||||
|
|
@ -22,5 +22,5 @@ See :ref:`Response Transforming Directives` for similar directives.
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: mapResponseEntity
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
.. _-mapResponseHeaders-:
|
||||
|
||||
mapResponseHeaders
|
||||
======================
|
||||
==================
|
||||
|
||||
Changes the list of response headers that was generated by the inner route.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapResponseHeaders
|
||||
|
||||
Description
|
||||
|
|
@ -22,5 +22,5 @@ See :ref:`Response Transforming Directives` for similar directives.
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: mapResponseHeaders
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
.. _-mapRouteResult-:
|
||||
|
||||
mapRouteResult
|
||||
================
|
||||
==============
|
||||
|
||||
Changes the message the inner route sends to the responder.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRouteResult
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform what
|
||||
the inner route sends to the responder (see :ref:`The Responder Chain`).
|
||||
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform the
|
||||
:ref:`RouteResult` coming back from the inner route.
|
||||
|
||||
See :ref:`Result Transformation Directives` for similar directives.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapRouteResult
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-mapRouteResultFuture-:
|
||||
|
||||
mapRouteResultFuture
|
||||
====================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRouteResultFuture
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapRouteResultFuture
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
.. _-mapRouteResultPF-:
|
||||
|
||||
mapRouteResultPF
|
||||
================
|
||||
|
||||
Changes the message the inner route sends to the responder.
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRouteResultPF
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform the
|
||||
:ref:`RouteResult` coming back from the inner route. It's similar to the :ref:`-mapRouteResult-` directive but allows to
|
||||
specify a partial function that doesn't have to handle all potential ``RouteResult`` instances.
|
||||
|
||||
See :ref:`Result Transformation Directives` for similar directives.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: mapRouteResultPF
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-mapRouteResultWith-:
|
||||
|
||||
mapRouteResultWith
|
||||
==================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRouteResultWith
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapRouteResultWith
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-mapRouteResultWithPF-:
|
||||
|
||||
mapRouteResultWithPF
|
||||
====================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapRouteResultWithPF
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapRouteResultWithPF
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-mapSettings-:
|
||||
|
||||
mapSettings
|
||||
===========
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapSettings
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapSettings
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-mapUnmatchedPath-:
|
||||
|
||||
mapUnmatchedPath
|
||||
================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: mapUnmatchedPath
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0mapUnmatchedPath
|
||||
|
|
@ -8,7 +8,7 @@ A directive that passes the request unchanged to its inner route.
|
|||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: pass
|
||||
|
||||
Description
|
||||
|
|
@ -20,6 +20,6 @@ The directive is usually used as a "neutral element" when combining directives g
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: pass
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ Provides a constant value to the inner route.
|
|||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: provide
|
||||
|
||||
Description
|
||||
|
|
@ -22,5 +22,5 @@ See :ref:`ProvideDirectives` for an overview of similar directives.
|
|||
Example
|
||||
-------
|
||||
|
||||
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0provide
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-recoverRejections-:
|
||||
|
||||
recoverRejections
|
||||
=================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: recoverRejections
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0recoverRejections
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
.. _-recoverRejectionsWith-:
|
||||
|
||||
recoverRejectionsWith
|
||||
=====================
|
||||
|
||||
...
|
||||
|
||||
Signature
|
||||
---------
|
||||
|
||||
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
|
||||
:snippet: recoverRejectionsWith
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
...
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
|
||||
:snippet: 0recoverRejectionsWith
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue