htp #20103 introduce Scaladoc groups for Directives
This commit is contained in:
parent
f2771eaab9
commit
aec81c2ac2
28 changed files with 537 additions and 8 deletions
|
|
@ -42,4 +42,4 @@ class NameDefaultUnmarshallerReceptacle[T](val name: String, val default: T, val
|
|||
|
||||
class RequiredValueUnmarshallerReceptacle[T](val name: String, val requiredValue: T, val um: FSU[T])
|
||||
|
||||
class RepeatedValueUnmarshallerReceptacle[T](val name: String, val um: FSU[T])
|
||||
class RepeatedValueUnmarshallerReceptacle[T](val name: String, val um: FSU[T])
|
||||
|
|
|
|||
|
|
@ -223,12 +223,18 @@ object PathMatcher extends ImplicitPathMatcherConstruction {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @groupname pathmatcherimpl Path matcher implicits
|
||||
* @groupprio pathmatcherimpl 172
|
||||
*/
|
||||
trait ImplicitPathMatcherConstruction {
|
||||
import PathMatcher._
|
||||
|
||||
/**
|
||||
* Creates a PathMatcher that consumes (a prefix of) the first path segment
|
||||
* (if the path begins with a segment) and extracts a given value.
|
||||
*
|
||||
* @group pathmatcherimpl
|
||||
*/
|
||||
implicit def stringExtractionPair2PathMatcher[T](tuple: (String, T)): PathMatcher1[T] =
|
||||
PathMatcher(tuple._1 :: Path.Empty, Tuple1(tuple._2))
|
||||
|
|
@ -236,10 +242,15 @@ trait ImplicitPathMatcherConstruction {
|
|||
/**
|
||||
* Creates a PathMatcher that consumes (a prefix of) the first path segment
|
||||
* (if the path begins with a segment).
|
||||
*
|
||||
* @group pathmatcherimpl
|
||||
*/
|
||||
implicit def segmentStringToPathMatcher(segment: String): PathMatcher0 =
|
||||
PathMatcher(segment :: Path.Empty, ())
|
||||
|
||||
/**
|
||||
* @group pathmatcherimpl
|
||||
*/
|
||||
implicit def stringNameOptionReceptacle2PathMatcher(nr: NameOptionReceptacle[String]): PathMatcher0 =
|
||||
PathMatcher(nr.name).?
|
||||
|
||||
|
|
@ -249,6 +260,8 @@ trait ImplicitPathMatcherConstruction {
|
|||
* Extracts either the complete match (if the regex doesn't contain a capture group) or
|
||||
* the capture group (if the regex contains exactly one).
|
||||
* If the regex contains more than one capture group the method throws an IllegalArgumentException.
|
||||
*
|
||||
* @group pathmatcherimpl
|
||||
*/
|
||||
implicit def regex2PathMatcher(regex: Regex): PathMatcher1[String] = regex.groupCount match {
|
||||
case 0 ⇒ new PathMatcher1[String] {
|
||||
|
|
@ -276,18 +289,26 @@ trait ImplicitPathMatcherConstruction {
|
|||
* Creates a PathMatcher from the given Map of path segments (prefixes) to extracted values.
|
||||
* If the unmatched path starts with a segment having one of the maps keys as a prefix
|
||||
* the matcher consumes this path segment (prefix) and extracts the corresponding map value.
|
||||
*
|
||||
* @group pathmatcherimpl
|
||||
*/
|
||||
implicit def valueMap2PathMatcher[T](valueMap: Map[String, T]): PathMatcher1[T] =
|
||||
if (valueMap.isEmpty) PathMatchers.nothingMatcher
|
||||
else valueMap.map { case (prefix, value) ⇒ stringExtractionPair2PathMatcher((prefix, value)) }.reduceLeft(_ | _)
|
||||
}
|
||||
|
||||
/**
|
||||
* @groupname pathmatcher Path matchers
|
||||
* @groupprio pathmatcher 171
|
||||
*/
|
||||
trait PathMatchers {
|
||||
import PathMatcher._
|
||||
|
||||
/**
|
||||
* Converts a path string containing slashes into a PathMatcher that interprets slashes as
|
||||
* path segment separators.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
def separateOnSlashes(string: String): PathMatcher0 = {
|
||||
@tailrec def split(ix: Int = 0, matcher: PathMatcher0 = null): PathMatcher0 = {
|
||||
|
|
@ -301,6 +322,8 @@ trait PathMatchers {
|
|||
|
||||
/**
|
||||
* A PathMatcher that matches a single slash character ('/').
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object Slash extends PathMatcher0 {
|
||||
def apply(path: Path) = path match {
|
||||
|
|
@ -311,6 +334,8 @@ trait PathMatchers {
|
|||
|
||||
/**
|
||||
* A PathMatcher that matches the very end of the requests URI path.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object PathEnd extends PathMatcher0 {
|
||||
def apply(path: Path) = path match {
|
||||
|
|
@ -324,6 +349,8 @@ trait PathMatchers {
|
|||
* unmatched part of the request's URI path as an (encoded!) String.
|
||||
* If you need access to the remaining unencoded elements of the path
|
||||
* use the `RestPath` matcher!
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object Rest extends PathMatcher1[String] {
|
||||
def apply(path: Path) = Matched(Path.Empty, Tuple1(path.toString))
|
||||
|
|
@ -332,6 +359,8 @@ trait PathMatchers {
|
|||
/**
|
||||
* A PathMatcher that matches and extracts the complete remaining,
|
||||
* unmatched part of the request's URI path.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object RestPath extends PathMatcher1[Path] {
|
||||
def apply(path: Path) = Matched(Path.Empty, Tuple1(path))
|
||||
|
|
@ -341,6 +370,8 @@ trait PathMatchers {
|
|||
* A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Int value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger
|
||||
* than Int.MaxValue.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object IntNumber extends NumberMatcher[Int](Int.MaxValue, 10) {
|
||||
def fromChar(c: Char) = fromDecimalChar(c)
|
||||
|
|
@ -350,6 +381,8 @@ trait PathMatchers {
|
|||
* A PathMatcher that efficiently matches a number of digits and extracts their (non-negative) Long value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger
|
||||
* than Long.MaxValue.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object LongNumber extends NumberMatcher[Long](Long.MaxValue, 10) {
|
||||
def fromChar(c: Char) = fromDecimalChar(c)
|
||||
|
|
@ -359,6 +392,8 @@ trait PathMatchers {
|
|||
* A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Int value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Int value larger
|
||||
* than Int.MaxValue.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object HexIntNumber extends NumberMatcher[Int](Int.MaxValue, 16) {
|
||||
def fromChar(c: Char) = fromHexChar(c)
|
||||
|
|
@ -368,12 +403,17 @@ trait PathMatchers {
|
|||
* A PathMatcher that efficiently matches a number of hex-digits and extracts their (non-negative) Long value.
|
||||
* The matcher will not match 0 digits or a sequence of digits that would represent an Long value larger
|
||||
* than Long.MaxValue.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object HexLongNumber extends NumberMatcher[Long](Long.MaxValue, 16) {
|
||||
def fromChar(c: Char) = fromHexChar(c)
|
||||
}
|
||||
|
||||
// common implementation of Number matchers
|
||||
/**
|
||||
* @group pathmatcher
|
||||
*/
|
||||
abstract class NumberMatcher[@specialized(Int, Long) T](max: T, base: T)(implicit x: Integral[T])
|
||||
extends PathMatcher1[T] {
|
||||
|
||||
|
|
@ -414,6 +454,8 @@ trait PathMatchers {
|
|||
/**
|
||||
* A PathMatcher that 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.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
val DoubleNumber: PathMatcher1[Double] =
|
||||
PathMatcher("""[+-]?\d*\.?\d*""".r) flatMap { string ⇒
|
||||
|
|
@ -423,6 +465,8 @@ trait PathMatchers {
|
|||
|
||||
/**
|
||||
* A PathMatcher that matches and extracts a java.util.UUID instance.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
val JavaUUID: PathMatcher1[UUID] =
|
||||
PathMatcher("""[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}""".r) flatMap { string ⇒
|
||||
|
|
@ -433,12 +477,16 @@ trait PathMatchers {
|
|||
/**
|
||||
* A PathMatcher that always matches, doesn't consume anything and extracts nothing.
|
||||
* Serves mainly as a neutral element in PathMatcher composition.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
val Neutral: PathMatcher0 = PathMatcher.provide(())
|
||||
|
||||
/**
|
||||
* A PathMatcher that matches if the unmatched path starts with a path segment.
|
||||
* If so the path segment is extracted as a String.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
object Segment extends PathMatcher1[String] {
|
||||
def apply(path: Path) = path match {
|
||||
|
|
@ -451,6 +499,8 @@ trait PathMatchers {
|
|||
* A PathMatcher that matches up to 128 remaining segments as a List[String].
|
||||
* This can also be no segments resulting in the empty list.
|
||||
* If the path has a trailing slash this slash will *not* be matched.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
val Segments: PathMatcher1[List[String]] = Segments(min = 0, max = 128)
|
||||
|
||||
|
|
@ -458,6 +508,8 @@ trait PathMatchers {
|
|||
* A PathMatcher that matches the given number of path segments (separated by slashes) as a List[String].
|
||||
* If there are more than `count` segments present the remaining ones will be left unmatched.
|
||||
* If the path has a trailing slash this slash will *not* be matched.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
def Segments(count: Int): PathMatcher1[List[String]] = Segment.repeat(count, separator = Slash)
|
||||
|
||||
|
|
@ -465,11 +517,15 @@ trait PathMatchers {
|
|||
* A PathMatcher that matches between `min` and `max` (both inclusively) path segments (separated by slashes)
|
||||
* as a List[String]. If there are more than `count` segments present the remaining ones will be left unmatched.
|
||||
* If the path has a trailing slash this slash will *not* be matched.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
def Segments(min: Int, max: Int): PathMatcher1[List[String]] = Segment.repeat(min, max, separator = Slash)
|
||||
|
||||
/**
|
||||
* A PathMatcher that never matches anything.
|
||||
*
|
||||
* @group pathmatcher
|
||||
*/
|
||||
def nothingMatcher[L: Tuple]: PathMatcher[L] =
|
||||
new PathMatcher[L] {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,15 @@ package akka.http.scaladsl.server
|
|||
import akka.http.scaladsl.util.FastFuture
|
||||
import akka.http.scaladsl.util.FastFuture._
|
||||
|
||||
/**
|
||||
* @groupname concat Route concatenation
|
||||
* @groupprio concat 300
|
||||
*/
|
||||
trait RouteConcatenation {
|
||||
|
||||
/**
|
||||
* @group concat
|
||||
*/
|
||||
implicit def enhanceRouteWithConcatenation(route: Route): RouteConcatenation.RouteWithConcatenation =
|
||||
new RouteConcatenation.RouteWithConcatenation(route: Route)
|
||||
}
|
||||
|
|
@ -32,4 +39,4 @@ object RouteConcatenation extends RouteConcatenation {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,75 +15,131 @@ import akka.http.scaladsl.util.FastFuture
|
|||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.util.FastFuture._
|
||||
|
||||
/**
|
||||
* @groupname basic Basic directives
|
||||
* @groupprio basic 10
|
||||
*/
|
||||
trait BasicDirectives {
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapInnerRoute(f: Route ⇒ Route): Directive0 =
|
||||
Directive { inner ⇒ f(inner(())) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRequestContext(f: RequestContext ⇒ RequestContext): Directive0 =
|
||||
mapInnerRoute { inner ⇒ ctx ⇒ inner(f(ctx)) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRequest(f: HttpRequest ⇒ HttpRequest): Directive0 =
|
||||
mapRequestContext(_ mapRequest f)
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRouteResultFuture(f: Future[RouteResult] ⇒ Future[RouteResult]): Directive0 =
|
||||
Directive { inner ⇒ ctx ⇒ f(inner(())(ctx)) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRouteResult(f: RouteResult ⇒ RouteResult): Directive0 =
|
||||
Directive { inner ⇒ ctx ⇒ inner(())(ctx).fast.map(f)(ctx.executionContext) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRouteResultWith(f: RouteResult ⇒ Future[RouteResult]): Directive0 =
|
||||
Directive { inner ⇒ ctx ⇒ inner(())(ctx).fast.flatMap(f)(ctx.executionContext) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRouteResultPF(f: PartialFunction[RouteResult, RouteResult]): Directive0 =
|
||||
mapRouteResult(f.applyOrElse(_, conforms[RouteResult]))
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRouteResultWithPF(f: PartialFunction[RouteResult, Future[RouteResult]]): Directive0 =
|
||||
mapRouteResultWith(f.applyOrElse(_, FastFuture.successful[RouteResult]))
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def recoverRejections(f: immutable.Seq[Rejection] ⇒ RouteResult): Directive0 =
|
||||
mapRouteResultPF { case RouteResult.Rejected(rejections) ⇒ f(rejections) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def recoverRejectionsWith(f: immutable.Seq[Rejection] ⇒ Future[RouteResult]): Directive0 =
|
||||
mapRouteResultWithPF { case RouteResult.Rejected(rejections) ⇒ f(rejections) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapRejections(f: immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]): Directive0 =
|
||||
recoverRejections(rejections ⇒ RouteResult.Rejected(f(rejections)))
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapResponse(f: HttpResponse ⇒ HttpResponse): Directive0 =
|
||||
mapRouteResultPF { case RouteResult.Complete(response) ⇒ RouteResult.Complete(f(response)) }
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapResponseEntity(f: ResponseEntity ⇒ ResponseEntity): Directive0 =
|
||||
mapResponse(_ mapEntity f)
|
||||
|
||||
/**
|
||||
* @group basic
|
||||
*/
|
||||
def mapResponseHeaders(f: immutable.Seq[HttpHeader] ⇒ immutable.Seq[HttpHeader]): Directive0 =
|
||||
mapResponse(_ mapHeaders f)
|
||||
|
||||
/**
|
||||
* A Directive0 that always passes the request on to its inner route
|
||||
* (i.e. does nothing with the request or the response).
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def pass: Directive0 = Directive.Empty
|
||||
|
||||
/**
|
||||
* Injects the given value into a directive.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def provide[T](value: T): Directive1[T] = tprovide(Tuple1(value))
|
||||
|
||||
/**
|
||||
* Injects the given values into a directive.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def tprovide[L: Tuple](values: L): Directive[L] =
|
||||
Directive { _(values) }
|
||||
|
||||
/**
|
||||
* Extracts a single value using the given function.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extract[T](f: RequestContext ⇒ T): Directive1[T] =
|
||||
textract(ctx ⇒ Tuple1(f(ctx)))
|
||||
|
||||
/**
|
||||
* Extracts a number of values using the given function.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def textract[L: Tuple](f: RequestContext ⇒ L): Directive[L] =
|
||||
Directive { inner ⇒ ctx ⇒ inner(f(ctx))(ctx) }
|
||||
|
|
@ -91,6 +147,8 @@ trait BasicDirectives {
|
|||
/**
|
||||
* Adds a TransformationRejection cancelling all rejections equal to the given one
|
||||
* to the list of rejections potentially coming back from the inner route.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def cancelRejection(rejection: Rejection): Directive0 =
|
||||
cancelRejections(_ == rejection)
|
||||
|
|
@ -98,6 +156,8 @@ trait BasicDirectives {
|
|||
/**
|
||||
* Adds a TransformationRejection cancelling all rejections of one of the given classes
|
||||
* to the list of rejections potentially coming back from the inner route.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def cancelRejections(classes: Class[_]*): Directive0 =
|
||||
cancelRejections(r ⇒ classes.exists(_ isInstance r))
|
||||
|
|
@ -105,91 +165,123 @@ trait BasicDirectives {
|
|||
/**
|
||||
* Adds a TransformationRejection cancelling all rejections for which the given filter function returns true
|
||||
* to the list of rejections potentially coming back from the inner route.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def cancelRejections(cancelFilter: Rejection ⇒ Boolean): Directive0 =
|
||||
mapRejections(_ :+ TransformationRejection(_ filterNot cancelFilter))
|
||||
|
||||
/**
|
||||
* Transforms the unmatchedPath of the RequestContext using the given function.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def mapUnmatchedPath(f: Uri.Path ⇒ Uri.Path): Directive0 =
|
||||
mapRequestContext(_ mapUnmatchedPath f)
|
||||
|
||||
/**
|
||||
* Extracts the yet unmatched path from the RequestContext.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractUnmatchedPath: Directive1[Uri.Path] = BasicDirectives._extractUnmatchedPath
|
||||
|
||||
/**
|
||||
* Extracts the current [[HttpRequest]] instance.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractRequest: Directive1[HttpRequest] = BasicDirectives._extractRequest
|
||||
|
||||
/**
|
||||
* Extracts the complete request URI.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractUri: Directive1[Uri] = BasicDirectives._extractUri
|
||||
|
||||
/**
|
||||
* Runs its inner route with the given alternative [[scala.concurrent.ExecutionContextExecutor]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def withExecutionContext(ec: ExecutionContextExecutor): Directive0 =
|
||||
mapRequestContext(_ withExecutionContext ec)
|
||||
|
||||
/**
|
||||
* Extracts the [[scala.concurrent.ExecutionContextExecutor]] from the [[akka.http.scaladsl.server.RequestContext]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractExecutionContext: Directive1[ExecutionContextExecutor] = BasicDirectives._extractExecutionContext
|
||||
|
||||
/**
|
||||
* Runs its inner route with the given alternative [[akka.stream.Materializer]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def withMaterializer(materializer: Materializer): Directive0 =
|
||||
mapRequestContext(_ withMaterializer materializer)
|
||||
|
||||
/**
|
||||
* Extracts the [[akka.stream.Materializer]] from the [[akka.http.scaladsl.server.RequestContext]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractMaterializer: Directive1[Materializer] = BasicDirectives._extractMaterializer
|
||||
|
||||
/**
|
||||
* Runs its inner route with the given alternative [[akka.event.LoggingAdapter]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def withLog(log: LoggingAdapter): Directive0 =
|
||||
mapRequestContext(_ withLog log)
|
||||
|
||||
/**
|
||||
* Extracts the [[akka.event.LoggingAdapter]] from the [[akka.http.scaladsl.server.RequestContext]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractLog: Directive1[LoggingAdapter] =
|
||||
BasicDirectives._extractLog
|
||||
|
||||
/**
|
||||
* Runs its inner route with the given alternative [[RoutingSettings]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def withSettings(settings: RoutingSettings): Directive0 =
|
||||
mapRequestContext(_ withRoutingSettings settings)
|
||||
|
||||
/**
|
||||
* Runs the inner route with settings mapped by the given function.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def mapSettings(f: RoutingSettings ⇒ RoutingSettings): Directive0 =
|
||||
mapRequestContext(ctx ⇒ ctx.withRoutingSettings(f(ctx.settings)))
|
||||
|
||||
/**
|
||||
* Extracts the [[RoutingSettings]] from the [[akka.http.scaladsl.server.RequestContext]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractSettings: Directive1[RoutingSettings] =
|
||||
BasicDirectives._extractSettings
|
||||
|
||||
/**
|
||||
* Extracts the [[akka.http.scaladsl.settings.ParserSettings]] from the [[akka.http.scaladsl.server.RequestContext]].
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractParserSettings: Directive1[ParserSettings] =
|
||||
BasicDirectives._extractParserSettings
|
||||
|
||||
/**
|
||||
* Extracts the [[akka.http.scaladsl.server.RequestContext]] itself.
|
||||
*
|
||||
* @group basic
|
||||
*/
|
||||
def extractRequestContext: Directive1[RequestContext] = BasicDirectives._extractRequestContext
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ import HttpMethods._
|
|||
import StatusCodes._
|
||||
import EntityTag._
|
||||
|
||||
/**
|
||||
* @groupname cachecondition Cache condition directives
|
||||
* @groupprio cachecondition 20
|
||||
*/
|
||||
trait CacheConditionDirectives {
|
||||
import BasicDirectives._
|
||||
import RouteDirectives._
|
||||
|
|
@ -26,6 +30,8 @@ trait CacheConditionDirectives {
|
|||
* Note: if you want to combine this directive with `withRangeSupport(...)` you need to put
|
||||
* it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)`
|
||||
* must be on a deeper level in your route structure in order to function correctly.
|
||||
*
|
||||
* @group cachecondition
|
||||
*/
|
||||
def conditional(eTag: EntityTag): Directive0 = conditional(Some(eTag), None)
|
||||
|
||||
|
|
@ -39,6 +45,8 @@ trait CacheConditionDirectives {
|
|||
* Note: if you want to combine this directive with `withRangeSupport(...)` you need to put
|
||||
* it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)`
|
||||
* must be on a deeper level in your route structure in order to function correctly.
|
||||
*
|
||||
* @group cachecondition
|
||||
*/
|
||||
def conditional(lastModified: DateTime): Directive0 = conditional(None, Some(lastModified))
|
||||
|
||||
|
|
@ -52,6 +60,8 @@ trait CacheConditionDirectives {
|
|||
* Note: if you want to combine this directive with `withRangeSupport(...)` you need to put
|
||||
* it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)`
|
||||
* must be on a deeper level in your route structure in order to function correctly.
|
||||
*
|
||||
* @group cachecondition
|
||||
*/
|
||||
def conditional(eTag: EntityTag, lastModified: DateTime): Directive0 = conditional(Some(eTag), Some(lastModified))
|
||||
|
||||
|
|
@ -65,6 +75,8 @@ trait CacheConditionDirectives {
|
|||
* Note: if you want to combine this directive with `withRangeSupport(...)` you need to put
|
||||
* it on the *outside* of the `withRangeSupport(...)` directive, i.e. `withRangeSupport(...)`
|
||||
* must be on a deeper level in your route structure in order to function correctly.
|
||||
*
|
||||
* @group cachecondition
|
||||
*/
|
||||
def conditional(eTag: Option[EntityTag], lastModified: Option[DateTime]): Directive0 = {
|
||||
def addResponseHeaders: Directive0 =
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ import akka.http.scaladsl.model._
|
|||
import akka.http.scaladsl.coding._
|
||||
import akka.http.impl.util._
|
||||
|
||||
/**
|
||||
* @groupname coding Coding directives
|
||||
* @groupprio coding 50
|
||||
*/
|
||||
trait CodingDirectives {
|
||||
import BasicDirectives._
|
||||
import MiscDirectives._
|
||||
|
|
@ -23,6 +27,8 @@ trait CodingDirectives {
|
|||
/**
|
||||
* Rejects the request with an UnacceptedResponseEncodingRejection
|
||||
* if the given response encoding is not accepted by the client.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def responseEncodingAccepted(encoding: HttpEncoding): Directive0 =
|
||||
extractRequest.flatMap { request ⇒
|
||||
|
|
@ -37,6 +43,8 @@ trait CodingDirectives {
|
|||
*
|
||||
* If the `Accept-Encoding` header is missing or empty or specifies an encoding other than
|
||||
* identity, gzip or deflate then no encoding is used.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def encodeResponse: Directive0 =
|
||||
_encodeResponse(DefaultEncodeResponseEncoders)
|
||||
|
|
@ -51,6 +59,8 @@ trait CodingDirectives {
|
|||
*
|
||||
* If the `Accept-Encoding` header is empty and `NoCoding` is part of the encoders then no
|
||||
* response encoding is used. Otherwise the request is rejected.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def encodeResponseWith(first: Encoder, more: Encoder*): Directive0 =
|
||||
_encodeResponse(immutable.Seq(first +: more: _*))
|
||||
|
|
@ -60,6 +70,8 @@ trait CodingDirectives {
|
|||
/**
|
||||
* Decodes the incoming request using the given Decoder.
|
||||
* If the request encoding doesn't match the request is rejected with an `UnsupportedRequestEncodingRejection`.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def decodeRequestWith(decoder: Decoder): Directive0 = {
|
||||
def applyDecoder =
|
||||
|
|
@ -85,6 +97,8 @@ trait CodingDirectives {
|
|||
|
||||
/**
|
||||
* Rejects the request with an UnsupportedRequestEncodingRejection if its encoding doesn't match the given one.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def requestEncodedWith(encoding: HttpEncoding): Directive0 =
|
||||
extract(_.request.encoding).flatMap {
|
||||
|
|
@ -97,6 +111,8 @@ trait CodingDirectives {
|
|||
* encoders. If the request encoding doesn't match one of the given encoders
|
||||
* the request is rejected with an `UnsupportedRequestEncodingRejection`.
|
||||
* If no decoders are given the default encoders (`Gzip`, `Deflate`, `NoCoding`) are used.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def decodeRequestWith(decoders: Decoder*): Directive0 =
|
||||
theseOrDefault(decoders).map(decodeRequestWith).reduce(_ | _)
|
||||
|
|
@ -105,6 +121,8 @@ trait CodingDirectives {
|
|||
* Decompresses the incoming request if it is `gzip` or `deflate` compressed.
|
||||
* Uncompressed requests are passed through untouched.
|
||||
* If the request encoded with another encoding the request is rejected with an `UnsupportedRequestEncodingRejection`.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def decodeRequest: Directive0 =
|
||||
decodeRequestWith(DefaultCoders: _*)
|
||||
|
|
@ -112,6 +130,8 @@ trait CodingDirectives {
|
|||
/**
|
||||
* Inspects the response entity and adds a `Content-Encoding: gzip` response header if
|
||||
* the entities media-type is precompressed with gzip and no `Content-Encoding` header is present yet.
|
||||
*
|
||||
* @group coding
|
||||
*/
|
||||
def withPrecompressedMediaTypeSupport: Directive0 =
|
||||
mapResponse { response ⇒
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ import akka.http.scaladsl.model._
|
|||
import akka.http.scaladsl.model.headers._
|
||||
import akka.http.impl.util._
|
||||
|
||||
/**
|
||||
* @groupname cookie Cookie directives
|
||||
* @groupprio cookie 30
|
||||
*/
|
||||
trait CookieDirectives {
|
||||
import HeaderDirectives._
|
||||
import RespondWithDirectives._
|
||||
|
|
@ -17,6 +21,8 @@ trait CookieDirectives {
|
|||
/**
|
||||
* Extracts the [[HttpCookiePair]] with the given name. If the cookie is not present the
|
||||
* request is rejected with a respective [[MissingCookieRejection]].
|
||||
*
|
||||
* @group cookie
|
||||
*/
|
||||
def cookie(name: String): Directive1[HttpCookiePair] =
|
||||
headerValue(findCookie(name)) | reject(MissingCookieRejection(name))
|
||||
|
|
@ -24,6 +30,8 @@ trait CookieDirectives {
|
|||
/**
|
||||
* Extracts the [[HttpCookiePair]] with the given name as an `Option[HttpCookiePair]`.
|
||||
* If the cookie is not present a value of `None` is extracted.
|
||||
*
|
||||
* @group cookie
|
||||
*/
|
||||
def optionalCookie(name: String): Directive1[Option[HttpCookiePair]] =
|
||||
optionalHeaderValue(findCookie(name))
|
||||
|
|
@ -35,12 +43,16 @@ trait CookieDirectives {
|
|||
|
||||
/**
|
||||
* Adds a [[Set-Cookie]] response header with the given cookies.
|
||||
*
|
||||
* @group cookie
|
||||
*/
|
||||
def setCookie(first: HttpCookie, more: HttpCookie*): Directive0 =
|
||||
respondWithHeaders((first :: more.toList).map(`Set-Cookie`(_)))
|
||||
|
||||
/**
|
||||
* Adds a [[Set-Cookie]] response header expiring the given cookies.
|
||||
*
|
||||
* @group cookie
|
||||
*/
|
||||
def deleteCookie(first: HttpCookie, more: HttpCookie*): Directive0 =
|
||||
respondWithHeaders((first :: more.toList).map { c ⇒
|
||||
|
|
@ -49,6 +61,8 @@ trait CookieDirectives {
|
|||
|
||||
/**
|
||||
* Adds a [[Set-Cookie]] response header expiring the cookie with the given properties.
|
||||
*
|
||||
* @group cookie
|
||||
*/
|
||||
def deleteCookie(name: String, domain: String = "", path: String = ""): Directive0 =
|
||||
deleteCookie(HttpCookie(name, "", domain = domain.toOption, path = path.toOption))
|
||||
|
|
|
|||
|
|
@ -9,11 +9,17 @@ import akka.event.Logging._
|
|||
import akka.event.LoggingAdapter
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
/**
|
||||
* @groupname debugging Debugging directives
|
||||
* @groupprio debugging 40
|
||||
*/
|
||||
trait DebuggingDirectives {
|
||||
import BasicDirectives._
|
||||
|
||||
/**
|
||||
* Produces a log entry for every incoming request.
|
||||
*
|
||||
* @group debugging
|
||||
*/
|
||||
def logRequest(magnet: LoggingMagnet[HttpRequest ⇒ Unit]): Directive0 =
|
||||
extractRequestContext.flatMap { ctx ⇒
|
||||
|
|
@ -23,6 +29,8 @@ trait DebuggingDirectives {
|
|||
|
||||
/**
|
||||
* Produces a log entry for every [[RouteResult]].
|
||||
*
|
||||
* @group debugging
|
||||
*/
|
||||
def logResult(magnet: LoggingMagnet[RouteResult ⇒ Unit]): Directive0 =
|
||||
extractRequestContext.flatMap { ctx ⇒
|
||||
|
|
@ -34,6 +42,8 @@ trait DebuggingDirectives {
|
|||
|
||||
/**
|
||||
* Produces a log entry for every incoming request and [[RouteResult]].
|
||||
*
|
||||
* @group debugging
|
||||
*/
|
||||
def logRequestResult(magnet: LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit]): Directive0 =
|
||||
extractRequestContext.flatMap { ctx ⇒
|
||||
|
|
|
|||
|
|
@ -11,12 +11,18 @@ import scala.util.control.NonFatal
|
|||
import akka.http.scaladsl.util.FastFuture
|
||||
import akka.http.scaladsl.util.FastFuture._
|
||||
|
||||
/**
|
||||
* @groupname execution Execution directives
|
||||
* @groupprio execution 60
|
||||
*/
|
||||
trait ExecutionDirectives {
|
||||
import BasicDirectives._
|
||||
|
||||
/**
|
||||
* Transforms exceptions thrown during evaluation of its inner route using the given
|
||||
* [[akka.http.scaladsl.server.ExceptionHandler]].
|
||||
*
|
||||
* @group execution
|
||||
*/
|
||||
def handleExceptions(handler: ExceptionHandler): Directive0 =
|
||||
Directive { innerRouteBuilder ⇒
|
||||
|
|
@ -33,6 +39,8 @@ trait ExecutionDirectives {
|
|||
/**
|
||||
* Transforms rejections produced by its inner route using the given
|
||||
* [[akka.http.scaladsl.server.RejectionHandler]].
|
||||
*
|
||||
* @group execution
|
||||
*/
|
||||
def handleRejections(handler: RejectionHandler): Directive0 =
|
||||
extractRequestContext flatMap { ctx ⇒
|
||||
|
|
@ -55,4 +63,4 @@ trait ExecutionDirectives {
|
|||
}
|
||||
}
|
||||
|
||||
object ExecutionDirectives extends ExecutionDirectives
|
||||
object ExecutionDirectives extends ExecutionDirectives
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ import akka.http.scaladsl.model._
|
|||
import akka.http.scaladsl.model.headers._
|
||||
import akka.http.impl.util._
|
||||
|
||||
/**
|
||||
* @groupname fileandresource File and resource directives
|
||||
* @groupprio fileandresource 70
|
||||
*/
|
||||
trait FileAndResourceDirectives {
|
||||
import CacheConditionDirectives._
|
||||
import MethodDirectives._
|
||||
|
|
@ -30,6 +34,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* Completes GET requests with the content of the given file.
|
||||
* If the file cannot be found or read the request is rejected.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromFile(fileName: String)(implicit resolver: ContentTypeResolver): Route =
|
||||
getFromFile(new File(fileName))
|
||||
|
|
@ -37,6 +43,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* Completes GET requests with the content of the given file.
|
||||
* If the file cannot be found or read the request is rejected.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromFile(file: File)(implicit resolver: ContentTypeResolver): Route =
|
||||
getFromFile(file, resolver(file.getName))
|
||||
|
|
@ -44,6 +52,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* Completes GET requests with the content of the given file.
|
||||
* If the file cannot be found or read the request is rejected.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromFile(file: File, contentType: ContentType): Route =
|
||||
get {
|
||||
|
|
@ -72,6 +82,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* Completes GET requests with the content of the given class-path resource.
|
||||
* If the resource cannot be found or read the Route rejects the request.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromResource(resourceName: String)(implicit resolver: ContentTypeResolver): Route =
|
||||
getFromResource(resourceName, resolver(resourceName))
|
||||
|
|
@ -79,6 +91,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* Completes GET requests with the content of the given resource.
|
||||
* If the resource is a directory or cannot be found or read the Route rejects the request.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromResource(resourceName: String, contentType: ContentType, classLoader: ClassLoader = defaultClassLoader): Route =
|
||||
if (!resourceName.endsWith("/"))
|
||||
|
|
@ -104,6 +118,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* Completes GET requests with the content of a file underneath the given directory.
|
||||
* If the file cannot be read the Route rejects the request.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromDirectory(directoryName: String)(implicit resolver: ContentTypeResolver): Route = {
|
||||
val base = withTrailingSlash(directoryName)
|
||||
|
|
@ -120,6 +136,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* 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]`.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def listDirectoryContents(directories: String*)(implicit renderer: DirectoryRenderer): Route =
|
||||
get {
|
||||
|
|
@ -147,6 +165,8 @@ trait FileAndResourceDirectives {
|
|||
|
||||
/**
|
||||
* Same as `getFromBrowseableDirectories` with only one directory.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromBrowseableDirectory(directory: String)(implicit renderer: DirectoryRenderer, resolver: ContentTypeResolver): Route =
|
||||
getFromBrowseableDirectories(directory)
|
||||
|
|
@ -154,6 +174,8 @@ trait FileAndResourceDirectives {
|
|||
/**
|
||||
* Serves the content of the given directories as a file system browser, i.e. files are sent and directories
|
||||
* served as browseable listings.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromBrowseableDirectories(directories: String*)(implicit renderer: DirectoryRenderer, resolver: ContentTypeResolver): Route = {
|
||||
directories.map(getFromDirectory).reduceLeft(_ ~ _) ~ listDirectoryContents(directories: _*)
|
||||
|
|
@ -163,6 +185,8 @@ trait FileAndResourceDirectives {
|
|||
* Same as "getFromDirectory" except that the file is not fetched from the file system but rather from a
|
||||
* "resource directory".
|
||||
* If the requested resource is itself a directory or cannot be found or read the Route rejects the request.
|
||||
*
|
||||
* @group fileandresource
|
||||
*/
|
||||
def getFromResourceDirectory(directoryName: String, classLoader: ClassLoader = defaultClassLoader)(implicit resolver: ContentTypeResolver): Route = {
|
||||
val base = if (directoryName.isEmpty) "" else withTrailingSlash(directoryName)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ import scala.concurrent.Future
|
|||
import scala.util.{ Failure, Success }
|
||||
import akka.stream.scaladsl._
|
||||
|
||||
/**
|
||||
* @groupname fileupload File upload directives
|
||||
* @groupprio fileupload 80
|
||||
*/
|
||||
trait FileUploadDirectives {
|
||||
|
||||
import BasicDirectives._
|
||||
|
|
@ -24,6 +28,8 @@ trait FileUploadDirectives {
|
|||
* If there is an error writing to disk the request will be failed with the thrown exception, if there is no such
|
||||
* field the request will be rejected, if there are multiple file parts with the same name, the first one will be
|
||||
* used and the subsequent ones ignored.
|
||||
*
|
||||
* @group fileupload
|
||||
*/
|
||||
def uploadedFile(fieldName: String): Directive1[(FileInfo, File)] =
|
||||
extractRequestContext.flatMap { ctx ⇒
|
||||
|
|
@ -55,6 +61,8 @@ trait FileUploadDirectives {
|
|||
* for streaming the file contents somewhere. If there is no such field the request will be rejected,
|
||||
* if there are multiple file parts with the same name, the first one will be used and the subsequent
|
||||
* ones ignored.
|
||||
*
|
||||
* @group fileupload
|
||||
*/
|
||||
def fileUpload(fieldName: String): Directive1[(FileInfo, Source[ByteString, Any])] =
|
||||
entity(as[Multipart.FormData]).flatMap { formData ⇒
|
||||
|
|
|
|||
|
|
@ -16,33 +16,47 @@ import scala.collection.immutable
|
|||
import scala.concurrent.Future
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
/**
|
||||
* @groupname form Form field directives
|
||||
* @groupprio form 90
|
||||
*/
|
||||
trait FormFieldDirectives extends ToNameReceptacleEnhancements {
|
||||
import FormFieldDirectives._
|
||||
|
||||
/**
|
||||
* Extracts HTTP form fields from the request as a ``Map[String, String]``.
|
||||
*
|
||||
* @group form
|
||||
*/
|
||||
def formFieldMap: Directive1[Map[String, String]] = _formFieldMap
|
||||
|
||||
/**
|
||||
* Extracts HTTP form fields from the request as a ``Map[String, List[String]]``.
|
||||
*
|
||||
* @group form
|
||||
*/
|
||||
def formFieldMultiMap: Directive1[Map[String, List[String]]] = _formFieldMultiMap
|
||||
|
||||
/**
|
||||
* Extracts HTTP form fields from the request as a ``Seq[(String, String)]``.
|
||||
*
|
||||
* @group form
|
||||
*/
|
||||
def formFieldSeq: Directive1[immutable.Seq[(String, String)]] = _formFieldSeq
|
||||
|
||||
/**
|
||||
* Extracts an HTTP form field from the request.
|
||||
* Rejects the request if the defined form field matcher(s) don't match.
|
||||
*
|
||||
* @group form
|
||||
*/
|
||||
def formField(pdm: FieldMagnet): pdm.Out = pdm()
|
||||
|
||||
/**
|
||||
* Extracts a number of HTTP form field from the request.
|
||||
* Rejects the request if the defined form field matcher(s) don't match.
|
||||
*
|
||||
* @group form
|
||||
*/
|
||||
def formFields(pdm: FieldMagnet): pdm.Out = pdm()
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,17 @@ import akka.http.scaladsl.util.FastFuture._
|
|||
|
||||
// format: OFF
|
||||
|
||||
/**
|
||||
* @groupname future Future directives
|
||||
* @groupprio future 100
|
||||
*/
|
||||
trait FutureDirectives {
|
||||
|
||||
/**
|
||||
* "Unwraps" a `Future[T]` and runs the inner route after future
|
||||
* completion with the future's value as an extraction of type `Try[T]`.
|
||||
*
|
||||
* @group future
|
||||
*/
|
||||
def onComplete[T](future: ⇒ Future[T]): Directive1[Try[T]] =
|
||||
Directive { inner ⇒ ctx ⇒
|
||||
|
|
@ -32,6 +38,8 @@ trait FutureDirectives {
|
|||
* ExceptionHandler.
|
||||
* If type `T` is already a Tuple it is directly expanded into the respective
|
||||
* number of extractions.
|
||||
*
|
||||
* @group future
|
||||
*/
|
||||
def onSuccess(magnet: OnSuccessMagnet): Directive[magnet.Out] = magnet.directive
|
||||
|
||||
|
|
@ -41,6 +49,8 @@ trait FutureDirectives {
|
|||
* If the future succeeds the request is completed using the values marshaller
|
||||
* (This directive therefore requires a marshaller for the futures type to be
|
||||
* implicitly available.)
|
||||
*
|
||||
* @group future
|
||||
*/
|
||||
def completeOrRecoverWith(magnet: CompleteOrRecoverWithMagnet): Directive1[Throwable] = magnet.directive
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ import akka.http.scaladsl.server.util.ClassMagnet
|
|||
import akka.http.scaladsl.model._
|
||||
import akka.http.impl.util._
|
||||
|
||||
/**
|
||||
* @groupname header Header directives
|
||||
* @groupprio header 110
|
||||
*/
|
||||
trait HeaderDirectives {
|
||||
import BasicDirectives._
|
||||
import RouteDirectives._
|
||||
|
|
@ -24,6 +28,8 @@ trait HeaderDirectives {
|
|||
* Extracts an HTTP header value using the given function. If the function result is undefined for all headers the
|
||||
* request is rejected with an empty rejection set. If the given function throws an exception the request is rejected
|
||||
* with a [[akka.http.scaladsl.server.MalformedHeaderRejection]].
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def headerValue[T](f: HttpHeader ⇒ Option[T]): Directive1[T] = {
|
||||
val protectedF: HttpHeader ⇒ Option[Either[Rejection, T]] = header ⇒
|
||||
|
|
@ -42,18 +48,24 @@ trait HeaderDirectives {
|
|||
/**
|
||||
* Extracts an HTTP header value using the given partial function. If the function is undefined for all headers the
|
||||
* request is rejected with an empty rejection set.
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def headerValuePF[T](pf: PartialFunction[HttpHeader, T]): Directive1[T] = headerValue(pf.lift)
|
||||
|
||||
/**
|
||||
* Extracts the value of the first HTTP request header with the given name.
|
||||
* If no header with a matching name is found the request is rejected with a [[akka.http.scaladsl.server.MissingHeaderRejection]].
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def headerValueByName(headerName: Symbol): Directive1[String] = headerValueByName(headerName.name)
|
||||
|
||||
/**
|
||||
* Extracts the value of the HTTP request header with the given name.
|
||||
* If no header with a matching name is found the request is rejected with a [[akka.http.scaladsl.server.MissingHeaderRejection]].
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def headerValueByName(headerName: String): Directive1[String] =
|
||||
headerValue(optionalValue(headerName.toLowerCase)) | reject(MissingHeaderRejection(headerName))
|
||||
|
|
@ -64,6 +76,8 @@ trait HeaderDirectives {
|
|||
*
|
||||
* Custom headers will only be matched by this directive if they extend [[ModeledCustomHeader]]
|
||||
* and provide a companion extending [[ModeledCustomHeaderCompanion]].
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def headerValueByType[T](magnet: HeaderMagnet[T]): Directive1[T] =
|
||||
headerValuePF(magnet.extractPF) | reject(MissingHeaderRejection(magnet.runtimeClass.getSimpleName))
|
||||
|
|
@ -73,6 +87,8 @@ trait HeaderDirectives {
|
|||
* Extracts an optional HTTP header value using the given function.
|
||||
* If the given function throws an exception the request is rejected
|
||||
* with a [[akka.http.scaladsl.server.MalformedHeaderRejection]].
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def optionalHeaderValue[T](f: HttpHeader ⇒ Option[T]): Directive1[Option[T]] =
|
||||
headerValue(f).map(Some(_): Option[T]).recoverPF {
|
||||
|
|
@ -84,18 +100,24 @@ trait HeaderDirectives {
|
|||
* Extracts an optional HTTP header value using the given partial function.
|
||||
* If the given function throws an exception the request is rejected
|
||||
* with a [[akka.http.scaladsl.server.MalformedHeaderRejection]].
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def optionalHeaderValuePF[T](pf: PartialFunction[HttpHeader, T]): Directive1[Option[T]] =
|
||||
optionalHeaderValue(pf.lift)
|
||||
|
||||
/**
|
||||
* Extracts the value of the optional HTTP request header with the given name.
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def optionalHeaderValueByName(headerName: Symbol): Directive1[Option[String]] =
|
||||
optionalHeaderValueByName(headerName.name)
|
||||
|
||||
/**
|
||||
* Extracts the value of the optional HTTP request header with the given name.
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def optionalHeaderValueByName(headerName: String): Directive1[Option[String]] = {
|
||||
val lowerCaseName = headerName.toLowerCase
|
||||
|
|
@ -109,6 +131,8 @@ trait HeaderDirectives {
|
|||
*
|
||||
* Custom headers will only be matched by this directive if they extend [[ModeledCustomHeader]]
|
||||
* and provide a companion extending [[ModeledCustomHeaderCompanion]].
|
||||
*
|
||||
* @group header
|
||||
*/
|
||||
def optionalHeaderValueByType[T <: HttpHeader](magnet: HeaderMagnet[T]): Directive1[Option[T]] =
|
||||
optionalHeaderValuePF(magnet.extractPF)
|
||||
|
|
@ -156,4 +180,4 @@ trait LowPriorityHeaderMagnetImplicits {
|
|||
val runtimeClass: Class[T] = tag.runtimeClass.asInstanceOf[Class[T]]
|
||||
val extractPF: PartialFunction[Any, T] = { case x: T ⇒ x }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,23 +8,33 @@ package directives
|
|||
import scala.util.matching.Regex
|
||||
import akka.http.impl.util._
|
||||
|
||||
/**
|
||||
* @groupname host Host directives
|
||||
* @groupprio host 110
|
||||
*/
|
||||
trait HostDirectives {
|
||||
import BasicDirectives._
|
||||
import RouteDirectives._
|
||||
|
||||
/**
|
||||
* Extracts the hostname part of the Host request header value.
|
||||
*
|
||||
* @group host
|
||||
*/
|
||||
def extractHost: Directive1[String] = HostDirectives._extractHost
|
||||
|
||||
/**
|
||||
* Rejects all requests with a host name different from the given ones.
|
||||
*
|
||||
* @group host
|
||||
*/
|
||||
def host(hostNames: String*): Directive0 = host(hostNames.contains(_))
|
||||
|
||||
//#require-host
|
||||
/**
|
||||
* Rejects all requests for whose host name the given predicate function returns false.
|
||||
*
|
||||
* @group host
|
||||
*/
|
||||
def host(predicate: String ⇒ Boolean): Directive0 = extractHost.require(predicate)
|
||||
//#
|
||||
|
|
@ -34,6 +44,8 @@ trait HostDirectives {
|
|||
* For all matching requests the prefix string matching the regex is extracted and passed to the inner route.
|
||||
* If the regex contains a capturing group only the string matched by this group is extracted.
|
||||
* If the regex contains more than one capturing group an IllegalArgumentException is thrown.
|
||||
*
|
||||
* @group host
|
||||
*/
|
||||
def host(regex: Regex): Directive1[String] = {
|
||||
def forFunc(regexMatch: String ⇒ Option[String]): Directive1[String] = {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
|||
import akka.http.scaladsl.unmarshalling.{ Unmarshaller, FromRequestUnmarshaller }
|
||||
import akka.http.impl.util._
|
||||
|
||||
/**
|
||||
* @groupname marshalling Marshalling directives
|
||||
* @groupprio marshalling 120
|
||||
*/
|
||||
trait MarshallingDirectives {
|
||||
import BasicDirectives._
|
||||
import FutureDirectives._
|
||||
|
|
@ -20,6 +24,8 @@ trait MarshallingDirectives {
|
|||
* Unmarshalls the requests entity to the given type passes it to its inner Route.
|
||||
* If there is a problem with unmarshalling the request is rejected with the [[Rejection]]
|
||||
* produced by the unmarshaller.
|
||||
*
|
||||
* @group marshalling
|
||||
*/
|
||||
def entity[T](um: FromRequestUnmarshaller[T]): Directive1[T] =
|
||||
extractRequestContext.flatMap[Tuple1[T]] { ctx ⇒
|
||||
|
|
@ -36,12 +42,16 @@ trait MarshallingDirectives {
|
|||
|
||||
/**
|
||||
* Returns the in-scope [[FromRequestUnmarshaller]] for the given type.
|
||||
*
|
||||
* @group marshalling
|
||||
*/
|
||||
def as[T](implicit um: FromRequestUnmarshaller[T]) = um
|
||||
|
||||
/**
|
||||
* Uses the marshaller for the given type to produce a completion function that is passed to its inner function.
|
||||
* You can use it do decouple marshaller resolution from request completion.
|
||||
*
|
||||
* @group marshalling
|
||||
*/
|
||||
def completeWith[T](marshaller: ToResponseMarshaller[T])(inner: (T ⇒ Unit) ⇒ Unit): Route =
|
||||
extractRequestContext { ctx ⇒
|
||||
|
|
@ -55,12 +65,16 @@ trait MarshallingDirectives {
|
|||
|
||||
/**
|
||||
* Returns the in-scope Marshaller for the given type.
|
||||
*
|
||||
* @group marshalling
|
||||
*/
|
||||
def instanceOf[T](implicit m: ToResponseMarshaller[T]): ToResponseMarshaller[T] = m
|
||||
|
||||
/**
|
||||
* Completes the request using the given function. The input to the function is produced with the in-scope
|
||||
* entity unmarshaller and the result value of the function is marshalled with the in-scope marshaller.
|
||||
*
|
||||
* @group marshalling
|
||||
*/
|
||||
def handleWith[A, B](f: A ⇒ B)(implicit um: FromRequestUnmarshaller[A], m: ToResponseMarshaller[B]): Route =
|
||||
entity(um) { a ⇒ complete(f(a)) }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ package directives
|
|||
import akka.http.scaladsl.model.{ StatusCodes, HttpMethod }
|
||||
import akka.http.scaladsl.model.HttpMethods._
|
||||
|
||||
/**
|
||||
* @groupname method Method directives
|
||||
* @groupprio method 130
|
||||
*/
|
||||
trait MethodDirectives {
|
||||
import BasicDirectives._
|
||||
import RouteDirectives._
|
||||
|
|
@ -16,47 +20,65 @@ trait MethodDirectives {
|
|||
|
||||
/**
|
||||
* Rejects all non-DELETE requests.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def delete: Directive0 = _delete
|
||||
|
||||
/**
|
||||
* Rejects all non-GET requests.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def get: Directive0 = _get
|
||||
|
||||
/**
|
||||
* Rejects all non-HEAD requests.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def head: Directive0 = _head
|
||||
|
||||
/**
|
||||
* Rejects all non-OPTIONS requests.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def options: Directive0 = _options
|
||||
|
||||
/**
|
||||
* Rejects all non-PATCH requests.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def patch: Directive0 = _patch
|
||||
|
||||
/**
|
||||
* Rejects all non-POST requests.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def post: Directive0 = _post
|
||||
|
||||
/**
|
||||
* Rejects all non-PUT requests.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def put: Directive0 = _put
|
||||
|
||||
/**
|
||||
* Extracts the request method.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def extractMethod: Directive1[HttpMethod] = _extractMethod
|
||||
|
||||
//#method
|
||||
/**
|
||||
* Rejects all requests whose HTTP method does not match the given one.
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def method(httpMethod: HttpMethod): Directive0 =
|
||||
extractMethod.flatMap[Unit] {
|
||||
|
|
@ -73,6 +95,8 @@ trait MethodDirectives {
|
|||
* This directive is useful for:
|
||||
* - Use in combination with JSONP (JSONP only supports GET)
|
||||
* - Supporting older browsers that lack support for certain HTTP methods. E.g. IE8 does not support PATCH
|
||||
*
|
||||
* @group method
|
||||
*/
|
||||
def overrideMethodWithParameter(paramName: String): Directive0 =
|
||||
parameter(paramName?) flatMap {
|
||||
|
|
|
|||
|
|
@ -8,12 +8,18 @@ package directives
|
|||
import akka.http.scaladsl.model._
|
||||
import headers._
|
||||
|
||||
/**
|
||||
* @groupname misc Miscellaneous directives
|
||||
* @groupprio misc 140
|
||||
*/
|
||||
trait MiscDirectives {
|
||||
import RouteDirectives._
|
||||
|
||||
/**
|
||||
* Checks the given condition before running its inner route.
|
||||
* If the condition fails the route is rejected with a [[ValidationRejection]].
|
||||
*
|
||||
* @group misc
|
||||
*/
|
||||
def validate(check: ⇒ Boolean, errorMsg: String): Directive0 =
|
||||
Directive { inner ⇒ if (check) inner(()) else reject(ValidationRejection(errorMsg)) }
|
||||
|
|
@ -21,17 +27,23 @@ trait MiscDirectives {
|
|||
/**
|
||||
* Extracts the client's IP from either the X-Forwarded-For, Remote-Address or X-Real-IP header
|
||||
* (in that order of priority).
|
||||
*
|
||||
* @group misc
|
||||
*/
|
||||
def extractClientIP: Directive1[RemoteAddress] = MiscDirectives._extractClientIP
|
||||
|
||||
/**
|
||||
* Rejects if the request entity is non-empty.
|
||||
*
|
||||
* @group misc
|
||||
*/
|
||||
def requestEntityEmpty: Directive0 = MiscDirectives._requestEntityEmpty
|
||||
|
||||
/**
|
||||
* Rejects with a [[RequestEntityExpectedRejection]] if the request entity is empty.
|
||||
* Non-empty requests are passed on unchanged to the inner route.
|
||||
*
|
||||
* @group misc
|
||||
*/
|
||||
def requestEntityPresent: Directive0 = MiscDirectives._requestEntityPresent
|
||||
|
||||
|
|
@ -39,6 +51,8 @@ trait MiscDirectives {
|
|||
* Converts responses with an empty entity into (empty) rejections.
|
||||
* This way you can, for example, have the marshalling of a ''None'' option
|
||||
* be treated as if the request could not be matched.
|
||||
*
|
||||
* @group misc
|
||||
*/
|
||||
def rejectEmptyResponse: Directive0 = MiscDirectives._rejectEmptyResponse
|
||||
|
||||
|
|
@ -50,6 +64,8 @@ trait MiscDirectives {
|
|||
* If there are several best language alternatives that the client
|
||||
* has equal preference for (even if this preference is zero!)
|
||||
* the order of the arguments is used as a tie breaker (First one wins).
|
||||
*
|
||||
* @group misc
|
||||
*/
|
||||
def selectPreferredLanguage(first: Language, more: Language*): Directive1[Language] =
|
||||
BasicDirectives.extractRequest.map { request ⇒
|
||||
|
|
|
|||
|
|
@ -11,21 +11,31 @@ import scala.util.{ Failure, Success }
|
|||
import akka.http.scaladsl.common._
|
||||
import akka.http.impl.util._
|
||||
|
||||
/**
|
||||
* @groupname param Parameter directives
|
||||
* @groupprio param 150
|
||||
*/
|
||||
trait ParameterDirectives extends ToNameReceptacleEnhancements {
|
||||
import ParameterDirectives._
|
||||
|
||||
/**
|
||||
* Extracts the request's query parameters as a `Map[String, String]`.
|
||||
*
|
||||
* @group param
|
||||
*/
|
||||
def parameterMap: Directive1[Map[String, String]] = _parameterMap
|
||||
|
||||
/**
|
||||
* Extracts the request's query parameters as a `Map[String, List[String]]`.
|
||||
*
|
||||
* @group param
|
||||
*/
|
||||
def parameterMultiMap: Directive1[Map[String, List[String]]] = _parameterMultiMap
|
||||
|
||||
/**
|
||||
* Extracts the request's query parameters as a `Seq[(String, String)]`.
|
||||
*
|
||||
* @group param
|
||||
*/
|
||||
def parameterSeq: Directive1[immutable.Seq[(String, String)]] = _parameterSeq
|
||||
|
||||
|
|
@ -37,6 +47,8 @@ trait ParameterDirectives extends ToNameReceptacleEnhancements {
|
|||
* "too many arguments for method parameter" or "type mismatch" error.
|
||||
*
|
||||
* As a workaround add an `import ParameterDirectives.ParamMagnet` or use Scala 2.11.x.
|
||||
*
|
||||
* @group param
|
||||
*/
|
||||
def parameter(pdm: ParamMagnet): pdm.Out = pdm()
|
||||
|
||||
|
|
@ -48,6 +60,8 @@ trait ParameterDirectives extends ToNameReceptacleEnhancements {
|
|||
* "too many arguments for method parameters" or "type mismatch" error.
|
||||
*
|
||||
* As a workaround add an `import ParameterDirectives.ParamMagnet` or use Scala 2.11.x.
|
||||
*
|
||||
* @group param
|
||||
*/
|
||||
def parameters(pdm: ParamMagnet): pdm.Out = pdm()
|
||||
|
||||
|
|
@ -170,4 +184,4 @@ object ParameterDirectives extends ParameterDirectives {
|
|||
at[Directive[TA], P] { (a, t) ⇒ a & pdef(t) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ import akka.http.scaladsl.common.ToNameReceptacleEnhancements
|
|||
import akka.http.scaladsl.model.StatusCodes
|
||||
import akka.http.scaladsl.model.Uri.Path
|
||||
|
||||
/**
|
||||
* @groupname path Path directives
|
||||
* @groupprio path 170
|
||||
*/
|
||||
trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction with ToNameReceptacleEnhancements {
|
||||
import BasicDirectives._
|
||||
import RouteDirectives._
|
||||
|
|
@ -18,6 +22,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* Applies the given [[PathMatcher]] to the remaining unmatched path after consuming a leading slash.
|
||||
* The matcher has to match the remaining path completely.
|
||||
* If matched the value extracted by the [[PathMatcher]] is extracted on the directive level.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def path[L](pm: PathMatcher[L]): Directive[L] = pathPrefix(pm ~ PathEnd)
|
||||
|
||||
|
|
@ -25,6 +31,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* Applies the given [[PathMatcher]] to a prefix of the remaining unmatched path after consuming a leading slash.
|
||||
* The matcher has to match a prefix of the remaining path.
|
||||
* If matched the value extracted by the PathMatcher is extracted on the directive level.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def pathPrefix[L](pm: PathMatcher[L]): Directive[L] = rawPathPrefix(Slash ~ pm)
|
||||
|
||||
|
|
@ -33,6 +41,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* [[RequestContext]] (i.e. without implicitly consuming a leading slash).
|
||||
* The matcher has to match a prefix of the remaining path.
|
||||
* If matched the value extracted by the PathMatcher is extracted on the directive level.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def rawPathPrefix[L](pm: PathMatcher[L]): Directive[L] = {
|
||||
implicit val LIsTuple = pm.ev
|
||||
|
|
@ -45,6 +55,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
/**
|
||||
* Checks whether the unmatchedPath of the [[RequestContext]] has a prefix matched by the
|
||||
* given PathMatcher. In analogy to the `pathPrefix` directive a leading slash is implied.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def pathPrefixTest[L](pm: PathMatcher[L]): Directive[L] = rawPathPrefixTest(Slash ~ pm)
|
||||
|
||||
|
|
@ -52,6 +64,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* Checks whether the unmatchedPath of the [[RequestContext]] has a prefix matched by the
|
||||
* given PathMatcher. However, as opposed to the `pathPrefix` directive the matched path is not
|
||||
* actually "consumed".
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def rawPathPrefixTest[L](pm: PathMatcher[L]): Directive[L] = {
|
||||
implicit val LIsTuple = pm.ev
|
||||
|
|
@ -66,6 +80,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* If matched the value extracted by the [[PathMatcher]] is extracted and the matched parts of the path are consumed.
|
||||
* Note that, 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`!
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def pathSuffix[L](pm: PathMatcher[L]): Directive[L] = {
|
||||
implicit val LIsTuple = pm.ev
|
||||
|
|
@ -81,6 +97,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* actually "consumed".
|
||||
* Note that, 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`!
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def pathSuffixTest[L](pm: PathMatcher[L]): Directive[L] = {
|
||||
implicit val LIsTuple = pm.ev
|
||||
|
|
@ -94,6 +112,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* Rejects the request if the unmatchedPath of the [[RequestContext]] is non-empty,
|
||||
* or said differently: only passes on the request to its inner route if the request path
|
||||
* has been matched completely.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def pathEnd: Directive0 = rawPathPrefix(PathEnd)
|
||||
|
||||
|
|
@ -124,12 +144,16 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
*
|
||||
* For further information, refer to:
|
||||
* @see [[http://googlewebmastercentral.blogspot.de/2010/04/to-slash-or-not-to-slash.html]]
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def pathEndOrSingleSlash: Directive0 = rawPathPrefix(Slash.? ~ PathEnd)
|
||||
|
||||
/**
|
||||
* Only passes on the request to its inner route if the request path
|
||||
* consists of exactly one remaining slash.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def pathSingleSlash: Directive0 = pathPrefix(PathEnd)
|
||||
|
||||
|
|
@ -137,6 +161,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* If the request path doesn't end with a slash, redirect to the same uri with trailing slash in the path.
|
||||
*
|
||||
* '''Caveat''': [[path]] without trailing slash and [[pathEnd]] directives will not match inside of this directive.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def redirectToTrailingSlashIfMissing(redirectionType: StatusCodes.Redirection): Directive0 =
|
||||
extractUri.flatMap { uri ⇒
|
||||
|
|
@ -152,6 +178,8 @@ trait PathDirectives extends PathMatchers with ImplicitPathMatcherConstruction w
|
|||
* If the request path ends with a slash, redirect to the same uri without trailing slash in the path.
|
||||
*
|
||||
* '''Caveat''': [[pathSingleSlash]] directive will not match inside of this directive.
|
||||
*
|
||||
* @group path
|
||||
*/
|
||||
def redirectToNoTrailingSlashIfPresent(redirectionType: StatusCodes.Redirection): Directive0 =
|
||||
extractUri.flatMap { uri ⇒
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ import akka.util.ByteString
|
|||
import akka.stream.SourceShape
|
||||
import akka.stream.OverflowStrategy
|
||||
|
||||
/**
|
||||
* @groupname range Range directives
|
||||
* @groupprio range 180
|
||||
*/
|
||||
trait RangeDirectives {
|
||||
import akka.http.scaladsl.server.directives.BasicDirectives._
|
||||
import akka.http.scaladsl.server.directives.RouteDirectives._
|
||||
|
|
@ -33,6 +37,8 @@ trait RangeDirectives {
|
|||
* on a higher level in your route structure in order to function correctly.
|
||||
*
|
||||
* @see [[https://tools.ietf.org/html/rfc7233]]
|
||||
*
|
||||
* @group range
|
||||
*/
|
||||
def withRangeSupport: Directive0 =
|
||||
extractRequestContext.flatMap { ctx ⇒
|
||||
|
|
|
|||
|
|
@ -4,28 +4,40 @@ package directives
|
|||
import akka.http.scaladsl.model._
|
||||
import scala.collection.immutable
|
||||
|
||||
/**
|
||||
* @groupname response Response directives
|
||||
* @groupprio response 190
|
||||
*/
|
||||
trait RespondWithDirectives {
|
||||
import BasicDirectives._
|
||||
|
||||
/**
|
||||
* Unconditionally adds the given response header to all HTTP responses of its inner Route.
|
||||
*
|
||||
* @group response
|
||||
*/
|
||||
def respondWithHeader(responseHeader: HttpHeader): Directive0 = respondWithHeaders(responseHeader)
|
||||
|
||||
/**
|
||||
* Adds the given response header to all HTTP responses of its inner Route,
|
||||
* if the response from the inner Route doesn't already contain a header with the same name.
|
||||
*
|
||||
* @group response
|
||||
*/
|
||||
def respondWithDefaultHeader(responseHeader: HttpHeader): Directive0 = respondWithDefaultHeaders(responseHeader)
|
||||
|
||||
/**
|
||||
* Unconditionally adds the given response headers to all HTTP responses of its inner Route.
|
||||
*
|
||||
* @group response
|
||||
*/
|
||||
def respondWithHeaders(responseHeaders: HttpHeader*): Directive0 =
|
||||
respondWithHeaders(responseHeaders.toList)
|
||||
|
||||
/**
|
||||
* Unconditionally adds the given response headers to all HTTP responses of its inner Route.
|
||||
*
|
||||
* @group response
|
||||
*/
|
||||
def respondWithHeaders(responseHeaders: immutable.Seq[HttpHeader]): Directive0 =
|
||||
mapResponseHeaders(responseHeaders ++ _)
|
||||
|
|
@ -33,12 +45,17 @@ trait RespondWithDirectives {
|
|||
/**
|
||||
* Adds the given response headers to all HTTP responses of its inner Route,
|
||||
* if a header already exists it is not added again.
|
||||
*
|
||||
* @group response
|
||||
*/
|
||||
def respondWithDefaultHeaders(responseHeaders: HttpHeader*): Directive0 =
|
||||
respondWithDefaultHeaders(responseHeaders.toList)
|
||||
|
||||
/* Adds the given response headers to all HTTP responses of its inner Route,
|
||||
/**
|
||||
* Adds the given response headers to all HTTP responses of its inner Route,
|
||||
* if a header already exists it is not added again.
|
||||
*
|
||||
* @group response
|
||||
*/
|
||||
def respondWithDefaultHeaders(responseHeaders: immutable.Seq[HttpHeader]): Directive0 =
|
||||
mapResponse(_.withDefaultHeaders(responseHeaders))
|
||||
|
|
|
|||
|
|
@ -9,21 +9,31 @@ import akka.http.scaladsl.marshalling.ToResponseMarshallable
|
|||
import akka.http.scaladsl.model._
|
||||
import StatusCodes._
|
||||
|
||||
/**
|
||||
* @groupname route Route directives
|
||||
* @groupprio route 200
|
||||
*/
|
||||
trait RouteDirectives {
|
||||
|
||||
/**
|
||||
* Rejects the request with an empty set of rejections.
|
||||
*
|
||||
* @group route
|
||||
*/
|
||||
def reject: StandardRoute = RouteDirectives._reject
|
||||
|
||||
/**
|
||||
* Rejects the request with the given rejections.
|
||||
*
|
||||
* @group route
|
||||
*/
|
||||
def reject(rejections: Rejection*): StandardRoute =
|
||||
StandardRoute(_.reject(rejections: _*))
|
||||
|
||||
/**
|
||||
* Completes the request with redirection response of the given type to the given URI.
|
||||
*
|
||||
* @group route
|
||||
*/
|
||||
def redirect(uri: Uri, redirectionType: Redirection): StandardRoute =
|
||||
StandardRoute {
|
||||
|
|
@ -42,6 +52,8 @@ trait RouteDirectives {
|
|||
|
||||
/**
|
||||
* Completes the request using the given arguments.
|
||||
*
|
||||
* @group route
|
||||
*/
|
||||
def complete(m: ⇒ ToResponseMarshallable): StandardRoute =
|
||||
StandardRoute(_.complete(m))
|
||||
|
|
@ -49,6 +61,8 @@ trait RouteDirectives {
|
|||
/**
|
||||
* Bubbles the given error up the response chain, where it is dealt with by the closest `handleExceptions`
|
||||
* directive and its ExceptionHandler.
|
||||
*
|
||||
* @group route
|
||||
*/
|
||||
def failWith(error: Throwable): StandardRoute =
|
||||
StandardRoute(_.fail(error))
|
||||
|
|
|
|||
|
|
@ -5,16 +5,24 @@
|
|||
package akka.http.scaladsl.server
|
||||
package directives
|
||||
|
||||
/**
|
||||
* @groupname scheme Scheme directives
|
||||
* @groupprio scheme 210
|
||||
*/
|
||||
trait SchemeDirectives {
|
||||
import BasicDirectives._
|
||||
|
||||
/**
|
||||
* Extracts the Uri scheme from the request.
|
||||
*
|
||||
* @group scheme
|
||||
*/
|
||||
def extractScheme: Directive1[String] = SchemeDirectives._extractScheme
|
||||
|
||||
/**
|
||||
* Rejects all requests whose Uri scheme does not match the given one.
|
||||
*
|
||||
* @group scheme
|
||||
*/
|
||||
def scheme(name: String): Directive0 =
|
||||
extractScheme.require(_ == name, SchemeRejection(name)) & cancelRejections(classOf[SchemeRejection])
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ import scala.util.{Try, Success}
|
|||
* and [[Authorization]]. Most prominently, HTTP Basic authentication as defined in RFC 2617.
|
||||
*
|
||||
* See: <a href="https://www.ietf.org/rfc/rfc2617.txt">RFC 2617</a>.
|
||||
*
|
||||
* @groupname security Security directives
|
||||
* @groupprio security 220
|
||||
*/
|
||||
trait SecurityDirectives {
|
||||
import BasicDirectives._
|
||||
|
|
@ -31,25 +34,41 @@ trait SecurityDirectives {
|
|||
/**
|
||||
* The result of an HTTP authentication attempt is either the user object or
|
||||
* an HttpChallenge to present to the browser.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
type AuthenticationResult[+T] = Either[HttpChallenge, T]
|
||||
//#authentication-result
|
||||
|
||||
//#authenticator
|
||||
/**
|
||||
* @group security
|
||||
*/
|
||||
type Authenticator[T] = Credentials ⇒ Option[T]
|
||||
//#authenticator
|
||||
//#async-authenticator
|
||||
/**
|
||||
* @group security
|
||||
*/
|
||||
type AsyncAuthenticator[T] = Credentials ⇒ Future[Option[T]]
|
||||
//#async-authenticator
|
||||
//#authenticator-pf
|
||||
/**
|
||||
* @group security
|
||||
*/
|
||||
type AuthenticatorPF[T] = PartialFunction[Credentials, T]
|
||||
//#authenticator-pf
|
||||
//#async-authenticator-pf
|
||||
/**
|
||||
* @group security
|
||||
*/
|
||||
type AsyncAuthenticatorPF[T] = PartialFunction[Credentials, Future[T]]
|
||||
//#async-authenticator-pf
|
||||
|
||||
/**
|
||||
* Extracts the potentially present [[HttpCredentials]] provided with the request's [[Authorization]] header.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def extractCredentials: Directive1[Option[HttpCredentials]] =
|
||||
optionalHeaderValueByType[Authorization](()).map(_.map(_.credentials))
|
||||
|
|
@ -58,6 +77,8 @@ trait SecurityDirectives {
|
|||
* Wraps the inner route with Http Basic authentication support using a given `Authenticator[T]`.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateBasic[T](realm: String, authenticator: Authenticator[T]): AuthenticationDirective[T] =
|
||||
authenticateBasicAsync(realm, cred ⇒ FastFuture.successful(authenticator(cred)))
|
||||
|
|
@ -66,6 +87,8 @@ trait SecurityDirectives {
|
|||
* Wraps the inner route with Http Basic authentication support.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateBasicAsync[T](realm: String, authenticator: AsyncAuthenticator[T]): AuthenticationDirective[T] =
|
||||
extractExecutionContext.flatMap { implicit ec ⇒
|
||||
|
|
@ -81,6 +104,8 @@ trait SecurityDirectives {
|
|||
* A directive that wraps the inner route with Http Basic authentication support.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateBasicPF[T](realm: String, authenticator: AuthenticatorPF[T]): AuthenticationDirective[T] =
|
||||
authenticateBasic(realm, authenticator.lift)
|
||||
|
|
@ -89,6 +114,8 @@ trait SecurityDirectives {
|
|||
* A directive that wraps the inner route with Http Basic authentication support.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateBasicPFAsync[T](realm: String, authenticator: AsyncAuthenticatorPF[T]): AuthenticationDirective[T] =
|
||||
extractExecutionContext.flatMap { implicit ec ⇒
|
||||
|
|
@ -101,6 +128,8 @@ trait SecurityDirectives {
|
|||
* A directive that wraps the inner route with OAuth2 Bearer Token authentication support.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateOAuth2[T](realm: String, authenticator: Authenticator[T]): AuthenticationDirective[T] =
|
||||
authenticateOAuth2Async(realm, cred ⇒ FastFuture.successful(authenticator(cred)))
|
||||
|
|
@ -109,6 +138,8 @@ trait SecurityDirectives {
|
|||
* A directive that wraps the inner route with OAuth2 Bearer Token authentication support.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateOAuth2Async[T](realm: String, authenticator: AsyncAuthenticator[T]): AuthenticationDirective[T] =
|
||||
extractExecutionContext.flatMap { implicit ec ⇒
|
||||
|
|
@ -124,6 +155,8 @@ trait SecurityDirectives {
|
|||
* A directive that wraps the inner route with OAuth2 Bearer Token authentication support.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateOAuth2PF[T](realm: String, authenticator: AuthenticatorPF[T]): AuthenticationDirective[T] =
|
||||
authenticateOAuth2(realm, authenticator.lift)
|
||||
|
|
@ -132,6 +165,8 @@ trait SecurityDirectives {
|
|||
* A directive that wraps the inner route with OAuth2 Bearer Token authentication support.
|
||||
* The given authenticator determines whether the credentials in the request are valid
|
||||
* and, if so, which user object to supply to the inner route.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateOAuth2PFAsync[T](realm: String, authenticator: AsyncAuthenticatorPF[T]): AuthenticationDirective[T] =
|
||||
extractExecutionContext.flatMap { implicit ec ⇒
|
||||
|
|
@ -146,6 +181,7 @@ trait SecurityDirectives {
|
|||
* to the inner route. If the function returns `Left(challenge)` the request is rejected with an
|
||||
* [[AuthenticationFailedRejection]] that contains this challenge to be added to the response.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateOrRejectWithChallenge[T](authenticator: Option[HttpCredentials] ⇒ Future[AuthenticationResult[T]]): AuthenticationDirective[T] =
|
||||
extractExecutionContext.flatMap { implicit ec ⇒
|
||||
|
|
@ -162,6 +198,8 @@ trait SecurityDirectives {
|
|||
/**
|
||||
* Lifts an authenticator function into a directive. Same as `authenticateOrRejectWithChallenge`
|
||||
* but only applies the authenticator function with a certain type of credentials.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authenticateOrRejectWithChallenge[C <: HttpCredentials: ClassTag, T](
|
||||
authenticator: Option[C] ⇒ Future[AuthenticationResult[T]]): AuthenticationDirective[T] =
|
||||
|
|
@ -170,12 +208,16 @@ trait SecurityDirectives {
|
|||
/**
|
||||
* Applies the given authorization check to the request.
|
||||
* If the check fails the route is rejected with an [[AuthorizationFailedRejection]].
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authorize(check: ⇒ Boolean): Directive0 = authorize(_ ⇒ check)
|
||||
|
||||
/**
|
||||
* Applies the given authorization check to the request.
|
||||
* If the check fails the route is rejected with an [[AuthorizationFailedRejection]].
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authorize(check: RequestContext ⇒ Boolean): Directive0 =
|
||||
authorizeAsync(ctx => Future.successful(check(ctx)))
|
||||
|
|
@ -184,6 +226,8 @@ trait SecurityDirectives {
|
|||
* Asynchronous version of [[authorize]].
|
||||
* If the [[Future]] fails or is completed with `false`
|
||||
* authorization fails and the route is rejected with an [[AuthorizationFailedRejection]].
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authorizeAsync(check: ⇒ Future[Boolean]): Directive0 =
|
||||
authorizeAsync(ctx => check)
|
||||
|
|
@ -192,6 +236,8 @@ trait SecurityDirectives {
|
|||
* Asynchronous version of [[authorize]].
|
||||
* If the [[Future]] fails or is completed with `false`
|
||||
* authorization fails and the route is rejected with an [[AuthorizationFailedRejection]].
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def authorizeAsync(check: RequestContext ⇒ Future[Boolean]): Directive0 =
|
||||
extractExecutionContext.flatMap { implicit ec ⇒
|
||||
|
|
@ -205,6 +251,8 @@ trait SecurityDirectives {
|
|||
|
||||
/**
|
||||
* Creates a `Basic` [[HttpChallenge]] for the given realm.
|
||||
*
|
||||
* @group security
|
||||
*/
|
||||
def challengeFor(realm: String) = HttpChallenge(scheme = "Basic", realm = realm, params = Map.empty)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,15 @@ import akka.http.scaladsl.server.{ Directive, Directive0 }
|
|||
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
/**
|
||||
* @groupname timeout Timeout directives
|
||||
* @groupprio timeout 160
|
||||
*/
|
||||
trait TimeoutDirectives {
|
||||
|
||||
/**
|
||||
* @group timeout
|
||||
*/
|
||||
def withoutRequestTimeout: Directive0 =
|
||||
withRequestTimeout(Duration.Inf)
|
||||
|
||||
|
|
@ -20,6 +27,8 @@ trait TimeoutDirectives {
|
|||
*
|
||||
* Due to the inherent raciness it is not guaranteed that the update will be applied before
|
||||
* the previously set timeout has expired!
|
||||
*
|
||||
* @group timeout
|
||||
*/
|
||||
def withRequestTimeout(timeout: Duration): Directive0 =
|
||||
withRequestTimeout(timeout, None)
|
||||
|
|
@ -31,6 +40,8 @@ trait TimeoutDirectives {
|
|||
* the previously set timeout has expired!
|
||||
*
|
||||
* @param handler optional custom "timeout response" function. If left None, the default timeout HttpResponse will be used.
|
||||
*
|
||||
* @group timeout
|
||||
*/
|
||||
def withRequestTimeout(timeout: Duration, handler: HttpRequest ⇒ HttpResponse): Directive0 =
|
||||
withRequestTimeout(timeout, Some(handler))
|
||||
|
|
@ -42,6 +53,8 @@ trait TimeoutDirectives {
|
|||
* the previously set timeout has expired!
|
||||
*
|
||||
* @param handler optional custom "timeout response" function. If left None, the default timeout HttpResponse will be used.
|
||||
*
|
||||
* @group timeout
|
||||
*/
|
||||
def withRequestTimeout(timeout: Duration, handler: Option[HttpRequest ⇒ HttpResponse]): Directive0 =
|
||||
Directive { inner ⇒
|
||||
|
|
@ -63,6 +76,8 @@ trait TimeoutDirectives {
|
|||
*
|
||||
* Due to the inherent raciness it is not guaranteed that the update will be applied before
|
||||
* the previously set timeout has expired!
|
||||
*
|
||||
* @group timeout
|
||||
*/
|
||||
def withRequestTimeoutResponse(handler: HttpRequest ⇒ HttpResponse): Directive0 =
|
||||
Directive { inner ⇒
|
||||
|
|
@ -76,4 +91,4 @@ trait TimeoutDirectives {
|
|||
|
||||
}
|
||||
|
||||
object TimeoutDirectives extends TimeoutDirectives
|
||||
object TimeoutDirectives extends TimeoutDirectives
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ import scala.collection.immutable
|
|||
import akka.http.scaladsl.model.ws.{ UpgradeToWebSocket, Message }
|
||||
import akka.stream.scaladsl.Flow
|
||||
|
||||
/**
|
||||
* @groupname websocket WebSocket directives
|
||||
* @groupprio websocket 230
|
||||
*/
|
||||
trait WebSocketDirectives {
|
||||
import RouteDirectives._
|
||||
import HeaderDirectives._
|
||||
|
|
@ -17,6 +21,8 @@ trait WebSocketDirectives {
|
|||
|
||||
/**
|
||||
* Extract the [[UpgradeToWebSocket]] header if existent. Rejects with an [[ExpectedWebSocketRequestRejection]], otherwise.
|
||||
*
|
||||
* @group websocket
|
||||
*/
|
||||
def extractUpgradeToWebSocket: Directive1[UpgradeToWebSocket] =
|
||||
optionalHeaderValueByType[UpgradeToWebSocket](()).flatMap {
|
||||
|
|
@ -27,12 +33,16 @@ trait WebSocketDirectives {
|
|||
/**
|
||||
* Extract the list of WebSocket subprotocols as offered by the client in the [[Sec-WebSocket-Protocol]] header if
|
||||
* this is a WebSocket request. Rejects with an [[ExpectedWebSocketRequestRejection]], otherwise.
|
||||
*
|
||||
* @group websocket
|
||||
*/
|
||||
def extractOfferedWsProtocols: Directive1[immutable.Seq[String]] = extractUpgradeToWebSocket.map(_.requestedProtocols)
|
||||
|
||||
/**
|
||||
* Handles WebSocket requests with the given handler and rejects other requests with an
|
||||
* [[ExpectedWebSocketRequestRejection]].
|
||||
*
|
||||
* @group websocket
|
||||
*/
|
||||
def handleWebSocketMessages(handler: Flow[Message, Message, Any]): Route =
|
||||
handleWebSocketMessagesForOptionalProtocol(handler, None)
|
||||
|
|
@ -40,6 +50,8 @@ trait WebSocketDirectives {
|
|||
/**
|
||||
* Handles WebSocket requests with the given handler if the given subprotocol is offered in the request and
|
||||
* rejects other requests with an [[ExpectedWebSocketRequestRejection]] or an [[UnsupportedWebSocketSubprotocolRejection]].
|
||||
*
|
||||
* @group websocket
|
||||
*/
|
||||
def handleWebSocketMessagesForProtocol(handler: Flow[Message, Message, Any], subprotocol: String): Route =
|
||||
handleWebSocketMessagesForOptionalProtocol(handler, Some(subprotocol))
|
||||
|
|
@ -54,6 +66,8 @@ trait WebSocketDirectives {
|
|||
* the request is rejected with an [[UnsupportedWebSocketSubprotocolRejection]] rejection.
|
||||
*
|
||||
* To support several subprotocols you may chain several `handleWebSocketMessage` Routes.
|
||||
*
|
||||
* @group websocket
|
||||
*/
|
||||
def handleWebSocketMessagesForOptionalProtocol(handler: Flow[Message, Message, Any], subprotocol: Option[String]): Route =
|
||||
extractUpgradeToWebSocket { upgrade ⇒
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ object Scaladoc extends AutoPlugin {
|
|||
|
||||
def scaladocOptions(ver: String, base: File): List[String] = {
|
||||
val urlString = GitHub.url(ver) + "/€{FILE_PATH}.scala"
|
||||
val opts = List("-implicits", "-doc-source-url", urlString, "-sourcepath", base.getAbsolutePath)
|
||||
val opts = List("-implicits", "-groups", "-doc-source-url", urlString, "-sourcepath", base.getAbsolutePath)
|
||||
CliOptions.scaladocDiagramsEnabled.ifTrue("-diagrams").toList ::: opts
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue