=doc a first set of new and imported documentation for akka-http
This commit is contained in:
parent
6f11735765
commit
af14fd8243
81 changed files with 3674 additions and 54 deletions
109
akka-docs-dev/_sphinx/exts/includecode2.py
Normal file
109
akka-docs-dev/_sphinx/exts/includecode2.py
Normal 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)
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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!!!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
@ -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")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
HTTPS
|
HTTPS
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
Is not yet supported.
|
||||||
|
|
||||||
(todo)
|
(todo)
|
||||||
|
|
@ -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
|
||||||
------------
|
------------
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
Custom Directives
|
|
||||||
=================
|
|
||||||
|
|
||||||
(todo)
|
|
||||||
138
akka-docs-dev/rst/scala/http/directives.rst
Normal file
138
akka-docs-dev/rst/scala/http/directives.rst
Normal 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`.
|
||||||
161
akka-docs-dev/rst/scala/http/directives/alphabetically.rst
Normal file
161
akka-docs-dev/rst/scala/http/directives/alphabetically.rst
Normal 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.
|
||||||
|
====================================== =================================================================================
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
106
akka-docs-dev/rst/scala/http/directives/by-trait.rst
Normal file
106
akka-docs-dev/rst/scala/http/directives/by-trait.rst
Normal 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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
.. _CookieDirectives:
|
||||||
|
|
||||||
|
CookieDirectives
|
||||||
|
================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
cookie
|
||||||
|
deleteCookie
|
||||||
|
optionalCookie
|
||||||
|
setCookie
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
.. _DebuggingDirectives:
|
||||||
|
|
||||||
|
DebuggingDirectives
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
logRequest
|
||||||
|
logRequestResult
|
||||||
|
logResult
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
.. _ExecutionDirectives:
|
||||||
|
|
||||||
|
ExecutionDirectives
|
||||||
|
===================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
handleExceptions
|
||||||
|
handleRejections
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
.. _PathDirectives:
|
||||||
|
|
||||||
|
PathDirectives
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
path
|
||||||
|
pathEnd
|
||||||
|
pathEndOrSingleSlash
|
||||||
|
pathPrefix
|
||||||
|
pathPrefixTest
|
||||||
|
pathSingleSlash
|
||||||
|
pathSuffix
|
||||||
|
pathSuffixTest
|
||||||
|
rawPathPrefix
|
||||||
|
rawPathPrefixTest
|
||||||
|
|
||||||
|
../../path-matchers
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
|
|
@ -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-
|
||||||
31
akka-docs-dev/rst/scala/http/exception-handling.rst
Normal file
31
akka-docs-dev/rst/scala/http/exception-handling.rst
Normal 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
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
200
akka-docs-dev/rst/scala/http/path-matchers.rst
Normal file
200
akka-docs-dev/rst/scala/http/path-matchers.rst
Normal 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
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
Predefined Directives (alphabetically)
|
|
||||||
======================================
|
|
||||||
|
|
||||||
(todo)
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
Predefined Directives (by trait)
|
|
||||||
================================
|
|
||||||
|
|
||||||
(todo)
|
|
||||||
100
akka-docs-dev/rst/scala/http/rejections.rst
Normal file
100
akka-docs-dev/rst/scala/http/rejections.rst
Normal 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.
|
||||||
|
|
@ -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)
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue