=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
|
|
@ -22,9 +22,8 @@ class HttpServerExampleSpec
|
|||
implicit val materializer = FlowMaterializer()
|
||||
|
||||
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)
|
||||
// handle connection here
|
||||
}
|
||||
//#bind-example
|
||||
}
|
||||
|
|
@ -56,7 +55,9 @@ class HttpServerExampleSpec
|
|||
serverBinding.connections foreach { connection =>
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue