!htp #15927 port MarshallingDirectives from spray
This commit is contained in:
parent
931e8f9b18
commit
193dda6e01
6 changed files with 237 additions and 13 deletions
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package akka.http.server
|
||||
package directives
|
||||
|
||||
import scala.xml.NodeSeq
|
||||
import akka.http.marshallers.xml.ScalaXmlSupport
|
||||
import akka.http.unmarshalling._
|
||||
import akka.http.marshalling._
|
||||
import akka.http.model._
|
||||
import akka.http.marshallers.sprayjson.SprayJsonSupport._
|
||||
import MediaTypes._
|
||||
import HttpCharsets._
|
||||
import headers._
|
||||
import spray.json.DefaultJsonProtocol._
|
||||
|
||||
class MarshallingDirectivesSpec extends RoutingSpec {
|
||||
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
|
||||
}
|
||||
|
||||
implicit val IntMarshaller: ToEntityMarshaller[Int] =
|
||||
Marshaller.oneOf(ContentType(`application/xhtml+xml`), ContentType(`text/xml`, `UTF-8`)) { contentType ⇒
|
||||
nodeSeqMarshaller(contentType).wrap(contentType) { (i: Int) ⇒ <int>{ i }</int> }
|
||||
}
|
||||
|
||||
"The 'entityAs' directive" should {
|
||||
"extract an object from the requests entity using the in-scope Unmarshaller" in {
|
||||
Put("/", <p>cool</p>) ~> {
|
||||
entity(as[NodeSeq]) { echoComplete }
|
||||
} ~> check { responseAs[String] shouldEqual "<p>cool</p>" }
|
||||
}
|
||||
"return a RequestEntityExpectedRejection rejection if the request has no entity" in {
|
||||
Put() ~> {
|
||||
entity(as[Int]) { echoComplete }
|
||||
} ~> check { rejection shouldEqual RequestEntityExpectedRejection }
|
||||
}
|
||||
"return an UnsupportedRequestContentTypeRejection if no matching unmarshaller is in scope" in {
|
||||
Put("/", HttpEntity(`text/css`, "<p>cool</p>")) ~> {
|
||||
entity(as[NodeSeq]) { echoComplete }
|
||||
} ~> check {
|
||||
rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(`text/xml`, `application/xml`, `text/html`, `application/xhtml+xml`))
|
||||
}
|
||||
Put("/", HttpEntity(ContentType(`text/xml`, `UTF-16`), "<int>26</int>")) ~> {
|
||||
entity(as[Int]) { echoComplete }
|
||||
} ~> check {
|
||||
rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(ContentTypeRange(`text/xml`, iso88592), `text/html`))
|
||||
}
|
||||
}
|
||||
"cancel UnsupportedRequestContentTypeRejections if a subsequent `entity` directive succeeds" in {
|
||||
Put("/", HttpEntity(`text/plain`, "yeah")) ~> {
|
||||
entity(as[NodeSeq]) { _ ⇒ completeOk } ~
|
||||
entity(as[String]) { _ ⇒ validate(false, "Problem") { completeOk } }
|
||||
} ~> check { rejection shouldEqual ValidationRejection("Problem") }
|
||||
}
|
||||
"extract an Option[T] from the requests entity using the in-scope Unmarshaller" in {
|
||||
Put("/", <p>cool</p>) ~> {
|
||||
entity(as[Option[NodeSeq]]) { echoComplete }
|
||||
} ~> check { responseAs[String] shouldEqual "Some(<p>cool</p>)" }
|
||||
}
|
||||
"extract an Option[T] as None if the request has no entity" in {
|
||||
Put() ~> {
|
||||
entity(as[Option[Int]]) { echoComplete }
|
||||
} ~> check { responseAs[String] shouldEqual "None" }
|
||||
}
|
||||
"return an UnsupportedRequestContentTypeRejection if no matching unmarshaller is in scope (for Option[T]s)" in {
|
||||
Put("/", HttpEntity(`text/css`, "<p>cool</p>")) ~> {
|
||||
entity(as[Option[NodeSeq]]) { echoComplete }
|
||||
} ~> check {
|
||||
rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(`text/xml`, `application/xml`, `text/html`, `application/xhtml+xml`))
|
||||
}
|
||||
}
|
||||
"properly extract with a super-unmarshaller" in {
|
||||
case class Person(name: String)
|
||||
val jsonUnmarshaller: FromEntityUnmarshaller[Person] = jsonFormat1(Person)
|
||||
val xmlUnmarshaller: FromEntityUnmarshaller[Person] =
|
||||
ScalaXmlSupport.nodeSeqUnmarshaller(`text/xml`).map(seq ⇒ Person(seq.text))
|
||||
|
||||
implicit val unmarshaller = Unmarshaller.firstOf(jsonUnmarshaller, xmlUnmarshaller)
|
||||
|
||||
val route = entity(as[Person]) { echoComplete }
|
||||
|
||||
Put("/", HttpEntity(`text/xml`, "<name>Peter Xml</name>")) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Person(Peter Xml)"
|
||||
}
|
||||
Put("/", HttpEntity(`application/json`, """{ "name": "Paul Json" }""")) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "Person(Paul Json)"
|
||||
}
|
||||
Put("/", HttpEntity(`text/plain`, """name = Sir Text }""")) ~> route ~> check {
|
||||
rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(`application/json`, `text/xml`))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"The 'completeWith' directive" should {
|
||||
"provide a completion function converting custom objects to an HttpEntity using the in-scope marshaller" in {
|
||||
Get() ~> completeWith(instanceOf[Int]) { prod ⇒ prod(42) } ~> check {
|
||||
responseEntity shouldEqual HttpEntity(ContentType(`application/xhtml+xml`, `UTF-8`), "<int>42</int>")
|
||||
}
|
||||
}
|
||||
"return a UnacceptedResponseContentTypeRejection rejection if no acceptable marshaller is in scope" in {
|
||||
Get() ~> Accept(`text/css`) ~> completeWith(instanceOf[Int]) { prod ⇒ prod(42) } ~> check {
|
||||
rejection shouldEqual UnacceptedResponseContentTypeRejection(Set(`application/xhtml+xml`, ContentType(`text/xml`, `UTF-8`)))
|
||||
}
|
||||
}
|
||||
"convert the response content to an accepted charset" in {
|
||||
Get() ~> `Accept-Charset`(`UTF-8`) ~> completeWith(instanceOf[String]) { prod ⇒ prod("Hällö") } ~> check {
|
||||
responseEntity shouldEqual HttpEntity(ContentType(`text/plain`, `UTF-8`), "Hällö")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"The 'handleWith' directive" should {
|
||||
def times2(x: Int) = x * 2
|
||||
|
||||
"support proper round-trip content unmarshalling/marshalling to and from a function" in (
|
||||
Put("/", HttpEntity(`text/html`, "<int>42</int>")) ~> Accept(`text/xml`) ~> handleWith(times2)
|
||||
~> check { responseEntity shouldEqual HttpEntity(ContentType(`text/xml`, `UTF-8`), "<int>84</int>") })
|
||||
|
||||
"result in UnsupportedRequestContentTypeRejection rejection if there is no unmarshaller supporting the requests charset" in (
|
||||
Put("/", HttpEntity(`text/xml`, "<int>42</int>")) ~> Accept(`text/xml`) ~> handleWith(times2)
|
||||
~> check {
|
||||
rejection shouldEqual UnsupportedRequestContentTypeRejection(Set(ContentTypeRange(`text/xml`, iso88592), `text/html`))
|
||||
})
|
||||
|
||||
"result in an UnacceptedResponseContentTypeRejection rejection if there is no marshaller supporting the requests Accept-Charset header" in (
|
||||
Put("/", HttpEntity(`text/html`, "<int>42</int>")) ~> addHeaders(Accept(`text/xml`), `Accept-Charset`(`UTF-16`)) ~>
|
||||
handleWith(times2) ~> check {
|
||||
rejection shouldEqual UnacceptedResponseContentTypeRejection(Set(`application/xhtml+xml`, ContentType(`text/xml`, `UTF-8`)))
|
||||
})
|
||||
}
|
||||
|
||||
"The marshalling infrastructure for JSON" should {
|
||||
import spray.json._
|
||||
case class Foo(name: String)
|
||||
implicit val fooFormat = jsonFormat1(Foo)
|
||||
val foo = Foo("Hällö")
|
||||
|
||||
"render JSON with UTF-8 encoding if no `Accept-Charset` request header is present" in {
|
||||
Get() ~> complete(foo) ~> check {
|
||||
responseEntity shouldEqual HttpEntity(ContentType(`application/json`, `UTF-8`), foo.toJson.prettyPrint)
|
||||
}
|
||||
}
|
||||
"reject JSON rendering if an `Accept-Charset` request header requests a non-UTF-8 encoding" in {
|
||||
Get() ~> `Accept-Charset`(`ISO-8859-1`) ~> complete(foo) ~> check {
|
||||
rejection shouldEqual UnacceptedResponseContentTypeRejection(Set(ContentType(`application/json`, `UTF-8`)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue