+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)
|
||||
.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 reject(customRejection: CustomRejection): RouteResult = underlying.reject(CustomRejectionWrapper(customRejection))
|
||||
|
||||
def executionContext(): ExecutionContext = underlying.executionContext
|
||||
def materializer(): Materializer = underlying.materializer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ private[http] object RouteImplementation extends Directives with server.RouteCon
|
|||
}
|
||||
handleExceptions(pf)
|
||||
|
||||
case HandleRejections(handler) ⇒ handleRejections(new RejectionHandlerWrapper(handler))
|
||||
case Validated(isValid, errorMsg) ⇒ validate(isValid, errorMsg)
|
||||
case RangeSupport() ⇒ withRangeSupport
|
||||
case SetCookie(cookie) ⇒ setCookie(cookie.asScala)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import akka.http.scaladsl.{ server ⇒ ss }
|
|||
* INTERNAL API
|
||||
*/
|
||||
private[http] class RouteResultImpl(val underlying: Future[ss.RouteResult]) extends js.RouteResult
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
|
|
@ -20,3 +21,10 @@ private[http] object RouteResultImpl {
|
|||
implicit def autoConvert(result: Future[ss.RouteResult]): js.RouteResult =
|
||||
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 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 {
|
||||
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
|
||||
|
||||
// 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 scala.annotation.varargs
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
abstract class ExecutionDirectives extends CookieDirectives {
|
||||
/**
|
||||
|
|
@ -16,4 +17,25 @@ abstract class ExecutionDirectives extends CookieDirectives {
|
|||
@varargs
|
||||
def handleExceptions(handler: ExceptionHandler, innerRoute: Route, moreInnerRoutes: Route*): Route =
|
||||
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