=doc a first set of new and imported documentation for akka-http

This commit is contained in:
Johannes Rudolph 2014-12-18 09:25:33 +01:00
parent 6f11735765
commit af14fd8243
81 changed files with 3674 additions and 54 deletions

View file

@ -0,0 +1,109 @@
import os
import codecs
import re
from os import path
from docutils import nodes
from docutils.parsers.rst import Directive, directives
class IncludeCode2(Directive):
"""
Include a code example from a file with sections delimited with special comments.
"""
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {
'snippet': directives.unchanged_required
}
def run(self):
document = self.state.document
arg0 = self.arguments[0]
(filename, sep, section) = arg0.partition('#')
if not document.settings.file_insertion_enabled:
return [document.reporter.warning('File insertion disabled', line=self.lineno)]
env = document.settings.env
if filename.startswith('/') or filename.startswith(os.sep):
rel_fn = filename[1:]
else:
docdir = path.dirname(env.doc2path(env.docname, base=None))
rel_fn = path.join(docdir, filename)
try:
fn = path.join(env.srcdir, rel_fn)
except UnicodeDecodeError:
# the source directory is a bytestring with non-ASCII characters;
# let's try to encode the rel_fn in the file system encoding
rel_fn = rel_fn.encode(sys.getfilesystemencoding())
fn = path.join(env.srcdir, rel_fn)
encoding = self.options.get('encoding', env.config.source_encoding)
codec_info = codecs.lookup(encoding)
try:
f = codecs.StreamReaderWriter(open(fn, 'U'),
codec_info[2], codec_info[3], 'strict')
lines = f.readlines()
f.close()
except (IOError, OSError):
return [document.reporter.warning(
'Include file %r not found or reading it failed' % fn,
line=self.lineno)]
except UnicodeError:
return [document.reporter.warning(
'Encoding %r used for reading included file %r seems to '
'be wrong, try giving an :encoding: option' %
(encoding, fn))]
snippet = self.options.get('snippet')
current_snippets = ""
res = []
for line in lines:
comment = line.rstrip().split("//", 1)[1] if line.find("//") >= 0 else ""
if comment.startswith("#") and len(comment) > 1:
current_snippets = comment
indent = line.find("//")
elif len(line) > 2 and line[2] == '"' and not current_snippets.startswith("#"):
current_snippets = line[2:]
indent = 4
elif comment == "#" and current_snippets.startswith("#"):
current_snippets = ""
elif len(line) > 2 and line[2] == '}' and not current_snippets.startswith("#"):
current_snippets = ""
elif current_snippets.find(snippet) >= 0 and comment.find("hide") == -1:
res.append(line[indent:].rstrip() + '\n')
elif comment.find(snippet) >= 0:
array = line.split("//", 1)
l = array[0].rstrip() if array[1].startswith("/") > 0 else array[0].strip()
res.append(l + '\n')
elif re.search("(def|val) "+re.escape(snippet)+"(?=[:(\[])", line):
# match signature line from `def <snippet>` but without trailing `=`
start = line.find("def")
if start == -1: start = line.find("val")
# include `implicit` if definition is prefixed with it
implicitstart = line.find("implicit")
if implicitstart != -1 and implicitstart < start: start = implicitstart
end = line.rfind("=")
if end == -1: current_snippets = "matching_signature"
res.append(line[start:end] + '\n')
elif current_snippets == "matching_signature":
end = line.rfind("=")
if end != -1: current_snippets = ""
res.append(line[start:end] + '\n')
text = ''.join(res)
if text == "":
return [document.reporter.warning('Snippet "' + snippet + '" not found!', line=self.lineno)]
retnode = nodes.literal_block(text, text, source=fn)
document.settings.env.note_dependency(rel_fn)
return [retnode]
def setup(app):
app.require_sphinx('1.0')
app.add_directive('includecode2', IncludeCode2)

View file

@ -8,7 +8,7 @@ import sys, os
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
sys.path.append(os.path.abspath('../_sphinx/exts')) sys.path.append(os.path.abspath('../_sphinx/exts'))
extensions = ['sphinx.ext.todo', 'includecode'] extensions = ['sphinx.ext.todo', 'includecode', 'includecode2']
templates_path = ['_templates'] templates_path = ['_templates']
source_suffix = '.rst' source_suffix = '.rst'

View file

@ -22,9 +22,8 @@ class HttpServerExampleSpec
implicit val materializer = FlowMaterializer() implicit val materializer = FlowMaterializer()
val serverBinding = Http(system).bind(interface = "localhost", port = 8080) val serverBinding = Http(system).bind(interface = "localhost", port = 8080)
for (connection <- serverBinding.connections) { serverBinding.connections.foreach { connection // foreach materializes the source
println("Accepted new connection from " + connection.remoteAddress) println("Accepted new connection from " + connection.remoteAddress)
// handle connection here
} }
//#bind-example //#bind-example
} }
@ -56,7 +55,9 @@ class HttpServerExampleSpec
serverBinding.connections foreach { connection => serverBinding.connections foreach { connection =>
println("Accepted new connection from " + connection.remoteAddress) println("Accepted new connection from " + connection.remoteAddress)
connection handleWith { Flow[HttpRequest] map requestHandler } connection handleWithSyncHandler requestHandler
// this is equivalent to
// connection handleWith { Flow[HttpRequest] map requestHandler }
} }
//#full-server-example //#full-server-example
} }

View file

@ -0,0 +1,126 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
import akka.http.server._
import Directives._
import akka.http.testkit.ScalatestRouteTest
import org.scalatest._
class DirectiveExamplesSpec extends RoutingSpec {
"example-1, example-2" in {
val route: Route =
path("order" / IntNumber) { id =>
get {
complete {
"Received GET request for order " + id
}
} ~
put {
complete {
"Received PUT request for order " + id
}
}
}
verify(route) // hide
}
"example-3" in {
def innerRoute(id: Int): Route =
get {
complete {
"Received GET 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-4" in {
val route =
path("order" / IntNumber) { id =>
(get | put) { ctx =>
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
}
}
verify(route) // hide
}
"example-5" in {
val getOrPut = get | put
val route =
path("order" / IntNumber) { id =>
getOrPut { ctx =>
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
}
}
verify(route) // hide
}
"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)
}
verify(route) // hide
}
"example-7" in {
val orderGetOrPut = path("order" / IntNumber) & (get | put)
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)
}
verify(route) // hide
}
def verify(route: Route) = {
Get("/order/42") ~> route ~> check { responseAs[String] shouldEqual "Received GET request for order 42" }
Put("/order/42") ~> route ~> check { responseAs[String] shouldEqual "Received PUT request for order 42" }
Get("/") ~> route ~> check { handled shouldEqual false }
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
import akka.actor.ActorSystem
import akka.http.server.Route
import akka.stream.FlowMaterializer
object MyHandler {
//# example-1
import akka.http.model.HttpResponse
import akka.http.model.StatusCodes._
import akka.http.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 = FlowMaterializer()
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!!!"
}
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
import akka.actor.ActorSystem
import akka.stream.FlowMaterializer
import akka.http.server.{ Route, MissingCookieRejection }
import scala.concurrent.ExecutionContext
object MyRejectionHandler {
//# example-1
import akka.http.model._
import akka.http.server._
import StatusCodes._
import Directives._
implicit val myRejectionHandler = RejectionHandler {
case MissingCookieRejection(cookieName) :: _ =>
complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!"))
}
object MyApp {
implicit val system = ActorSystem()
import system.dispatcher
implicit val materializer = FlowMaterializer()
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.coding.Gzip
val route =
path("order") {
get {
complete("Received GET")
} ~
post {
decodeRequest(Gzip) {
complete("Received POST")
}
}
}
}
}

View file

@ -0,0 +1,11 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
import akka.http.server.Directives
import akka.http.testkit.ScalatestRouteTest
import org.scalatest.{ Matchers, WordSpec }
abstract class RoutingSpec extends WordSpec with Matchers with Directives with ScalatestRouteTest

View file

@ -0,0 +1,197 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package directives
import akka.http.model.headers.RawHeader
import akka.http.server.RouteResult.Rejected
import akka.http.server._
import akka.util.ByteString
import scala.util.control.NonFatal
import akka.http.model._
class BasicDirectivesExamplesSpec extends RoutingSpec {
"0extract" in {
val uriLength = extract(_.request.uri.toString.length)
val route =
uriLength { len =>
complete(s"The length of the request URI is $len")
}
Get("/abcdef") ~> route ~> check {
responseAs[String] shouldEqual "The length of the request URI is 25"
}
}
"textract" in {
val pathAndQuery = textract { ctx =>
val uri = ctx.request.uri
(uri.path, uri.query)
}
val route =
pathAndQuery { (p, query) =>
complete(s"The path is $p and the query is $query")
}
Get("/abcdef?ghi=12") ~> route ~> check {
responseAs[String] shouldEqual "The path is /abcdef and the query is ghi=12"
}
}
"tprovide" in {
def provideStringAndLength(value: String) = tprovide((value, value.length))
val route =
provideStringAndLength("test") { (value, len) =>
complete(s"Value is $value and its length is $len")
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "Value is test and its length is 4"
}
}
"0mapResponse" in {
def overwriteResultStatus(response: HttpResponse): HttpResponse =
response.copy(status = StatusCodes.BadGateway)
val route = mapResponse(overwriteResultStatus)(complete("abc"))
Get("/abcdef?ghi=12") ~> route ~> check {
status shouldEqual StatusCodes.BadGateway
}
}
"mapResponseEntity" in {
def prefixEntity(entity: ResponseEntity): ResponseEntity = entity match {
case HttpEntity.Strict(contentType, data) =>
HttpEntity.Strict(contentType, ByteString("test") ++ data)
case _ => throw new IllegalStateException("Unexpected entity type")
}
val prefixWithTest: Directive0 = mapResponseEntity(prefixEntity)
val route = prefixWithTest(complete("abc"))
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "testabc"
}
}
"mapResponseHeaders" in {
// adds all request headers to the response
val echoRequestHeaders = extract(_.request.headers).flatMap(respondWithHeaders)
val removeIdHeader = mapResponseHeaders(_.filterNot(_.lowercaseName == "id"))
val route =
removeIdHeader {
echoRequestHeaders {
complete("test")
}
}
Get("/") ~> RawHeader("id", "12345") ~> RawHeader("id2", "67890") ~> route ~> check {
header("id") shouldEqual None
header("id2").get.value shouldEqual "67890"
}
}
"mapInnerRoute" in {
val completeWithInnerException =
mapInnerRoute { route =>
ctx =>
try {
route(ctx)
} catch {
case NonFatal(e) => ctx.complete(s"Got ${e.getClass.getSimpleName} '${e.getMessage}'")
}
}
val route =
completeWithInnerException {
complete(throw new IllegalArgumentException("BLIP! BLOP! Everything broke"))
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "Got IllegalArgumentException 'BLIP! BLOP! Everything broke'"
}
}
"mapRejections" in {
// ignore any rejections and replace them by AuthorizationFailedRejection
val replaceByAuthorizationFailed = mapRejections(_ => List(AuthorizationFailedRejection))
val route =
replaceByAuthorizationFailed {
path("abc")(complete("abc"))
}
Get("/") ~> route ~> check {
rejection shouldEqual AuthorizationFailedRejection
}
}
"0mapRequest" in {
def transformToPostRequest(req: HttpRequest): HttpRequest = req.copy(method = HttpMethods.POST)
val route =
mapRequest(transformToPostRequest) {
extractRequest { req =>
complete(s"The request method was ${req.method.name}")
}
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "The request method was POST"
}
}
"mapRequestContext" in {
val replaceRequest =
mapRequestContext(_.withRequest(HttpRequest(HttpMethods.POST)))
val route =
replaceRequest {
extractRequest { req =>
complete(req.method.value)
}
}
Get("/abc/def/ghi") ~> route ~> check {
responseAs[String] shouldEqual "POST"
}
}
"0mapRouteResponse" in {
val rejectAll = // not particularly useful directive
mapRouteResult {
case _ => Rejected(List(AuthorizationFailedRejection))
}
val route =
rejectAll {
complete("abc")
}
Get("/") ~> route ~> check {
rejections.nonEmpty shouldEqual true
}
}
"mapRouteResponsePF" in {
case object MyCustomRejection extends Rejection
val rejectRejections = // not particularly useful directive
mapRouteResultPF {
case Rejected(_) => Rejected(List(AuthorizationFailedRejection))
}
val route =
rejectRejections {
reject(MyCustomRejection)
}
Get("/") ~> route ~> check {
rejection shouldEqual AuthorizationFailedRejection
}
}
"pass" in {
Get("/") ~> pass(complete("abc")) ~> check {
responseAs[String] shouldEqual "abc"
}
}
"0provide" in {
def providePrefixedString(value: String): Directive1[String] = provide("prefix:" + value)
val route =
providePrefixedString("test") { value =>
complete(value)
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "prefix:test"
}
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package directives
import akka.http.coding._
import akka.http.model.{ HttpResponse, StatusCodes }
import akka.http.model.headers.{ HttpEncodings, HttpEncoding, `Accept-Encoding`, `Content-Encoding` }
import akka.http.model.headers.HttpEncodings._
import akka.http.server._
import akka.util.ByteString
import org.scalatest.matchers.Matcher
class CodingDirectivesExamplesSpec extends RoutingSpec {
"compressResponse-0" in {
val route = compressResponse() { complete("content") }
Get("/") ~> route ~> check {
response should haveContentEncoding(gzip)
}
Get("/") ~> `Accept-Encoding`(gzip, deflate) ~> route ~> check {
response should haveContentEncoding(gzip)
}
Get("/") ~> `Accept-Encoding`(deflate) ~> route ~> check {
response should haveContentEncoding(deflate)
}
Get("/") ~> `Accept-Encoding`(identity) ~> route ~> check {
status shouldEqual StatusCodes.OK
response should haveContentEncoding(identity)
responseAs[String] shouldEqual "content"
}
}
"compressResponse-1" in {
val route = compressResponse(Gzip) { complete("content") }
Get("/") ~> route ~> check {
response should haveContentEncoding(gzip)
}
Get("/") ~> `Accept-Encoding`(gzip, deflate) ~> route ~> check {
response should haveContentEncoding(gzip)
}
Get("/") ~> `Accept-Encoding`(deflate) ~> route ~> check {
rejection shouldEqual UnacceptedResponseEncodingRejection(gzip)
}
Get("/") ~> `Accept-Encoding`(identity) ~> route ~> check {
rejection shouldEqual UnacceptedResponseEncodingRejection(gzip)
}
}
"compressResponseIfRequested" in {
val route = compressResponseIfRequested() { complete("content") }
Get("/") ~> route ~> check {
response should haveContentEncoding(identity)
}
Get("/") ~> `Accept-Encoding`(gzip, deflate) ~> route ~> check {
response should haveContentEncoding(gzip)
}
Get("/") ~> `Accept-Encoding`(deflate) ~> route ~> check {
response should haveContentEncoding(deflate)
}
Get("/") ~> `Accept-Encoding`(identity) ~> route ~> check {
response should haveContentEncoding(identity)
}
}
"encodeResponse" in {
val route = encodeResponse(Gzip) { complete("content") }
Get("/") ~> route ~> check {
response should haveContentEncoding(gzip)
}
Get("/") ~> `Accept-Encoding`(gzip, deflate) ~> route ~> check {
response should haveContentEncoding(gzip)
}
Get("/") ~> `Accept-Encoding`(deflate) ~> route ~> check {
rejection shouldEqual UnacceptedResponseEncodingRejection(gzip)
}
Get("/") ~> `Accept-Encoding`(identity) ~> route ~> check {
rejection shouldEqual UnacceptedResponseEncodingRejection(gzip)
}
}
val helloGzipped = compress("Hello", Gzip)
val helloDeflated = compress("Hello", Deflate)
"decodeRequest" in {
val route =
decodeRequest(Gzip) {
entity(as[String]) { content: String =>
complete(s"Request content: '$content'")
}
}
Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> route ~> check {
responseAs[String] shouldEqual "Request content: 'Hello'"
}
Post("/", helloDeflated) ~> `Content-Encoding`(deflate) ~> route ~> check {
rejection shouldEqual UnsupportedRequestEncodingRejection(gzip)
}
Post("/", "hello") ~> `Content-Encoding`(identity) ~> route ~> check {
rejection shouldEqual UnsupportedRequestEncodingRejection(gzip)
}
}
"decompressRequest-0" in {
val route =
decompressRequest() {
entity(as[String]) { content: String =>
complete(s"Request content: '$content'")
}
}
Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> route ~> check {
responseAs[String] shouldEqual "Request content: 'Hello'"
}
Post("/", helloDeflated) ~> `Content-Encoding`(deflate) ~> route ~> check {
responseAs[String] shouldEqual "Request content: 'Hello'"
}
Post("/", "hello uncompressed") ~> `Content-Encoding`(identity) ~> route ~> check {
responseAs[String] shouldEqual "Request content: 'hello uncompressed'"
}
}
"decompressRequest-1" in {
val route =
decompressRequest(Gzip, NoCoding) {
entity(as[String]) { content: String =>
complete(s"Request content: '$content'")
}
}
Post("/", helloGzipped) ~> `Content-Encoding`(gzip) ~> route ~> check {
responseAs[String] shouldEqual "Request content: 'Hello'"
}
Post("/", helloDeflated) ~> `Content-Encoding`(deflate) ~> route ~> check {
rejections shouldEqual List(UnsupportedRequestEncodingRejection(gzip), UnsupportedRequestEncodingRejection(identity))
}
Post("/", "hello uncompressed") ~> `Content-Encoding`(identity) ~> route ~> check {
responseAs[String] shouldEqual "Request content: 'hello uncompressed'"
}
}
def haveContentEncoding(encoding: HttpEncoding): Matcher[HttpResponse] =
be(encoding) compose { (_: HttpResponse).header[`Content-Encoding`].map(_.encodings.head).getOrElse(HttpEncodings.identity) }
def compress(input: String, encoder: Encoder): ByteString = encoder.encode(ByteString(input))
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package directives
import akka.http.server._
import akka.http.model.headers.{ HttpCookie, Cookie, `Set-Cookie` }
import akka.http.util.DateTime
class CookieDirectivesExamplesSpec extends RoutingSpec {
"cookie" in {
val route =
cookie("userName") { nameCookie =>
complete(s"The logged in user is '${nameCookie.content}'")
}
Get("/") ~> Cookie(HttpCookie("userName", "paul")) ~> route ~> check {
responseAs[String] shouldEqual "The logged in user is 'paul'"
}
// missing cookie
Get("/") ~> route ~> check {
rejection shouldEqual MissingCookieRejection("userName")
}
Get("/") ~> Route.seal(route) ~> check {
responseAs[String] shouldEqual "Request is missing required cookie 'userName'"
}
}
"optionalCookie" in {
val route =
optionalCookie("userName") {
case Some(nameCookie) => complete(s"The logged in user is '${nameCookie.content}'")
case None => complete("No user logged in")
}
Get("/") ~> Cookie(HttpCookie("userName", "paul")) ~> route ~> check {
responseAs[String] shouldEqual "The logged in user is 'paul'"
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "No user logged in"
}
}
"deleteCookie" in {
val route =
deleteCookie("userName") {
complete("The user was logged out")
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "The user was logged out"
header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", content = "deleted", expires = Some(DateTime.MinValue))))
}
}
"setCookie" in {
val route =
setCookie(HttpCookie("userName", content = "paul")) {
complete("The user was logged in")
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "The user was logged in"
header[`Set-Cookie`] shouldEqual Some(`Set-Cookie`(HttpCookie("userName", content = "paul")))
}
}
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package directives
import akka.http.model.{ HttpResponse, HttpRequest }
import akka.http.server._
import akka.event.Logging
import akka.http.server.directives.{ LoggingMagnet, LogEntry, DebuggingDirectives }
class DebuggingDirectivesExamplesSpec extends RoutingSpec {
"logRequest-0" in {
// different possibilities of using logRequest
// The first alternatives use an implicitly available LoggingContext for logging
// marks with "get-user", log with debug level, HttpRequest.toString
DebuggingDirectives.logRequest("get-user")
// marks with "get-user", log with info level, HttpRequest.toString
DebuggingDirectives.logRequest("get-user", Logging.InfoLevel)
// logs just the request method at debug level
def requestMethod(req: HttpRequest): String = req.method.toString
DebuggingDirectives.logRequest(requestMethod _)
// logs just the request method at info level
def requestMethodAsInfo(req: HttpRequest): LogEntry = LogEntry(req.method.toString, Logging.InfoLevel)
DebuggingDirectives.logRequest(requestMethodAsInfo _)
// This one doesn't use the implicit LoggingContext but uses `println` for logging
def printRequestMethod(req: HttpRequest): Unit = println(req.method)
val logRequestPrintln = DebuggingDirectives.logRequest(LoggingMagnet(_ => printRequestMethod))
Get("/") ~> logRequestPrintln(complete("logged")) ~> check {
responseAs[String] shouldEqual "logged"
}
}
"logRequestResult" in {
// different possibilities of using logRequestResponse
// The first alternatives use an implicitly available LoggingContext for logging
// marks with "get-user", log with debug level, HttpRequest.toString, HttpResponse.toString
DebuggingDirectives.logRequestResult("get-user")
// marks with "get-user", log with info level, HttpRequest.toString, HttpResponse.toString
DebuggingDirectives.logRequestResult("get-user", Logging.InfoLevel)
// logs just the request method and response status at info level
def requestMethodAndResponseStatusAsInfo(req: HttpRequest): Any => Option[LogEntry] = {
case res: HttpResponse => Some(LogEntry(req.method + ":" + res.status, Logging.InfoLevel))
case _ => None // other kind of responses
}
DebuggingDirectives.logRequestResult(requestMethodAndResponseStatusAsInfo _)
// This one doesn't use the implicit LoggingContext but uses `println` for logging
def printRequestMethodAndResponseStatus(req: HttpRequest)(res: Any): Unit =
println(requestMethodAndResponseStatusAsInfo(req)(res).map(_.obj.toString).getOrElse(""))
val logRequestResultPrintln = DebuggingDirectives.logRequestResult(LoggingMagnet(_ => printRequestMethodAndResponseStatus))
Get("/") ~> logRequestResultPrintln(complete("logged")) ~> check {
responseAs[String] shouldEqual "logged"
}
}
"logResult" in {
// different possibilities of using logResponse
// The first alternatives use an implicitly available LoggingContext for logging
// marks with "get-user", log with debug level, HttpResponse.toString
DebuggingDirectives.logResult("get-user")
// marks with "get-user", log with info level, HttpResponse.toString
DebuggingDirectives.logResult("get-user", Logging.InfoLevel)
// logs just the response status at debug level
def responseStatus(res: Any): String = res match {
case x: HttpResponse => x.status.toString
case _ => "unknown response part"
}
DebuggingDirectives.logResult(responseStatus _)
// logs just the response status at info level
def responseStatusAsInfo(res: Any): LogEntry = LogEntry(responseStatus(res), Logging.InfoLevel)
DebuggingDirectives.logResult(responseStatusAsInfo _)
// This one doesn't use the implicit LoggingContext but uses `println` for logging
def printResponseStatus(res: Any): Unit = println(responseStatus(res))
val logResultPrintln = DebuggingDirectives.logResult(LoggingMagnet(_ => printResponseStatus))
Get("/") ~> logResultPrintln(complete("logged")) ~> check {
responseAs[String] shouldEqual "logged"
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package directives
import akka.http.model.StatusCodes
import akka.http.server._
class ExecutionDirectivesExamplesSpec extends RoutingSpec {
"handleExceptions" in {
val divByZeroHandler = ExceptionHandler {
case _: ArithmeticException => complete(StatusCodes.BadRequest, "You've got your arithmetic wrong, fool!")
}
val route =
path("divide" / IntNumber / IntNumber) { (a, b) =>
handleExceptions(divByZeroHandler) {
complete(s"The result is ${a / b}")
}
}
Get("/divide/10/5") ~> route ~> check {
responseAs[String] shouldEqual "The result is 2"
}
Get("/divide/10/0") ~> route ~> check {
status shouldEqual StatusCodes.BadRequest
responseAs[String] shouldEqual "You've got your arithmetic wrong, fool!"
}
}
"handleRejections" in {
val totallyMissingHandler = RejectionHandler {
case Nil /* secret code for path not found */ =>
complete(StatusCodes.NotFound, "Oh man, what you are looking for is long gone.")
}
val route =
pathPrefix("handled") {
handleRejections(totallyMissingHandler) {
path("existing")(complete("This path exists"))
}
}
Get("/handled/existing") ~> route ~> check {
responseAs[String] shouldEqual "This path exists"
}
Get("/missing") ~> Route.seal(route) /* applies default handler */ ~> check {
status shouldEqual StatusCodes.NotFound
responseAs[String] shouldEqual "The requested resource could not be found."
}
Get("/handled/missing") ~> route ~> check {
status shouldEqual StatusCodes.NotFound
responseAs[String] shouldEqual "Oh man, what you are looking for is long gone."
}
}
}

View file

@ -0,0 +1,271 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package directives
import akka.http.server._
class PathDirectivesExamplesSpec extends RoutingSpec {
//# path-matcher
val matcher: PathMatcher1[Option[Int]] =
"foo" / "bar" / "X" ~ IntNumber.? / ("edit" | "create")
//#
//# path-dsl
// matches /foo/
path("foo" /)
// matches e.g. /foo/123 and extracts "123" as a String
path("foo" / """\d+""".r)
// matches e.g. /foo/bar123 and extracts "123" as a String
path("foo" / """bar(\d+)""".r)
// similar to `path(Segments)`
path(Segment.repeat(10, separator = Slash))
// matches e.g. /i42 or /hCAFE and extracts an Int
path("i" ~ IntNumber | "h" ~ HexIntNumber)
// identical to path("foo" ~ (PathEnd | Slash))
path("foo" ~ Slash.?)
// matches /red or /green or /blue and extracts 1, 2 or 3 respectively
path(Map("red" -> 1, "green" -> 2, "blue" -> 3))
// matches anything starting with "/foo" except for /foobar
pathPrefix("foo" ~ !"bar")
//#
//# pathPrefixTest-, rawPathPrefix-, rawPathPrefixTest-, pathSuffix-, pathSuffixTest-
val completeWithUnmatchedPath =
extractUnmatchedPath { p =>
complete(p.toString)
}
//#
"path-example" in {
val route =
path("foo") {
complete("/foo")
} ~
path("foo" / "bar") {
complete("/foo/bar")
} ~
pathPrefix("ball") {
pathEnd {
complete("/ball")
} ~
path(IntNumber) { int =>
complete(if (int % 2 == 0) "even ball" else "odd ball")
}
}
Get("/") ~> route ~> check {
handled shouldEqual false
}
Get("/foo") ~> route ~> check {
responseAs[String] shouldEqual "/foo"
}
Get("/foo/bar") ~> route ~> check {
responseAs[String] shouldEqual "/foo/bar"
}
Get("/ball/1337") ~> route ~> check {
responseAs[String] shouldEqual "odd ball"
}
}
"pathEnd-" in {
val route =
pathPrefix("foo") {
pathEnd {
complete("/foo")
} ~
path("bar") {
complete("/foo/bar")
}
}
Get("/foo") ~> route ~> check {
responseAs[String] shouldEqual "/foo"
}
Get("/foo/") ~> route ~> check {
handled shouldEqual false
}
Get("/foo/bar") ~> route ~> check {
responseAs[String] shouldEqual "/foo/bar"
}
}
"pathEndOrSingleSlash-" in {
val route =
pathPrefix("foo") {
pathEndOrSingleSlash {
complete("/foo")
} ~
path("bar") {
complete("/foo/bar")
}
}
Get("/foo") ~> route ~> check {
responseAs[String] shouldEqual "/foo"
}
Get("/foo/") ~> route ~> check {
responseAs[String] shouldEqual "/foo"
}
Get("/foo/bar") ~> route ~> check {
responseAs[String] shouldEqual "/foo/bar"
}
}
"pathPrefix-" in {
val route =
pathPrefix("ball") {
pathEnd {
complete("/ball")
} ~
path(IntNumber) { int =>
complete(if (int % 2 == 0) "even ball" else "odd ball")
}
}
Get("/") ~> route ~> check {
handled shouldEqual false
}
Get("/ball") ~> route ~> check {
responseAs[String] shouldEqual "/ball"
}
Get("/ball/1337") ~> route ~> check {
responseAs[String] shouldEqual "odd ball"
}
}
"pathPrefixTest-" in {
val route =
pathPrefixTest("foo" | "bar") {
pathPrefix("foo") { completeWithUnmatchedPath } ~
pathPrefix("bar") { completeWithUnmatchedPath }
}
Get("/foo/doo") ~> route ~> check {
responseAs[String] shouldEqual "/doo"
}
Get("/bar/yes") ~> route ~> check {
responseAs[String] shouldEqual "/yes"
}
}
"pathSingleSlash-" in {
val route =
pathSingleSlash {
complete("root")
} ~
pathPrefix("ball") {
pathSingleSlash {
complete("/ball/")
} ~
path(IntNumber) { int =>
complete(if (int % 2 == 0) "even ball" else "odd ball")
}
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "root"
}
Get("/ball") ~> route ~> check {
handled shouldEqual false
}
Get("/ball/") ~> route ~> check {
responseAs[String] shouldEqual "/ball/"
}
Get("/ball/1337") ~> route ~> check {
responseAs[String] shouldEqual "odd ball"
}
}
"pathSuffix-" in {
val route =
pathPrefix("start") {
pathSuffix("end") {
completeWithUnmatchedPath
} ~
pathSuffix("foo" / "bar" ~ "baz") {
completeWithUnmatchedPath
}
}
Get("/start/middle/end") ~> route ~> check {
responseAs[String] shouldEqual "/middle/"
}
Get("/start/something/barbaz/foo") ~> route ~> check {
responseAs[String] shouldEqual "/something/"
}
}
"pathSuffixTest-" in {
val route =
pathSuffixTest(Slash) {
complete("slashed")
} ~
complete("unslashed")
Get("/foo/") ~> route ~> check {
responseAs[String] shouldEqual "slashed"
}
Get("/foo") ~> route ~> check {
responseAs[String] shouldEqual "unslashed"
}
}
"rawPathPrefix-" in {
val route =
pathPrefix("foo") {
rawPathPrefix("bar") { completeWithUnmatchedPath } ~
rawPathPrefix("doo") { completeWithUnmatchedPath }
}
Get("/foobar/baz") ~> route ~> check {
responseAs[String] shouldEqual "/baz"
}
Get("/foodoo/baz") ~> route ~> check {
responseAs[String] shouldEqual "/baz"
}
}
"rawPathPrefixTest-" in {
val route =
pathPrefix("foo") {
rawPathPrefixTest("bar") {
completeWithUnmatchedPath
}
}
Get("/foobar") ~> route ~> check {
responseAs[String] shouldEqual "bar"
}
Get("/foobaz") ~> route ~> check {
handled shouldEqual false
}
}
}

View file

@ -1,4 +1,6 @@
HTTPS HTTPS
===== =====
Is not yet supported.
(todo) (todo)

View file

@ -108,6 +108,9 @@ HttpEntity.CloseDelimited
A streaming entity of unspecified length that is delimited by closing the connection ("Connection: close"). Note, 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``. 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 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. requests and responses. In contrast, ``HttpEntity.CloseDelimited`` can only be used for responses.
@ -130,7 +133,9 @@ which allows access to the data of an entity regardless of its concrete subtype.
- Use Strict if the amount of data is small and it is already in the heap (or even available as a ``ByteString``) - 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 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 Chunked to support a data stream of unknown length
- Use CloseDelimited as an alternative to Chunked e.g. if chunked transfer encoding isn't supported by a peer. - 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 Header model
------------ ------------

View file

@ -8,11 +8,9 @@ HTTP/1.1 server implemented on top of `Akka Streams`_. (todo: fix link)
It sports the following features: It sports the following features:
- Low per-connection overhead for supporting many thousand concurrent connections
- Efficient message parsing and processing logic for high throughput applications
- Full support for `HTTP persistent connections`_ - Full support for `HTTP persistent connections`_
- Full support for `HTTP pipelining`_ - Full support for `HTTP pipelining`_
- Full support for asynchronous HTTP streaming (including "chunked" transfer encoding) accessible through an idiomatic - Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic
reactive streams API reactive streams API
- Optional SSL/TLS encryption - Optional SSL/TLS encryption
@ -27,7 +25,7 @@ Design Philosophy
Akka HTTP server is scoped with a clear focus on the essential functionality of an HTTP/1.1 server: Akka HTTP server is scoped with a clear focus on the essential functionality of an HTTP/1.1 server:
- Connection management - Connection management
- Message parsing and header separation - Parsing messages and headers
- Timeout management (for requests and connections) - Timeout management (for requests and connections)
- Response ordering (for transparent pipelining support) - Response ordering (for transparent pipelining support)
@ -44,8 +42,8 @@ Akka HTTP server is implemented on top of Akka streams and makes heavy use of th
implementation and also on all levels of its API. implementation and also on all levels of its API.
On the connection level Akka HTTP supports basically the same interface as Akka streams IO: A socket binding is On the connection level Akka HTTP supports basically the same interface as Akka streams IO: A socket binding is
represented as a stream of incoming connections. Each connection itself is composed of an input stream of requests and represented as a stream of incoming connections. The application needs to provide a ``Flow[HttpRequest, HttpResponse]``
an output consumer of responses. The application has to provide the handler to "translate" requests into responses. to "translate" requests into responses.
Streaming is also supported for single message entities itself. Particular kinds of ``HttpEntity`` Streaming is also supported for single message entities itself. Particular kinds of ``HttpEntity``
subclasses provide support for fixed or streamed message entities. subclasses provide support for fixed or streamed message entities.
@ -54,26 +52,27 @@ subclasses provide support for fixed or streamed message entities.
Starting and Stopping Starting and Stopping
--------------------- ---------------------
An Akka HTTP server is started by sending an ``Http.Bind`` command to the `akka.http.Http`_ extension: An Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.Http`_ extension:
.. includecode:: ../code/docs/http/HttpServerExampleSpec.scala .. includecode:: ../code/docs/http/HttpServerExampleSpec.scala
:include: bind-example :include: bind-example
With the ``Http.Bind`` command you specify the interface and port to bind to and register interest in handling incoming Arguments to the ``Http.bind`` method specify the interface and port to bind to and register interest in handling incoming
HTTP connections. Additionally the ``Http.Bind`` command also allows you to define socket options as well as a larger number HTTP connections. Additionally, the method also allows you to define socket options as well as a larger number
of settings for configuring the server according to your needs. of settings for configuring the server according to your needs.
The sender of the ``Http.Bind`` command (e.g. an actor you have written) will receive an ``Http.ServerBinding`` reply The result of the ``bind`` method is a ``Http.ServerBinding`` which is immediately returned. The ``ServerBinding.connections``
after the HTTP layer has successfully started the server at the respective endpoint. In case the bind fails (e.g. returns a ``Source[IncomingConnection]`` which is used to handle incoming connections. The actual binding is only done when this
because the port is already busy) a ``Failure`` message is dispatched instead. As shown in the above example this works source is materialized as part of a bigger processing pipeline. In case the bind fails (e.g. because the port is already
well with the ask pattern and Future operations. busy) the error will be reported by flagging an error on the ``IncomingConnection`` stream.
The ``Http.ServerBinding`` informs the binder of the actual local address of the bound socket and it contains a After materialization ``ServerBinding.localAddress`` returns the actual local address of the bound socket.
stream of incoming connections of type ``Producer[Http.IncomingConnection]``. Connections are handled by subscribing
to the connection stream and handling the incoming connections.
The binding is released and the underlying listening socket is closed when all subscribers of the Connections are handled by materializing a pipeline which uses the ``Source[IncomingConnection]`` returned by
``Http.ServerBinding.connectionStream`` have cancelled their subscriptions. ``ServerBinding.connections``. The binding is released and the underlying listening socket is closed when all
subscribers of the ``connections`` source have cancelled their subscription.
(todo: explain even lower level serverFlowToTransport API)
.. _akka.http.Http: @github@/akka-http-core/src/main/scala/akka/http/Http.scala .. _akka.http.Http: @github@/akka-http-core/src/main/scala/akka/http/Http.scala
@ -81,23 +80,28 @@ The binding is released and the underlying listening socket is closed when all s
Request-Response Cycle Request-Response Cycle
---------------------- ----------------------
When a new connection has been accepted it will be published by the ``Http.ServerBinding.connectionStream`` as an When a new connection has been accepted it will be published by by the ``ServerBinding.connections`` source as an
``Http.IncomingConnection`` which consists of the remote address, a ``requestProducer``, and a ``responseConsumer``. ``Http.IncomingConnection`` which consists of the remote address, and methods to provide a ``Flow[HttpRequest, HttpResponse]``
to handle requests coming in over this connection.
Handling requests in this model means connecting the ``requestProducer`` stream with an application-defined component that Requests are handled by calling one of the ``IncomingConnection.handleWithX`` methods with a handler, which can either be
maps requests to responses which then feeds into the ``responseConsumer``:
- a ``Flow[HttpRequest, HttpResponse]`` for ``handleWith``,
- a function ``HttpRequest => HttpResponse`` for ``handleWithSyncHandler``,
- or a function ``HttpRequest => Future[HttpResponse]`` for ``handleWithAsyncHandler``.
.. includecode:: ../code/docs/http/HttpServerExampleSpec.scala .. includecode:: ../code/docs/http/HttpServerExampleSpec.scala
:include: full-server-example :include: full-server-example
In this case, a request is handled by transforming the request stream with a function ``HttpRequest => HttpResponse`` In this case, a request is handled by transforming the request stream with a function ``HttpRequest => HttpResponse``
using Akka stream's ``map`` operator. Depending on the use case, arbitrary other ways of connecting are conceivable using using ``handleWithSyncHandler`` (or equivalently, Akka stream's ``map`` operator). Depending on the use case, arbitrary
Akka stream's operators (e.g using ``mapFuture`` to allow parallel processing of several requests when HTTP pipelining is other ways of connecting are conceivable using Akka stream's combinators.
enabled).
It's the application's responsibility to feed responses into the ``responseConsumer`` in the same order as the respective If the application provides a ``Flow``, it is also the responsibility of the application to generate exactly one response
requests have come in. Also, each request must result in exactly one response. Using stream operators like ``map`` or for every request and that the ordering of responses matches the ordering of the associated requests (which is relevant
``mapFuture`` will automatically fulfill this requirement. if HTTP pipelining is enabled where processing of multiple incoming requests may overlap). Using ``handleWithSyncHandler``
or ``handleWithAsyncHandler`` or, instead, if using stream operators like ``map`` or ``mapFuture`` this requirement
will automatically be fulfilled.
Streaming request/response entities Streaming request/response entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -112,8 +116,8 @@ a description of the alternatives.
Closing a connection Closing a connection
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
The HTTP connection will be closed when the ``responseConsumer`` gets completed or when the ``requestProducer``'s The HTTP connection will be closed when the handling ``Flow`` cancel its upstream subscription or the peer closes the
subscription was cancelled and no more responses are pending. connection.
You can also use the value of the ``Connection`` header of a response as described below to give a hint to the You can also use the value of the ``Connection`` header of a response as described below to give a hint to the
implementation to close the connection after the completion of the response. implementation to close the connection after the completion of the response.
@ -139,7 +143,7 @@ field of an HTTP message:
entities don't need to define a length. entities don't need to define a length.
- ``Server``: The ``Server`` header is usually added automatically and it's value can be configured. An application can - ``Server``: The ``Server`` header is usually added automatically and it's value can be configured. An application can
decide to provide a custom ``Server`` header by including an explicit instance in the response. decide to provide a custom ``Server`` header by including an explicit instance in the response.
- ``Date``: The ``Date`` header is added automatically and will be ignored if supplied manually. - ``Date``: The ``Date`` header is added automatically and can be overridden by supplying it manually.
- ``Connection``: When sending out responses the connection actor watches for a ``Connection`` header set by the - ``Connection``: When sending out responses the connection actor watches for a ``Connection`` header set by the
application and acts accordingly, i.e. you can force the connection actor to close the connection after having sent application and acts accordingly, i.e. you can force the connection actor to close the connection after having sent
the response by including a ``Connection("close")`` header. To unconditionally force a connection keep-alive you can the response by including a ``Connection("close")`` header. To unconditionally force a connection keep-alive you can

View file

@ -1,4 +0,0 @@
Custom Directives
=================
(todo)

View file

@ -0,0 +1,138 @@
.. _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`.

View file

@ -0,0 +1,161 @@
.. _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.
====================================== =================================================================================

View file

@ -0,0 +1,27 @@
.. _-extract-:
extract
=======
Calculates a value from the request context and provides the value to the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: extract
Description
-----------
The ``extract`` directive is used as a building block for :ref:`Custom Directives` to extract data from the
``RequestContext`` and provide it to the inner route. It is a special case for extracting one value of the more
general :ref:`-textract-` directive that can be used to extract more than one value.
See :ref:`ProvideDirectives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extract

View file

@ -0,0 +1,80 @@
.. _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

View file

@ -0,0 +1,24 @@
.. _-mapInnerRoute-:
mapInnerRoute
=============
Changes the execution model of the inner route by wrapping it with arbitrary logic.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: mapInnerRoute
Description
-----------
The ``mapInnerRoute`` directive is used as a building block for :ref:`Custom Directives` to replace the inner route
with any other route. Usually, the returned route wraps the original one with custom execution logic.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapInnerRoute

View file

@ -0,0 +1,26 @@
.. _-mapRejections-:
mapRejections
=============
Transforms the list of rejections the inner route produced.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: mapRejections
Description
-----------
The ``mapRejections`` directive is used as a building block for :ref:`Custom Directives` to transform a list
of rejections from the inner route to a new list of rejections.
See :ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapRejections

View file

@ -0,0 +1,28 @@
.. _-mapRequest-:
mapRequest
==========
Transforms the request before it is handled by the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: mapRequest
Description
-----------
The ``mapRequest`` directive is used as a building block for :ref:`Custom Directives` to transform a request before it
is handled by the inner route. Changing the ``request.uri`` parameter has no effect on path matching in the inner route
because the unmatched path is a separate field of the ``RequestContext`` value which is passed into routes. To change
the unmatched path or other fields of the ``RequestContext`` use the :ref:`-mapRequestContext-` directive.
See :ref:`Request Transforming Directives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapRequest

View file

@ -0,0 +1,27 @@
.. _-mapRequestContext-:
mapRequestContext
=================
Transforms the ``RequestContext`` before it is passed to the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: mapRequestContext
Description
-----------
The ``mapRequestContext`` directive is used as a building block for :ref:`Custom Directives` to transform
the request context before it is passed to the inner route. To change only the request value itself the
:ref:`-mapRequest-` directive can be used instead.
See :ref:`Request Transforming Directives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapRequestContext

View file

@ -0,0 +1,27 @@
.. _-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

View file

@ -0,0 +1,26 @@
.. _-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
:snippet: mapResponseEntity
Description
-----------
The ``mapResponseEntity`` directive is used as a building block for :ref:`Custom Directives` to transform a
response entity that was generated by the inner route.
See :ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapResponseEntity

View file

@ -0,0 +1,26 @@
.. _-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
:snippet: mapResponseHeaders
Description
-----------
The ``mapResponseHeaders`` directive is used as a building block for :ref:`Custom Directives` to transform the list of
response headers that was generated by the inner route.
See :ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapResponseHeaders

View file

@ -0,0 +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
: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`).
See :ref:`Result Transformation Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapRouteResult

View file

@ -0,0 +1,27 @@
.. _-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

View file

@ -0,0 +1,25 @@
.. _-pass-:
pass
====
A directive that passes the request unchanged to its inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: pass
Description
-----------
The directive is usually used as a "neutral element" when combining directives generically.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: pass

View file

@ -0,0 +1,26 @@
.. _-provide-:
provide
=======
Provides a constant value to the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: provide
Description
-----------
The `provide` directive is used as a building block for :ref:`Custom Directives` to provide a single value to the
inner route. To provide several values use the :ref:`-tprovide-` directive.
See :ref:`ProvideDirectives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0provide

View file

@ -0,0 +1,28 @@
.. _-textract-:
textract
========
Calculates a Tuple of values from the request context and provides them to the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: textract
Description
-----------
The ``textract`` directive is used as a building block for :ref:`Custom Directives` to extract data from the
``RequestContext`` and provide it to the inner route. To extract just one value use the :ref:`-extract-` directive. To
provide a constant value independent of the ``RequestContext`` use the :ref:`-tprovide-` directive instead.
See :ref:`ProvideDirectives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: textract

View file

@ -0,0 +1,28 @@
.. _-tprovide-:
tprovide
========
Provides an HList of values to the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: tprovide
Description
-----------
The ``tprovide`` directive is used as a building block for :ref:`Custom Directives` to provide data to the inner route.
To provide just one value use the :ref:`-provide-` directive. If you want to provide values calculated from the
``RequestContext`` use the :ref:`-textract-` directive instead.
See :ref:`ProvideDirectives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: tprovide

View file

@ -0,0 +1,106 @@
Predefined Directives (by trait)
================================
All predefined directives are organized into traits that form one part of the overarching ``Directives`` trait.
.. _Request Directives:
Directives filtering or extracting from the request
---------------------------------------------------
:ref:`MethodDirectives`
Filter and extract based on the request method.
:ref:`HeaderDirectives`
Filter and extract based on request headers.
:ref:`PathDirectives`
Filter and extract from the request URI path.
:ref:`HostDirectives`
Filter and extract based on the target host.
:ref:`ParameterDirectives`, :ref:`FormFieldDirectives`
Filter and extract based on query parameters or form fields.
:ref:`CodingDirectives`
Filter and decode compressed request content.
:ref:`MarshallingDirectives`
Extract the request entity.
:ref:`SchemeDirectives`
Filter and extract based on the request scheme.
:ref:`SecurityDirectives`
Handle authentication data from the request.
:ref:`CookieDirectives`
Filter and extract cookies.
:ref:`BasicDirectives` and :ref:`MiscDirectives`
Directives handling request properties.
.. _Response Directives:
Directives creating or transforming the response
------------------------------------------------
:ref:`CacheConditionDirectives`
Support for conditional requests (``304 Not Modified`` responses).
:ref:`ChunkingDirectives`
Automatically break a response into chunks.
:ref:`CookieDirectives`
Set, modify, or delete cookies.
:ref:`CodingDirectives`
Compress responses.
:ref:`FileAndResourceDirectives`
Deliver responses from files and resources.
:ref:`RangeDirectives`
Support for range requests (``206 Partial Content`` responses).
:ref:`RespondWithDirectives`
Change response properties.
:ref:`RouteDirectives`
Complete or reject a request with a response.
:ref:`BasicDirectives` and :ref:`MiscDirectives`
Directives handling or transforming response properties.
List of predefined directives by trait
--------------------------------------
.. toctree::
:maxdepth: 1
basic-directives/index
cache-condition-directives/index
caching-directives/index
chunking-directives/index
coding-directives/index
cookie-directives/index
debugging-directives/index
execution-directives/index
file-and-resource-directives/index
form-field-directives/index
future-directives/index
header-directives/index
host-directives/index
marshalling-directives/index
method-directives/index
misc-directives/index
parameter-directives/index
path-directives/index
range-directives/index
respond-with-directives/index
route-directives/index
scheme-directives/index
security-directives/index

View file

@ -0,0 +1,46 @@
.. _-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

View file

@ -0,0 +1,37 @@
.. _-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

View file

@ -0,0 +1,30 @@
.. _-decodeRequest-:
decodeRequest
=============
Tries to decode the request with the specified ``Decoder`` or rejects the request with an
``UnacceptedRequestEncodingRejection(supportedEncoding)``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
:snippet: decodeRequest
Description
-----------
The ``decodeRequest`` directive is the building block for the ``decompressRequest`` directive.
``decodeRequest`` and ``decompressRequest`` are related like this::
decompressRequest(Gzip) = decodeRequest(Gzip)
decompressRequest(a, b, c) = decodeRequest(a) | decodeRequest(b) | decodeRequest(c)
decompressRequest() = decodeRequest(Gzip) | decodeRequest(Deflate) | decodeRequest(NoEncoding)
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
:snippet: decodeRequest

View file

@ -0,0 +1,46 @@
.. _-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

View file

@ -0,0 +1,38 @@
.. _-encodeResponse-:
encodeResponse
==============
Tries to encode the response with the specified ``Encoder`` or rejects the request with an
``UnacceptedResponseEncodingRejection(supportedEncodings)``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
:snippet: encodeResponse
Description
-----------
The directive automatically applies the ``autoChunkFileBytes`` directive as well to avoid having to load
an entire file into JVM heap.
The parameter to the directive is either just an ``Encoder`` or all of an ``Encoder``, a threshold, and a
chunk size to configure the automatically applied ``autoChunkFileBytes`` directive.
The ``encodeResponse`` directive is the building block for the ``compressResponse`` and
``compressResponseIfRequested`` directives.
``encodeResponse``, ``compressResponse``, and ``compressResponseIfRequested`` are related like this::
compressResponse(Gzip) = encodeResponse(Gzip)
compressResponse(a, b, c) = encodeResponse(a) | encodeResponse(b) | encodeResponse(c)
compressResponse() = encodeResponse(Gzip) | encodeResponse(Deflate) | encodeResponse(NoEncoding)
compressResponseIfRequested() = encodeResponse(NoEncoding) | encodeResponse(Gzip) | encodeResponse(Deflate)
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
:snippet: encodeResponse

View file

@ -0,0 +1,64 @@
.. _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

View file

@ -0,0 +1,18 @@
.. _-requestEncodedWith-:
requestEncodedWith
==================
Passes the request to the inner route if the request is encoded with the argument encoding. Otherwise,
rejects the request with an ``UnacceptedRequestEncodingRejection(encoding)``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
:snippet: requestEncodedWith
Description
-----------
This directive is the building block for ``decodeRequest`` to reject unsupported encodings.

View file

@ -0,0 +1,18 @@
.. _-responseEncodingAccepted-:
responseEncodingAccepted
========================
Passes the request to the inner route if the request accepts the argument encoding. Otherwise,
rejects the request with an ``UnacceptedResponseEncodingRejection(encoding)``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
:snippet: responseEncodingAccepted
Description
-----------
This directive is the building block for ``encodeResponse`` to reject unsupported encodings.

View file

@ -0,0 +1,25 @@
.. _-cookie-:
cookie
======
Extracts a cookie with a given name from a request or otherwise rejects the request with a ``MissingCookieRejection`` if
the cookie is missing.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala
:snippet: cookie
Description
-----------
Use the :ref:`-optionalCookie-` directive instead if you want to support missing cookies in your inner route.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala
:snippet: cookie

View file

@ -0,0 +1,23 @@
.. _-deleteCookie-:
deleteCookie
============
Adds a header to the response to request the removal of the cookie with the given name on the client.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala
:snippet: deleteCookie
Description
-----------
Use the :ref:`-setCookie-` directive to update a cookie.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala
:snippet: deleteCookie

View file

@ -0,0 +1,12 @@
.. _CookieDirectives:
CookieDirectives
================
.. toctree::
:maxdepth: 1
cookie
deleteCookie
optionalCookie
setCookie

View file

@ -0,0 +1,24 @@
.. _-optionalCookie-:
optionalCookie
==============
Extracts an optional cookie with a given name from a request.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala
:snippet: optionalCookie
Description
-----------
Use the :ref:`-cookie-` directive instead if the inner route does not handle a missing cookie.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala
:snippet: optionalCookie

View file

@ -0,0 +1,24 @@
.. _-setCookie-:
setCookie
=========
Adds a header to the response to request the update of the cookie with the given name on the client.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CookieDirectives.scala
:snippet: setCookie
Description
-----------
Use the :ref:`-deleteCookie-` directive to delete a cookie.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/CookieDirectivesExamplesSpec.scala
:snippet: setCookie

View file

@ -0,0 +1,11 @@
.. _DebuggingDirectives:
DebuggingDirectives
===================
.. toctree::
:maxdepth: 1
logRequest
logRequestResult
logResult

View file

@ -0,0 +1,46 @@
.. _-logRequest-:
logRequest
==========
Logs the request.
Signature
---------
::
def logRequest(marker: String)(implicit log: LoggingContext): Directive0
def logRequest(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0
def logRequest(show: HttpRequest => String)(implicit log: LoggingContext): Directive0
def logRequest(show: HttpRequest => LogEntry)(implicit log: LoggingContext): Directive0
def logRequest(magnet: LoggingMagnet[HttpRequest => Unit])(implicit log: LoggingContext): Directive0
The signature shown is simplified, the real signature uses magnets. [1]_
.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading.
.. _`The Magnet Pattern`: /blog/2012-12-13-the-magnet-pattern/
Description
-----------
Logs the request using the supplied ``LoggingMagnet[HttpRequest => Unit]``. This ``LoggingMagnet`` is a wrapped
function ``HttpRequest => Unit`` that can be implicitly created from the different constructors shown above. These
constructors build a ``LoggingMagnet`` from these components:
* A marker to prefix each log message with.
* A log level.
* A ``show`` function that calculates a string representation for a request.
* An implicit ``LoggingContext`` that is used to emit the log message.
* A function that creates a ``LogEntry`` which is a combination of the elements above.
It is also possible to use any other function ``HttpRequest => Unit`` for logging by wrapping it with ``LoggingMagnet``.
See the examples for ways to use the ``logRequest`` directive.
Use ``logResult`` for logging the response, or ``logRequestResult`` for logging both.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala
:snippet: logRequest-0

View file

@ -0,0 +1,34 @@
.. _-logRequestResult-:
logRequestResult
==================
Logs request and response.
Signature
---------
::
def logRequestResult(marker: String)(implicit log: LoggingContext): Directive0
def logRequestResult(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0
def logRequestResult(show: HttpRequest ⇒ HttpResponsePart ⇒ Option[LogEntry])
(implicit log: LoggingContext): Directive0
def logRequestResult(show: HttpRequest ⇒ Any ⇒ Option[LogEntry])(implicit log: LoggingContext): Directive0
The signature shown is simplified, the real signature uses magnets. [1]_
.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading.
.. _`The Magnet Pattern`: /blog/2012-12-13-the-magnet-pattern/
Description
-----------
This directive is a combination of ``logRequest`` and ``logResult``. See ``logRequest`` for the general description
how these directives work.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala
:snippet: logRequestResult

View file

@ -0,0 +1,38 @@
.. _-logResult-:
logResult
===========
Logs the response.
Signature
---------
::
def logResult(marker: String)(implicit log: LoggingContext): Directive0
def logResult(marker: String, level: LogLevel)(implicit log: LoggingContext): Directive0
def logResult(show: Any => String)(implicit log: LoggingContext): Directive0
def logResult(show: Any => LogEntry)(implicit log: LoggingContext): Directive0
def logResult(magnet: LoggingMagnet[Any => Unit])(implicit log: LoggingContext): Directive0
The signature shown is simplified, the real signature uses magnets. [1]_
.. [1] See `The Magnet Pattern`_ for an explanation of magnet-based overloading.
.. _`The Magnet Pattern`: /blog/2012-12-13-the-magnet-pattern/
Description
-----------
See ``logRequest`` for the general description how these directives work. This directive is different
as it requires a ``LoggingMagnet[Any => Unit]``. Instead of just logging ``HttpResponses``, ``logResult`` is able to
log anything passing through :ref:`The Responder Chain` (which can either be a ``HttpResponsePart`` or a ``Rejected``
message reporting rejections).
Use ``logRequest`` for logging the request, or ``logRequestResult`` for logging both.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/DebuggingDirectivesExamplesSpec.scala
:snippet: logResult

View file

@ -0,0 +1,26 @@
.. _-handleExceptions-:
handleExceptions
================
Catches exceptions thrown by the inner route and handles them using the specified ``ExceptionHandler``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/ExecutionDirectives.scala
:snippet: handleExceptions
Description
-----------
Using this directive is an alternative to using a global implicitly defined ``ExceptionHandler`` that
applies to the complete route.
See :ref:`Exception Handling` for general information about options for handling exceptions.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/ExecutionDirectivesExamplesSpec.scala
:snippet: handleExceptions

View file

@ -0,0 +1,26 @@
.. _-handleRejections-:
handleRejections
================
Handles rejections produced by the inner route and handles them using the specified ``RejectionHandler``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/ExecutionDirectives.scala
:snippet: handleRejections
Description
-----------
Using this directive is an alternative to using a global implicitly defined ``RejectionHandler`` that
applies to the complete route.
See :ref:`Rejections` for general information about options for handling rejections.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/ExecutionDirectivesExamplesSpec.scala
:snippet: handleRejections

View file

@ -0,0 +1,10 @@
.. _ExecutionDirectives:
ExecutionDirectives
===================
.. toctree::
:maxdepth: 1
handleExceptions
handleRejections

View file

@ -0,0 +1,23 @@
.. _-getFromBrowseableDirectories-:
getFromBrowseableDirectories
============================
Serves the content of the given directories as a file system browser, i.e. files are sent and directories
served as browsable listings.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala
:snippet: getFromBrowseableDirectories
Description
-----------
The ``getFromBrowseableDirectories`` is a combination of serving files from the specified directories (like
``getFromDirectory``) and listing a browseable directory with ``listDirectoryContents``. Nesting this directive beneath
``get`` is not necessary as this directive will only respond to ``GET`` requests.
Use ``getFromBrowseableDirectory`` to serve only one directory. Use ``getFromDirectory`` if directory browsing isn't
required.

View file

@ -0,0 +1,13 @@
.. _-getFromBrowseableDirectory-:
getFromBrowseableDirectory
==========================
The single-directory variant of :ref:`-getFromBrowseableDirectories-`.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala
:snippet: getFromBrowseableDirectory

View file

@ -0,0 +1,26 @@
.. _-getFromDirectory-:
getFromDirectory
================
Completes GET requests with the content of a file underneath the given directory.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala
:snippet: getFromDirectory
Description
-----------
The ``unmatchedPath`` of the ``RequestContext`` is first transformed by the given ``pathRewriter`` function before being
appended to the given directory name to build the final file name.
The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread. If the file cannot
be read the route rejects the request.
To serve a single file use ``getFromFile``. To serve browsable directory listings use ``getFromBrowseableDirectories``.
To serve files from a classpath directory use ``getFromResourceDirectory`` instead.
Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests.

View file

@ -0,0 +1,23 @@
.. _-getFromFile-:
getFromFile
===========
Completes GET requests with the content of the given file.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala
:snippet: getFromFile
Description
-----------
The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread (but potentially
some other thread !). If the file cannot be found or read the request is rejected.
To serve files from a directory use ``getFromDirectory``, instead. To serve a file from a classpath resource
use ``getFromResource`` instead.
Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests.

View file

@ -0,0 +1,23 @@
.. _-getFromResource-:
getFromResource
===============
Completes GET requests with the content of the given classpath resource.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala
:snippet: getFromResource
Description
-----------
The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread (but potentially
some other thread !). If the file cannot be found or read the request is rejected.
To serve files from a classpath directory use ``getFromResourceDirectory`` instead. To serve files from a filesystem
directory use ``getFromDirectory``, instead.
Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests.

View file

@ -0,0 +1,23 @@
.. _-getFromResourceDirectory-:
getFromResourceDirectory
========================
Completes GET requests with the content of the given classpath resource directory.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala
:snippet: getFromResourceDirectory
Description
-----------
The actual I/O operation is running detached in a `Future`, so it doesn't block the current thread (but potentially
some other thread !). If the file cannot be found or read the request is rejected.
To serve a single resource use ``getFromResource``, instead. To server files from a filesystem directory use
``getFromDirectory`` instead.
Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests.

View file

@ -0,0 +1,24 @@
.. _FileAndResourceDirectives:
FileAndResourceDirectives
=========================
Like the :ref:`RouteDirectives` the ``FileAndResourceDirectives`` are somewhat special in spray's routing DSL.
Contrary to all other directives they do not produce instances of type ``Directive[L <: HList]`` but rather "plain"
routes of type ``Route``.
The reason is that they are not meant for wrapping an inner route (like most other directives, as intermediate-level
elements of a route structure, do) but rather form the actual route structure **leaves**.
So in most cases the inner-most element of a route structure branch is one of the :ref:`RouteDirectives` or
``FileAndResourceDirectives``.
.. toctree::
:maxdepth: 1
getFromBrowseableDirectories
getFromBrowseableDirectory
getFromDirectory
getFromFile
getFromResource
getFromResourceDirectory
listDirectoryContents

View file

@ -0,0 +1,24 @@
.. _-listDirectoryContents-:
listDirectoryContents
=====================
Completes GET requests with a unified listing of the contents of all given directories. The actual rendering of the
directory contents is performed by the in-scope `Marshaller[DirectoryListing]`.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/FileAndResourceDirectives.scala
:snippet: listDirectoryContents
Description
-----------
The ``listDirectoryContents`` directive renders a response only for directories. To just serve files use
``getFromDirectory``. To serve files and provide a browseable directory listing use ``getFromBrowsableDirectories``
instead.
The rendering can be overridden by providing a custom ``Marshaller[DirectoryListing]``.
Note that it's not required to wrap this directive with ``get`` as this directive will only respond to ``GET`` requests.

View file

@ -0,0 +1,20 @@
.. _PathDirectives:
PathDirectives
==============
.. toctree::
:maxdepth: 1
path
pathEnd
pathEndOrSingleSlash
pathPrefix
pathPrefixTest
pathSingleSlash
pathSuffix
pathSuffixTest
rawPathPrefix
rawPathPrefixTest
../../path-matchers

View file

@ -0,0 +1,41 @@
.. _-path-:
path
====
Matches the complete unmatched path of the ``RequestContext`` against the given ``PathMatcher``, potentially extracts
one or more values (depending on the type of the argument).
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: path
Description
-----------
This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other
potentially existing :ref:`-pathPrefix-` directives on higher levels of the routing structure.
Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`).
As opposed to the :ref:`-rawPathPrefix-` or :ref:`-rawPathPrefixTest-` directives ``path`` automatically adds a leading
slash to its ``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash.
The ``path`` directive attempts to match the **complete** remaining path, not just a prefix. If you only want to match
a path prefix and then delegate further filtering to a lower level in your routing structure use the :ref:`-pathPrefix-`
directive instead. As a consequence it doesn't make sense to nest a ``path`` or :ref:`-pathPrefix-` directive
underneath another ``path`` directive, as there is no way that they will ever match (since the unmatched path underneath
a ``path`` directive will always be empty).
Depending on the type of its ``PathMatcher`` argument the ``path`` directive extracts zero or more values from the URI.
If the match fails the request is rejected with an :ref:`empty rejection set <empty rejections>`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: path-example

View file

@ -0,0 +1,28 @@
.. _-pathEnd-:
pathEnd
=======
Only passes the request to its inner route if the unmatched path of the ``RequestContext`` is empty, i.e. the request
path has been fully matched by a higher-level :ref:`-path-` or :ref:`-pathPrefix-` directive.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: pathEnd
Description
-----------
This directive is a simple alias for ``rawPathPrefix(PathEnd)`` and is mostly used on an
inner-level to discriminate "path already fully matched" from other alternatives (see the example below).
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: pathEnd-

View file

@ -0,0 +1,30 @@
.. _-pathEndOrSingleSlash-:
pathEndOrSingleSlash
====================
Only passes the request to its inner route if the unmatched path of the ``RequestContext`` is either empty
or contains only one single slash.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: pathEndOrSingleSlash
Description
-----------
This directive is a simple alias for ``rawPathPrefix(Slash.? ~ PathEnd)`` and is mostly used on an inner-level to
discriminate "path already fully matched" from other alternatives (see the example below).
It is equivalent to ``pathEnd | pathSingleSlash`` but slightly more efficient.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: pathEndOrSingleSlash-

View file

@ -0,0 +1,35 @@
.. _-pathPrefix-:
pathPrefix
==========
Matches and consumes a prefix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``,
potentially extracts one or more values (depending on the type of the argument).
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: pathPrefix
Description
-----------
This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other
potentially existing ``pathPrefix`` or :ref:`-rawPathPrefix-` directives on higher levels of the routing structure.
Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`).
As opposed to its :ref:`-rawPathPrefix-` counterpart ``pathPrefix`` automatically adds a leading slash to its
``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash.
Depending on the type of its ``PathMatcher`` argument the ``pathPrefix`` directive extracts zero or more values from
the URI. If the match fails the request is rejected with an :ref:`empty rejection set <empty rejections>`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: pathPrefix-

View file

@ -0,0 +1,38 @@
.. _-pathPrefixTest-:
pathPrefixTest
==============
Checks whether the unmatched path of the ``RequestContext`` has a prefix matched by the given ``PathMatcher``.
Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from
the unmatched path.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: pathPrefixTest
Description
-----------
This directive is very similar to the :ref:`-pathPrefix-` directive with the one difference that the path prefix
it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as
is even in the case that the directive successfully matched and the request is passed on to its inner route.
For more info on how to create a ``PathMatcher`` see :ref:`pathmatcher-dsl`.
As opposed to its :ref:`-rawPathPrefixTest-` counterpart ``pathPrefixTest`` automatically adds a leading slash to its
``PathMatcher`` argument, you therefore don't have to start your matching expression with an explicit slash.
Depending on the type of its ``PathMatcher`` argument the ``pathPrefixTest`` directive extracts zero or more values from
the URI. If the match fails the request is rejected with an :ref:`empty rejection set <empty rejections>`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: pathPrefixTest-

View file

@ -0,0 +1,28 @@
.. _-pathSingleSlash-:
pathSingleSlash
===============
Only passes the request to its inner route if the unmatched path of the ``RequestContext``
contains exactly one single slash.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: pathSingleSlash
Description
-----------
This directive is a simple alias for ``pathPrefix(PathEnd)`` and is mostly used for matching requests to the root URI
(``/``) on an inner-level to discriminate "all path segments matched" from other alternatives (see the example below).
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: pathSingleSlash-

View file

@ -0,0 +1,38 @@
.. _-pathSuffix-:
pathSuffix
==========
Matches and consumes a suffix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``,
potentially extracts one or more values (depending on the type of the argument).
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: pathSuffix
Description
-----------
This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other
potentially existing path matching directives on higher levels of the routing structure.
Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`).
As opposed to :ref:`-pathPrefix-` this directive matches and consumes the unmatched path from the right, i.e. the end.
.. caution:: For efficiency reasons, the given ``PathMatcher`` must match the desired suffix in reversed-segment
order, i.e. ``pathSuffix("baz" / "bar")`` would match ``/foo/bar/baz``! The order within a segment match is
not reversed.
Depending on the type of its ``PathMatcher`` argument the ``pathPrefix`` directive extracts zero or more values from
the URI. If the match fails the request is rejected with an :ref:`empty rejection set <empty rejections>`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: pathSuffix-

View file

@ -0,0 +1,39 @@
.. _-pathSuffixTest-:
pathSuffixTest
==============
Checks whether the unmatched path of the ``RequestContext`` has a suffix matched by the given ``PathMatcher``.
Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from
the unmatched path.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: pathSuffixTest
Description
-----------
This directive is very similar to the :ref:`-pathSuffix-` directive with the one difference that the path suffix
it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as
is even in the case that the directive successfully matched and the request is passed on to its inner route.
As opposed to :ref:`-pathPrefixTest-` this directive matches and consumes the unmatched path from the right, i.e. the end.
.. caution:: For efficiency reasons, the given ``PathMatcher`` must match the desired suffix in reversed-segment
order, i.e. ``pathSuffixTest("baz" / "bar")`` would match ``/foo/bar/baz``! The order within a segment match is
not reversed.
Depending on the type of its ``PathMatcher`` argument the ``pathSuffixTest`` directive extracts zero or more values from
the URI. If the match fails the request is rejected with an :ref:`empty rejection set <empty rejections>`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: pathSuffixTest-

View file

@ -0,0 +1,35 @@
.. _-rawPathPrefix-:
rawPathPrefix
=============
Matches and consumes a prefix of the unmatched path of the ``RequestContext`` against the given ``PathMatcher``,
potentially extracts one or more values (depending on the type of the argument).
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: rawPathPrefix
Description
-----------
This directive filters incoming requests based on the part of their URI that hasn't been matched yet by other
potentially existing ``rawPathPrefix`` or :ref:`-pathPrefix-` directives on higher levels of the routing structure.
Its one parameter is usually an expression evaluating to a ``PathMatcher`` instance (see also: :ref:`pathmatcher-dsl`).
As opposed to its :ref:`-pathPrefix-` counterpart ``rawPathPrefix`` does *not* automatically add a leading slash to its
``PathMatcher`` argument. Rather its ``PathMatcher`` argument is applied to the unmatched path as is.
Depending on the type of its ``PathMatcher`` argument the ``rawPathPrefix`` directive extracts zero or more values from
the URI. If the match fails the request is rejected with an :ref:`empty rejection set <empty rejections>`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: rawPathPrefix-

View file

@ -0,0 +1,38 @@
.. _-rawPathPrefixTest-:
rawPathPrefixTest
=================
Checks whether the unmatched path of the ``RequestContext`` has a prefix matched by the given ``PathMatcher``.
Potentially extracts one or more values (depending on the type of the argument) but doesn't consume its match from
the unmatched path.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/PathDirectives.scala
:snippet: rawPathPrefixTest
Description
-----------
This directive is very similar to the :ref:`-pathPrefix-` directive with the one difference that the path prefix
it matched (if it matched) is *not* consumed. The unmatched path of the ``RequestContext`` is therefore left as
is even in the case that the directive successfully matched and the request is passed on to its inner route.
For more info on how to create a ``PathMatcher`` see :ref:`pathmatcher-dsl`.
As opposed to its :ref:`-pathPrefixTest-` counterpart ``rawPathPrefixTest`` does *not* automatically add a leading slash
to its ``PathMatcher`` argument. Rather its ``PathMatcher`` argument is applied to the unmatched path as is.
Depending on the type of its ``PathMatcher`` argument the ``rawPathPrefixTest`` directive extracts zero or more values
from the URI. If the match fails the request is rejected with an :ref:`empty rejection set <empty rejections>`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: rawPathPrefixTest-

View file

@ -0,0 +1,31 @@
.. _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

View file

@ -6,9 +6,12 @@ High-level HTTP API
quick-start quick-start
routing routing
directives
rejections
exception-handling
path-matchers
marshalling marshalling
custom-directives directives/alphabetically
predefined-directives-alphabetically directives/by-trait
predefined-directives-by-trait

View file

@ -0,0 +1,200 @@
.. _pathmatcher-dsl:
The PathMatcher DSL
===================
For being able to work with the :ref:`PathDirectives` effectively you should have some understanding of the
``PathMatcher`` mini-DSL that Akka HTTP provides for elegantly defining URI matching behavior.
Overview
--------
When a request (or rather the respective ``RequestContext`` instance) enters the route structure it has an
"unmatched path" that is identical to the ``request.uri.path``. As it descends the routing tree and passes through one
or more :ref:`-pathPrefix-`/:ref:`-path-` directives the "unmatched path" progressively gets "eaten into" from the left
until, in most cases, it eventually has been consumed completely.
What exactly gets matched and consumed as well as extracted from the unmatched path in each directive is defined with
the patch matching DSL, which is built around these types::
trait PathMatcher[L: Tuple]
type PathMatcher0 = PathMatcher[Unit]
type PathMatcher1[T] = PathMatcher[Tuple1[T]]
The number and types of the values extracted by a ``PathMatcher`` instance is represented by the ``L`` type
parameter which needs to be one of Scala's TupleN types or Unit (which is designated by the ``Tuple`` context bound).
The convenience alias ``PathMatcher0`` can be used for all matchers which don't extract anything while ``PathMatcher1[T]``
defines a matcher which only extracts a single value of type ``T``.
Here is an example of a more complex ``PathMatcher`` expression:
.. includecode2:: ../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: path-matcher
This will match paths like ``foo/bar/X42/edit`` or ``foo/bar/X/create``.
.. note:: The path matching DSL describes what paths to accept **after** URL decoding. This is why the path-separating
slashes have special status and cannot simply be specified as part of a string! The string "foo/bar" would match
the raw URI path "foo%2Fbar", which is most likely not what you want!
Basic PathMatchers
------------------
A complex ``PathMatcher`` can be constructed by combining or modifying more basic ones. Here are the basic matchers
that Akka HTTP already provides for you:
``String``
You can use a ``String`` instance as a ``PathMatcher0``. Strings simply match themselves and extract no value.
Note that strings are interpreted as the decoded representation of the path, so if they include a '/' character
this character will match "%2F" in the encoded raw URI!
``Regex``
You can use a ``Regex`` instance as a ``PathMatcher1[String]``, which matches whatever the regex matches and extracts
one ``String`` value. A ``PathMatcher`` created from a regular expression extracts either the complete match (if the
regex doesn't contain a capture group) or the capture group (if the regex contains exactly one capture group).
If the regex contains more than one capture group an ``IllegalArgumentException`` will be thrown.
``Map[String, T]``
You can use a ``Map[String, T]`` instance as a ``PathMatcher1[T]``, which matches any of the keys and extracts the
respective map value for it.
``Slash: PathMatcher0``
Matches exactly one path-separating slash (``/``) character and extracts nothing.
``Segment: PathMatcher1[String]``
Matches if the unmatched path starts with a path segment (i.e. not a slash).
If so the path segment is extracted as a ``String`` instance.
``PathEnd: PathMatcher0``
Matches the very end of the path, similar to ``$`` in regular expressions and extracts nothing.
``Rest: PathMatcher1[String]``
Matches and extracts the complete remaining unmatched part of the request's URI path as an (encoded!) String.
If you need access to the remaining *decoded* elements of the path use ``RestPath`` instead.
``RestPath: PathMatcher1[Path]``
Matches and extracts the complete remaining, unmatched part of the request's URI path.
``IntNumber: PathMatcher1[Int]``
Efficiently matches a number of decimal digits and extracts their (non-negative) ``Int`` value. The matcher will not
match zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Int.MaxValue``.
``LongNumber: PathMatcher1[Long]``
Efficiently matches a number of decimal digits and extracts their (non-negative) ``Long`` value. The matcher will not
match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MaxValue``.
``HexIntNumber: PathMatcher1[Int]``
Efficiently matches a number of hex digits and extracts their (non-negative) ``Int`` value. The matcher will not match
zero digits or a sequence of digits that would represent an ``Int`` value larger than ``Int.MaxValue``.
``HexLongNumber: PathMatcher1[Long]``
Efficiently matches a number of hex digits and extracts their (non-negative) ``Long`` value. The matcher will not
match zero digits or a sequence of digits that would represent an ``Long`` value larger than ``Long.MaxValue``.
``DoubleNumber: PathMatcher1[Double]``
Matches and extracts a ``Double`` value. The matched string representation is the pure decimal,
optionally signed form of a double value, i.e. without exponent.
``JavaUUID: PathMatcher1[UUID]``
Matches and extracts a ``java.util.UUID`` instance.
``Neutral: PathMatcher0``
A matcher that always matches, doesn't consume anything and extracts nothing.
Serves mainly as a neutral element in ``PathMatcher`` composition.
``Segments: PathMatcher1[List[String]]``
Matches all remaining segments as a list of strings. Note that this can also be "no segments" resulting in the empty
list. If the path has a trailing slash this slash will *not* be matched, i.e. remain unmatched and to be consumed by
potentially nested directives.
``separateOnSlashes(string: String): PathMatcher0``
Converts a path string containing slashes into a ``PathMatcher0`` that interprets slashes as
path segment separators. This means that a matcher matching "%2F" cannot be constructed with this helper.
``provide[L: Tuple](extractions: L): PathMatcher[L]``
Always matches, consumes nothing and extracts the given ``TupleX`` of values.
``PathMatcher[L: Tuple](prefix: Path, extractions: L): PathMatcher[L]``
Matches and consumes the given path prefix and extracts the given list of extractions.
If the given prefix is empty the returned matcher matches always and consumes nothing.
Combinators
-----------
Path matchers can be combined with these combinators to form higher-level constructs:
Tilde Operator (``~``)
The tilde is the most basic combinator. It simply concatenates two matchers into one, i.e if the first one matched
(and consumed) the second one is tried. The extractions of both matchers are combined type-safely.
For example: ``"foo" ~ "bar"`` yields a matcher that is identical to ``"foobar"``.
Slash Operator (``/``)
This operator concatenates two matchers and inserts a ``Slash`` matcher in between them.
For example: ``"foo" / "bar"`` is identical to ``"foo" ~ Slash ~ "bar"``.
Pipe Operator (``|``)
This operator combines two matcher alternatives in that the second one is only tried if the first one did *not* match.
The two sub-matchers must have compatible types.
For example: ``"foo" | "bar"`` will match either "foo" *or* "bar".
Modifiers
---------
Path matcher instances can be transformed with these modifier methods:
``/``
The slash operator cannot only be used as combinator for combining two matcher instances, it can also be used as
a postfix call. ``matcher /`` is identical to ``matcher ~ Slash`` but shorter and easier to read.
``?``
By postfixing a matcher with ``?`` you can turn any ``PathMatcher`` into one that always matches, optionally consumes
and potentially extracts an ``Option`` of the underlying matchers extraction. The result type depends on the type
of the underlying matcher:
.. rst-class:: table table-striped
=========================== =============================
If a ``matcher`` is of type then ``matcher.?`` is of type
=========================== =============================
``PathMatcher0`` ``PathMatcher0``
``PathMatcher1[T]`` ``PathMatcher1[Option[T]``
``PathMatcher[L: Tuple]`` ``PathMatcher[Option[L]]``
=========================== =============================
``repeat(separator: PathMatcher0 = PathMatchers.Neutral)``
By postfixing a matcher with ``repeat(separator)`` you can turn any ``PathMatcher`` into one that always matches,
consumes zero or more times (with the given separator) and potentially extracts a ``List`` of the underlying matcher's
extractions. The result type depends on the type of the underlying matcher:
.. rst-class:: table table-striped
=========================== =======================================
If a ``matcher`` is of type then ``matcher.repeat(...)`` is of type
=========================== =======================================
``PathMatcher0`` ``PathMatcher0``
``PathMatcher1[T]`` ``PathMatcher1[List[T]``
``PathMatcher[L: Tuple]`` ``PathMatcher[List[L]]``
=========================== =======================================
``unary_!``
By prefixing a matcher with ``!`` it can be turned into a ``PathMatcher0`` that only matches if the underlying matcher
does *not* match and vice versa.
``transform`` / ``(h)flatMap`` / ``(h)map``
These modifiers allow you to append your own "post-application" logic to another matcher in order to form a custom
one. You can map over the extraction(s), turn mismatches into matches or vice-versa or do anything else with the
results of the underlying matcher. Take a look at the method signatures and implementations for more guidance as to
how to use them.
Examples
--------
.. includecode2:: ../code/docs/http/server/directives/PathDirectivesExamplesSpec.scala
:snippet: path-dsl

View file

@ -1,4 +0,0 @@
Predefined Directives (alphabetically)
======================================
(todo)

View file

@ -1,4 +0,0 @@
Predefined Directives (by trait)
================================
(todo)

View file

@ -0,0 +1,100 @@
.. _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.

View file

@ -1,6 +1,105 @@
.. _http-routing-scala: .. _http-routing-scala:
Routing DSL The Routing DSL
=========== ===============
.. _Routes:
Routes
------
The "Route" is the central concept of the routing DSL since all structures you can build with it are instances of
a ``Route``. The type Route is defined like this::
type Route = RequestContext ⇒ Future[RouteResult]
It's a simple alias for a function taking a ``RequestContext`` as parameter and returning a ``Future[RouteResult]``.
Generally when a route receives a request (or rather a ``RequestContext`` for it) it can do one of these things:
- Complete the request by returning the value of ``requestContext.complete(...)``
- Reject the request by returning the value of ``requestContext.reject(...)`` (see :ref:`Rejections`)
- Fail the request by returning the value of ``requestContext.fail(...)`` or by just throwing an exception (see :ref:`Exception Handling`)
- Do any kind of asynchronous processing and instantly return a ``Future[RouteResult]`` to be eventually completed later on
The first case is pretty clear, by calling ``complete`` a given response is sent to the client as reaction to the
request. In the second case "reject" means that the route does not want to handle the request. You'll see further down
in the section about route composition what this is good for.
A ``Route`` can be sealed using ``Route.seal`` and by supplying a ``RejectionHandler`` and an ``ExceptionHandler`` which
converts rejection results and exceptional results into appropriate HTTP responses for the peer.
A ``Route`` can be lifted into a handler to be used with the http-core API using ``Route.handlerFlow`` or
``Route.asyncHandler``.
.. _RequestContext:
RequestContext
--------------
The request context wraps a request together with additional contextual information to be passed through the route tree.
Also, it provides the only way of creating a ``RouteResult`` by calling one of the above methods.
In addition to the request it contains the ``unmatchedPath``, a value that describes how much of the request URI has not
yet been matched and instances of several configuration instances like a ``LoggingAdapter``, an ``ExecutionContext``, and
``RoutingSettings``, so that they don't have to be passed around explicitly.
The ``RequestContext`` itself is immutable but contains several helper methods to create updated versions.
Composing Routes
----------------
There are three basic operations we need for building more complex routes from simpler ones:
.. rst-class:: wide
- Route transformation, which delegates processing to another, "inner" route but in the process changes some properties
of either the incoming request, the outgoing response or both
- Route filtering, which only lets requests satisfying a given filter condition pass and rejects all others
- Route chaining, which tries a second route if a given first one was rejected
The last point is achieved with the concatenation operator ``~``, which is an extension method provided by an implicit in
``RouteConcatenation``. The first two points are provided by so-called :ref:`Directives`, of which a large number is
already predefined by Akka HTTP and which you can also easily create yourself.
:ref:`Directives` deliver most of Akka HTTP's power and flexibility.
.. _The Routing Tree:
The Routing Tree
----------------
Essentially, when you combine directives and custom routes via nesting and the ``~`` operator, you build a routing
structure that forms a tree. When a request comes in it is injected into this tree at the root and flows down through
all the branches in a depth-first manner until either some node completes it or it is fully rejected.
Consider this schematic example::
val route =
a {
b {
c {
... // route 1
} ~
d {
... // route 2
} ~
... // route 3
} ~
e {
... // route 4
}
}
Here five directives form a routing tree.
.. rst-class:: wide
- Route 1 will only be reached if directives ``a``, ``b`` and ``c`` all let the request pass through.
- Route 2 will run if ``a`` and ``b`` pass, ``c`` rejects and ``d`` passes.
- Route 3 will run if ``a`` and ``b`` pass, but ``c`` and ``d`` reject.
Route 3 can therefore be seen as a "catch-all" route that only kicks in, if routes chained into preceding positions
reject. This mechanism can make complex filtering logic quite easy to implement: simply put the most
specific cases up front and the most general cases in the back.
(todo)