+htp #16438 add Java-side rejection handling support
This commit is contained in:
parent
96ef8875c8
commit
db1be86b02
11 changed files with 427 additions and 2 deletions
|
|
@ -56,4 +56,97 @@ public class ExecutionDirectivesTest extends JUnitRouteTest {
|
||||||
.assertStatusCode(400)
|
.assertStatusCode(400)
|
||||||
.assertEntity("Congratulations you provoked a division by zero!");
|
.assertEntity("Congratulations you provoked a division by zero!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandleMethodRejection() {
|
||||||
|
RejectionHandler rejectionHandler =
|
||||||
|
new RejectionHandler() {
|
||||||
|
@Override
|
||||||
|
public RouteResult handleMethodRejection(RequestContext ctx, HttpMethod supported) {
|
||||||
|
return ctx.complete(
|
||||||
|
HttpResponse.create()
|
||||||
|
.withStatus(400)
|
||||||
|
.withEntity("Whoopsie! Unsupported method. Supported would have been " + supported.value()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TestRoute route =
|
||||||
|
testRoute(
|
||||||
|
handleRejections(rejectionHandler,
|
||||||
|
get(complete("Successful!"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
route.run(HttpRequest.GET("/"))
|
||||||
|
.assertStatusCode(200)
|
||||||
|
.assertEntity("Successful!");
|
||||||
|
|
||||||
|
route.run(HttpRequest.POST("/"))
|
||||||
|
.assertStatusCode(400)
|
||||||
|
.assertEntity("Whoopsie! Unsupported method. Supported would have been GET");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class TooManyRequestsRejection extends CustomRejection {
|
||||||
|
final public String message;
|
||||||
|
TooManyRequestsRejection(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Handler testHandler =
|
||||||
|
new Handler() {
|
||||||
|
@Override
|
||||||
|
public RouteResult apply(RequestContext ctx) {
|
||||||
|
if (ctx.request().getUri().path().startsWith("/test"))
|
||||||
|
return ctx.complete("Successful!");
|
||||||
|
else
|
||||||
|
return ctx.reject(new TooManyRequestsRejection("Too many requests for busy path!"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandleCustomRejection() {
|
||||||
|
RejectionHandler rejectionHandler =
|
||||||
|
new RejectionHandler() {
|
||||||
|
@Override
|
||||||
|
public RouteResult handleCustomRejection(RequestContext ctx, CustomRejection rejection) {
|
||||||
|
if (rejection instanceof TooManyRequestsRejection) {
|
||||||
|
TooManyRequestsRejection rej = (TooManyRequestsRejection) rejection;
|
||||||
|
HttpResponse response =
|
||||||
|
HttpResponse.create()
|
||||||
|
.withStatus(StatusCodes.TOO_MANY_REQUESTS)
|
||||||
|
.withEntity(rej.message);
|
||||||
|
return ctx.complete(response);
|
||||||
|
} else
|
||||||
|
return passRejection();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
testRouteWithHandler(handleRejections(rejectionHandler, handleWith(testHandler)));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testHandleCustomRejectionByClass() {
|
||||||
|
Handler1<TooManyRequestsRejection> rejectionHandler =
|
||||||
|
new Handler1<TooManyRequestsRejection>() {
|
||||||
|
public RouteResult apply(RequestContext ctx, TooManyRequestsRejection rej) {
|
||||||
|
HttpResponse response =
|
||||||
|
HttpResponse.create()
|
||||||
|
.withStatus(StatusCodes.TOO_MANY_REQUESTS)
|
||||||
|
.withEntity(rej.message);
|
||||||
|
return ctx.complete(response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
testRouteWithHandler(handleRejections(TooManyRequestsRejection.class, rejectionHandler, handleWith(testHandler)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testRouteWithHandler(Route innerRoute) {
|
||||||
|
TestRoute route = testRoute(innerRoute);
|
||||||
|
|
||||||
|
route.run(HttpRequest.GET("/test"))
|
||||||
|
.assertStatusCode(200);
|
||||||
|
|
||||||
|
route.run(HttpRequest.GET("/other"))
|
||||||
|
.assertStatusCode(429)
|
||||||
|
.assertEntity("Too many requests for busy path!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.http.impl.server
|
||||||
|
|
||||||
|
import akka.http.javadsl.server.CustomRejection
|
||||||
|
import akka.http.scaladsl.server.Rejection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper that packs a Java custom rejection into a Scala Rejection.
|
||||||
|
*
|
||||||
|
* INTERNAL API
|
||||||
|
*/
|
||||||
|
private[http] case class CustomRejectionWrapper(customRejection: CustomRejection) extends Rejection
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.http.impl.server
|
||||||
|
|
||||||
|
import scala.collection.immutable
|
||||||
|
|
||||||
|
import akka.http.javadsl.server
|
||||||
|
import akka.http.javadsl.server.RouteResult
|
||||||
|
import akka.http.scaladsl.server._
|
||||||
|
|
||||||
|
import akka.http.impl.util.JavaMapping.Implicits._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL API
|
||||||
|
*/
|
||||||
|
private[http] class RejectionHandlerWrapper(javaHandler: server.RejectionHandler) extends RejectionHandler {
|
||||||
|
def apply(rejs: immutable.Seq[Rejection]): Option[Route] = Some { scalaCtx ⇒
|
||||||
|
val ctx = new RequestContextImpl(scalaCtx)
|
||||||
|
|
||||||
|
import javaHandler._
|
||||||
|
def handle(): RouteResult =
|
||||||
|
if (rejs.isEmpty) handleEmptyRejection(ctx)
|
||||||
|
else rejs.head match {
|
||||||
|
case MethodRejection(supported) ⇒
|
||||||
|
handleMethodRejection(ctx, supported.asJava)
|
||||||
|
case SchemeRejection(supported) ⇒
|
||||||
|
handleSchemeRejection(ctx, supported)
|
||||||
|
case MissingQueryParamRejection(parameterName) ⇒
|
||||||
|
handleMissingQueryParamRejection(ctx, parameterName)
|
||||||
|
case MalformedQueryParamRejection(parameterName, errorMsg, cause) ⇒
|
||||||
|
handleMalformedQueryParamRejection(ctx, parameterName, errorMsg, cause.orNull)
|
||||||
|
case MissingFormFieldRejection(fieldName) ⇒
|
||||||
|
handleMissingFormFieldRejection(ctx, fieldName)
|
||||||
|
case MalformedFormFieldRejection(fieldName, errorMsg, cause) ⇒
|
||||||
|
handleMalformedFormFieldRejection(ctx, fieldName, errorMsg, cause.orNull)
|
||||||
|
case MissingHeaderRejection(headerName) ⇒
|
||||||
|
handleMissingHeaderRejection(ctx, headerName)
|
||||||
|
case MalformedHeaderRejection(headerName, errorMsg, cause) ⇒
|
||||||
|
handleMalformedHeaderRejection(ctx, headerName, errorMsg, cause.orNull)
|
||||||
|
case UnsupportedRequestContentTypeRejection(supported) ⇒
|
||||||
|
handleUnsupportedRequestContentTypeRejection(ctx, supported.toList.toSeq.asJava)
|
||||||
|
case UnsupportedRequestEncodingRejection(supported) ⇒
|
||||||
|
handleUnsupportedRequestEncodingRejection(ctx, supported.asJava)
|
||||||
|
case UnsatisfiableRangeRejection(unsatisfiableRanges, actualEntityLength) ⇒
|
||||||
|
handleUnsatisfiableRangeRejection(ctx, unsatisfiableRanges.asJava, actualEntityLength)
|
||||||
|
case TooManyRangesRejection(maxRanges) ⇒
|
||||||
|
handleTooManyRangesRejection(ctx, maxRanges)
|
||||||
|
case MalformedRequestContentRejection(message, cause) ⇒
|
||||||
|
handleMalformedRequestContentRejection(ctx, message, cause.orNull)
|
||||||
|
case RequestEntityExpectedRejection ⇒
|
||||||
|
handleRequestEntityExpectedRejection(ctx)
|
||||||
|
case UnacceptedResponseContentTypeRejection(supported) ⇒
|
||||||
|
handleUnacceptedResponseContentTypeRejection(ctx, supported.toList.toSeq.asJava)
|
||||||
|
case UnacceptedResponseEncodingRejection(supported) ⇒
|
||||||
|
handleUnacceptedResponseEncodingRejection(ctx, supported.toList.toSeq.asJava)
|
||||||
|
case AuthenticationFailedRejection(cause, challenge) ⇒
|
||||||
|
handleAuthenticationFailedRejection(ctx, cause == AuthenticationFailedRejection.CredentialsMissing, challenge)
|
||||||
|
case AuthorizationFailedRejection ⇒
|
||||||
|
handleAuthorizationFailedRejection(ctx)
|
||||||
|
case MissingCookieRejection(cookieName) ⇒
|
||||||
|
handleMissingCookieRejection(ctx, cookieName)
|
||||||
|
case ExpectedWebsocketRequestRejection ⇒
|
||||||
|
handleExpectedWebsocketRequestRejection(ctx)
|
||||||
|
case UnsupportedWebsocketSubprotocolRejection(supportedProtocol) ⇒
|
||||||
|
handleUnsupportedWebsocketSubprotocolRejection(ctx, supportedProtocol)
|
||||||
|
case ValidationRejection(message, cause) ⇒
|
||||||
|
handleValidationRejection(ctx, message, cause.orNull)
|
||||||
|
|
||||||
|
case CustomRejectionWrapper(custom) ⇒ handleCustomRejection(ctx, custom)
|
||||||
|
case o ⇒ handleCustomScalaRejection(ctx, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle() match {
|
||||||
|
case r: RouteResultImpl ⇒ r.underlying
|
||||||
|
case PassRejectionRouteResult ⇒ scalaCtx.reject(rejs: _*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -46,6 +46,8 @@ private[http] final case class RequestContextImpl(underlying: ScalaRequestContex
|
||||||
|
|
||||||
def notFound(): RouteResult = underlying.reject()
|
def notFound(): RouteResult = underlying.reject()
|
||||||
|
|
||||||
|
def reject(customRejection: CustomRejection): RouteResult = underlying.reject(CustomRejectionWrapper(customRejection))
|
||||||
|
|
||||||
def executionContext(): ExecutionContext = underlying.executionContext
|
def executionContext(): ExecutionContext = underlying.executionContext
|
||||||
def materializer(): Materializer = underlying.materializer
|
def materializer(): Materializer = underlying.materializer
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ private[http] object RouteImplementation extends Directives with server.RouteCon
|
||||||
}
|
}
|
||||||
handleExceptions(pf)
|
handleExceptions(pf)
|
||||||
|
|
||||||
|
case HandleRejections(handler) ⇒ handleRejections(new RejectionHandlerWrapper(handler))
|
||||||
case Validated(isValid, errorMsg) ⇒ validate(isValid, errorMsg)
|
case Validated(isValid, errorMsg) ⇒ validate(isValid, errorMsg)
|
||||||
case RangeSupport() ⇒ withRangeSupport
|
case RangeSupport() ⇒ withRangeSupport
|
||||||
case SetCookie(cookie) ⇒ setCookie(cookie.asScala)
|
case SetCookie(cookie) ⇒ setCookie(cookie.asScala)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import akka.http.scaladsl.{ server ⇒ ss }
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
*/
|
*/
|
||||||
private[http] class RouteResultImpl(val underlying: Future[ss.RouteResult]) extends js.RouteResult
|
private[http] class RouteResultImpl(val underlying: Future[ss.RouteResult]) extends js.RouteResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
*/
|
*/
|
||||||
|
|
@ -20,3 +21,10 @@ private[http] object RouteResultImpl {
|
||||||
implicit def autoConvert(result: Future[ss.RouteResult]): js.RouteResult =
|
implicit def autoConvert(result: Future[ss.RouteResult]): js.RouteResult =
|
||||||
new RouteResultImpl(result)
|
new RouteResultImpl(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal result that flags that a rejection was not handled by a rejection handler.
|
||||||
|
*
|
||||||
|
* INTERNAL API
|
||||||
|
*/
|
||||||
|
private[http] case object PassRejectionRouteResult extends js.RouteResult
|
||||||
|
|
@ -70,6 +70,7 @@ private[http] object RouteStructure {
|
||||||
case class Validated(isValid: Boolean, errorMsg: String)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
|
case class Validated(isValid: Boolean, errorMsg: String)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
|
||||||
|
|
||||||
case class HandleExceptions(handler: ExceptionHandler)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
|
case class HandleExceptions(handler: ExceptionHandler)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
|
||||||
|
case class HandleRejections(handler: RejectionHandler)(val innerRoute: Route, val moreInnerRoutes: immutable.Seq[Route]) extends DirectiveRoute
|
||||||
|
|
||||||
sealed abstract class HostFilter extends DirectiveRoute {
|
sealed abstract class HostFilter extends DirectiveRoute {
|
||||||
def filter(hostName: String): Boolean
|
def filter(hostName: String): Boolean
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.http.javadsl.server
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for application defined rejections.
|
||||||
|
*/
|
||||||
|
abstract class CustomRejection
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.http.javadsl.server
|
||||||
|
|
||||||
|
import java.{ lang ⇒ jl }
|
||||||
|
|
||||||
|
import akka.http.impl.server.PassRejectionRouteResult
|
||||||
|
import akka.http.javadsl.model.{ ContentType, ContentTypeRange, HttpMethod }
|
||||||
|
import akka.http.javadsl.model.headers.{ HttpChallenge, ByteRange, HttpEncoding }
|
||||||
|
import akka.http.scaladsl.server.Rejection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for defining a RejectionHandler to be used with the `handleRejection` directive.
|
||||||
|
* Override one of the handler methods to define a route to be used in case the inner route
|
||||||
|
* rejects a request with the given rejection.
|
||||||
|
*
|
||||||
|
* Default implementations pass the rejection to outer handlers.
|
||||||
|
*/
|
||||||
|
abstract class RejectionHandler {
|
||||||
|
/**
|
||||||
|
* Callback called to handle the empty rejection which represents the
|
||||||
|
* "Not Found" condition.
|
||||||
|
*/
|
||||||
|
def handleEmptyRejection(ctx: RequestContext): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by method filters.
|
||||||
|
* Signals that the request was rejected because the HTTP method is unsupported.
|
||||||
|
*
|
||||||
|
* The default implementation does not handle the rejection.
|
||||||
|
*/
|
||||||
|
def handleMethodRejection(ctx: RequestContext, supported: HttpMethod): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by scheme filters.
|
||||||
|
* Signals that the request was rejected because the Uri scheme is unsupported.
|
||||||
|
*/
|
||||||
|
def handleSchemeRejection(ctx: RequestContext, supported: String): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by parameter filters.
|
||||||
|
* Signals that the request was rejected because a query parameter was not found.
|
||||||
|
*/
|
||||||
|
def handleMissingQueryParamRejection(ctx: RequestContext, parameterName: String): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by parameter filters.
|
||||||
|
* Signals that the request was rejected because a query parameter could not be interpreted.
|
||||||
|
*/
|
||||||
|
def handleMalformedQueryParamRejection(ctx: RequestContext, parameterName: String, errorMsg: String,
|
||||||
|
cause: Throwable): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by form field filters.
|
||||||
|
* Signals that the request was rejected because a form field was not found.
|
||||||
|
*/
|
||||||
|
def handleMissingFormFieldRejection(ctx: RequestContext, fieldName: String): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by form field filters.
|
||||||
|
* Signals that the request was rejected because a form field could not be interpreted.
|
||||||
|
*/
|
||||||
|
def handleMalformedFormFieldRejection(ctx: RequestContext, fieldName: String, errorMsg: String,
|
||||||
|
cause: Throwable): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by header directives.
|
||||||
|
* Signals that the request was rejected because a required header could not be found.
|
||||||
|
*/
|
||||||
|
def handleMissingHeaderRejection(ctx: RequestContext, headerName: String): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by header directives.
|
||||||
|
* Signals that the request was rejected because a header value is malformed.
|
||||||
|
*/
|
||||||
|
def handleMalformedHeaderRejection(ctx: RequestContext, headerName: String, errorMsg: String,
|
||||||
|
cause: Throwable): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by unmarshallers.
|
||||||
|
* Signals that the request was rejected because the requests content-type is unsupported.
|
||||||
|
*/
|
||||||
|
def handleUnsupportedRequestContentTypeRejection(ctx: RequestContext, supported: jl.Iterable[ContentTypeRange]): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by decoding filters.
|
||||||
|
* Signals that the request was rejected because the requests content encoding is unsupported.
|
||||||
|
*/
|
||||||
|
def handleUnsupportedRequestEncodingRejection(ctx: RequestContext, supported: HttpEncoding): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by range directives.
|
||||||
|
* Signals that the request was rejected because the requests contains only unsatisfiable ByteRanges.
|
||||||
|
* The actualEntityLength gives the client a hint to create satisfiable ByteRanges.
|
||||||
|
*/
|
||||||
|
def handleUnsatisfiableRangeRejection(ctx: RequestContext, unsatisfiableRanges: jl.Iterable[ByteRange], actualEntityLength: Long): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by range directives.
|
||||||
|
* Signals that the request contains too many ranges. An irregular high number of ranges
|
||||||
|
* indicates a broken client or a denial of service attack.
|
||||||
|
*/
|
||||||
|
def handleTooManyRangesRejection(ctx: RequestContext, maxRanges: Int): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by unmarshallers.
|
||||||
|
* Signals that the request was rejected because unmarshalling failed with an error that wasn't
|
||||||
|
* an `IllegalArgumentException`. Usually that means that the request content was not of the expected format.
|
||||||
|
* Note that semantic issues with the request content (e.g. because some parameter was out of range)
|
||||||
|
* will usually trigger a `ValidationRejection` instead.
|
||||||
|
*/
|
||||||
|
def handleMalformedRequestContentRejection(ctx: RequestContext, message: String, cause: Throwable): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by unmarshallers.
|
||||||
|
* Signals that the request was rejected because an message body entity was expected but not supplied.
|
||||||
|
*/
|
||||||
|
def handleRequestEntityExpectedRejection(ctx: RequestContext): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by marshallers.
|
||||||
|
* Signals that the request was rejected because the service is not capable of producing a response entity whose
|
||||||
|
* content type is accepted by the client
|
||||||
|
*/
|
||||||
|
def handleUnacceptedResponseContentTypeRejection(ctx: RequestContext, supported: jl.Iterable[ContentType]): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by encoding filters.
|
||||||
|
* Signals that the request was rejected because the service is not capable of producing a response entity whose
|
||||||
|
* content encoding is accepted by the client
|
||||||
|
*/
|
||||||
|
def handleUnacceptedResponseEncodingRejection(ctx: RequestContext, supported: jl.Iterable[HttpEncoding]): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by an [[akka.http.scaladsl.server.authentication.HttpAuthenticator]].
|
||||||
|
* Signals that the request was rejected because the user could not be authenticated. The reason for the rejection is
|
||||||
|
* specified in the cause.
|
||||||
|
*
|
||||||
|
* If credentialsMissing is false, existing credentials were rejected.
|
||||||
|
*/
|
||||||
|
def handleAuthenticationFailedRejection(ctx: RequestContext, credentialsMissing: Boolean, challenge: HttpChallenge): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by the 'authorize' directive.
|
||||||
|
* Signals that the request was rejected because the user is not authorized.
|
||||||
|
*/
|
||||||
|
def handleAuthorizationFailedRejection(ctx: RequestContext): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by the `cookie` directive.
|
||||||
|
* Signals that the request was rejected because a cookie was not found.
|
||||||
|
*/
|
||||||
|
def handleMissingCookieRejection(ctx: RequestContext, cookieName: String): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created when a websocket request was expected but none was found.
|
||||||
|
*/
|
||||||
|
def handleExpectedWebsocketRequestRejection(ctx: RequestContext): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created when a websocket request was not handled because none
|
||||||
|
* of the given subprotocols was supported.
|
||||||
|
*/
|
||||||
|
def handleUnsupportedWebsocketSubprotocolRejection(ctx: RequestContext, supportedProtocol: String): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle rejection created by the `validation` directive as well as for `IllegalArgumentExceptions`
|
||||||
|
* thrown by domain model constructors (e.g. via `require`).
|
||||||
|
* It signals that an expected value was semantically invalid.
|
||||||
|
*/
|
||||||
|
def handleValidationRejection(ctx: RequestContext, message: String, cause: Throwable): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle any custom rejection defined by the application.
|
||||||
|
*/
|
||||||
|
def handleCustomRejection(ctx: RequestContext, rejection: CustomRejection): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called to handle any other Scala rejection that is not covered by this class.
|
||||||
|
*/
|
||||||
|
def handleCustomScalaRejection(ctx: RequestContext, rejection: Rejection): RouteResult = passRejection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the RouteResult returned by this method in handler implementations to signal that a rejection was not handled
|
||||||
|
* and should be passed to an outer rejection handler.
|
||||||
|
*/
|
||||||
|
protected final def passRejection(): RouteResult = PassRejectionRouteResult
|
||||||
|
}
|
||||||
|
|
@ -73,5 +73,8 @@ trait RequestContext {
|
||||||
*/
|
*/
|
||||||
def notFound(): RouteResult
|
def notFound(): RouteResult
|
||||||
|
|
||||||
// FIXME: provide proper support for rejections, see #16438
|
/**
|
||||||
|
* Reject this request with an application-defined CustomRejection.
|
||||||
|
*/
|
||||||
|
def reject(customRejection: CustomRejection): RouteResult
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ package directives
|
||||||
import akka.http.impl.server.RouteStructure
|
import akka.http.impl.server.RouteStructure
|
||||||
|
|
||||||
import scala.annotation.varargs
|
import scala.annotation.varargs
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
abstract class ExecutionDirectives extends CookieDirectives {
|
abstract class ExecutionDirectives extends CookieDirectives {
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,4 +17,25 @@ abstract class ExecutionDirectives extends CookieDirectives {
|
||||||
@varargs
|
@varargs
|
||||||
def handleExceptions(handler: ExceptionHandler, innerRoute: Route, moreInnerRoutes: Route*): Route =
|
def handleExceptions(handler: ExceptionHandler, innerRoute: Route, moreInnerRoutes: Route*): Route =
|
||||||
RouteStructure.HandleExceptions(handler)(innerRoute, moreInnerRoutes.toList)
|
RouteStructure.HandleExceptions(handler)(innerRoute, moreInnerRoutes.toList)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles rejections in the inner routes using the specified handler.
|
||||||
|
*/
|
||||||
|
@varargs
|
||||||
|
def handleRejections(handler: RejectionHandler, innerRoute: Route, moreInnerRoutes: Route*): Route =
|
||||||
|
RouteStructure.HandleRejections(handler)(innerRoute, moreInnerRoutes.toList)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles rejections of the given type in the inner routes using the specified handler.
|
||||||
|
*/
|
||||||
|
@varargs
|
||||||
|
def handleRejections[T](tClass: Class[T], handler: Handler1[T], innerRoute: Route, moreInnerRoutes: Route*): Route =
|
||||||
|
RouteStructure.HandleRejections(new RejectionHandler {
|
||||||
|
implicit def tTag: ClassTag[T] = ClassTag(tClass)
|
||||||
|
override def handleCustomRejection(ctx: RequestContext, rejection: CustomRejection): RouteResult =
|
||||||
|
rejection match {
|
||||||
|
case t: T ⇒ handler.apply(ctx, t)
|
||||||
|
case _ ⇒ passRejection()
|
||||||
|
}
|
||||||
|
})(innerRoute, moreInnerRoutes.toList)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue