=htp #15792 make entityAs directive turn IllegalArgumentException into ValidationRejection

This commit is contained in:
Mathias 2015-03-31 11:48:27 +02:00
parent 37aa2cb886
commit 612fc052e6
3 changed files with 28 additions and 4 deletions

View file

@ -6,6 +6,7 @@ package akka.http.server
package directives
import scala.xml.NodeSeq
import org.scalatest.Inside
import akka.http.marshallers.xml.ScalaXmlSupport
import akka.http.unmarshalling._
import akka.http.marshalling._
@ -16,14 +17,14 @@ import HttpCharsets._
import headers._
import spray.json.DefaultJsonProtocol._
class MarshallingDirectivesSpec extends RoutingSpec {
class MarshallingDirectivesSpec extends RoutingSpec with Inside {
import ScalaXmlSupport._
private val iso88592 = HttpCharsets.getForKey("iso-8859-2").get
implicit val IntUnmarshaller: FromEntityUnmarshaller[Int] =
nodeSeqUnmarshaller(ContentTypeRange(`text/xml`, iso88592), `text/html`) map {
case NodeSeq.Empty throw Unmarshaller.NoContentException
case x x.text.toInt
case x { val i = x.text.toInt; require(i >= 0); i }
}
implicit val IntMarshaller: ToEntityMarshaller[Int] =
@ -60,6 +61,23 @@ class MarshallingDirectivesSpec extends RoutingSpec {
entity(as[String]) { _ validate(false, "Problem") { completeOk } }
} ~> check { rejection shouldEqual ValidationRejection("Problem") }
}
"return a ValidationRejection if the request entity is semantically invalid (IllegalArgumentException)" in {
Put("/", HttpEntity(ContentType(`text/xml`, iso88592), "<int>-3</int>")) ~> {
entity(as[Int]) { _ completeOk }
} ~> check {
inside(rejection) {
case ValidationRejection("requirement failed", Some(_: IllegalArgumentException))
}
}
}
"return a MalformedRequestContentRejection if unmarshalling failed due to a not further classified error" in {
Put("/", HttpEntity(`text/xml`, "<foo attr='illegal xml'")) ~> {
entity(as[NodeSeq]) { _ completeOk }
} ~> check {
rejection shouldEqual MalformedRequestContentRejection(
"XML document structures must start and end within the same entity.", None)
}
}
"extract an Option[T] from the requests entity using the in-scope Unmarshaller" in {
Put("/", <p>cool</p>) ~> {
entity(as[Option[NodeSeq]]) { echoComplete }

View file

@ -94,7 +94,10 @@ case class TooManyRangesRejection(maxRanges: Int) extends Rejection
/**
* Rejection created by unmarshallers.
* Signals that the request was rejected because there was an error while unmarshalling the request content
* 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.
*/
case class MalformedRequestContentRejection(message: String, cause: Option[Throwable] = None) extends Rejection
@ -161,7 +164,9 @@ case object AuthorizationFailedRejection extends Rejection
case class MissingCookieRejection(cookieName: String) extends Rejection
/**
* Rejection created by the `validation` directive.
* 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.
*/
case class ValidationRejection(message: String, cause: Option[Throwable] = None) extends Rejection

View file

@ -27,6 +27,7 @@ trait MarshallingDirectives {
case Success(value) provide(value)
case Failure(Unmarshaller.NoContentException) reject(RequestEntityExpectedRejection)
case Failure(Unmarshaller.UnsupportedContentTypeException(x)) reject(UnsupportedRequestContentTypeRejection(x))
case Failure(x: IllegalArgumentException) reject(ValidationRejection(x.getMessage.nullAsEmpty, Some(x)))
case Failure(x) reject(MalformedRequestContentRejection(x.getMessage.nullAsEmpty, Option(x.getCause)))
}
} & cancelRejections(RequestEntityExpectedRejection.getClass, classOf[UnsupportedRequestContentTypeRejection])