我在使用自定义JSON marshaller / unmarshaller时遇到问题。这很好用:
trait EWorksJsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
implicit object IndividualJsonFormat extends RootJsonFormat[Individual] {
def write(individual: Individual) = JsObject(
// blah blah blah
)
def read(value: JsValue): Individual = {
// blah blah blah
}
}
问题是返回Unsupported Content-Type, supported: application/json
,如下所示:
import akka.http.scaladsl.model.ContentTypes._
import akka.http.scaladsl.model.HttpEntity
import akka.http.scaladsl.testkit.ScalatestRouteTest
import akka.http.scaladsl.unmarshalling._
import eworks.model.immutableModel.SpeciesAll
import eworks.model.mutableModel.{Individual, Individuals, VirtualWorld}
import eworks.model.{Fixtures, LoadableModel, SpeciesDefaultLike}
import org.junit.runner.RunWith
import org.scalatest.Matchers._
import org.scalatest._
import org.scalatest.junit.JUnitRunner
import spray.json._
@RunWith(classOf[JUnitRunner])
class TestRest extends WordSpec with SpeciesDefaultLike with LoadableModel with ScalatestRouteTest with Fixtures with EWorksJsonSupport {
"EWorksJsonSupport" should {
"work for Individuals" in {
val jsObject: JsValue = harry.toJson
val entity = HttpEntity(`application/json`, jsObject.toString)
Post("/addIndividual", entity) ~> new RestHttp()(speciesDefaults).route ~> check {
handled === true
contentType === `application/json`
status.intValue === 200
val individual1 = Unmarshal(response.entity).to[Individual]
// ErrorFuture(akka.http.scaladsl.unmarshalling.Unmarshaller$UnsupportedContentTypeException: Unsupported Content-Type, supported: application/json)
val individual2 = responseAs[Individual]
responseAs[Individual] shouldBe harry
}
}
}
}
答案 0 :(得分:1)
您通过将您的实体发布到HttpResponse
(如已记录,见下文)从new RestHttp()(speciesDefaults).route
路由器获得的/addIndividual
响应已将text/plain
作为内容类型,您应该修复它。它的内容看起来也不像有效的JSON(见下文)。
回应是:
HttpResponse(
200 OK,
List(),
HttpEntity.Strict(
text/plain; charset=UTF-8,
Individual added: harry is a human; (unborn); lifeStage 'adult'
), HttpProtocol(HTTP/1.1)
)
答案 1 :(得分:1)
如果您不能更改内容类型,则可以:
val stringR : String = Await.result(Unmarshal(r).to[String],Duration.Inf)
val ind : Individual = Unmarshal(stringR).to[Individual]
答案 2 :(得分:0)
解决方案的关键是使用所需的complete
来呼叫ContentType
。这是我编写的一种方法,它提供HttpResponse
Content-Type
application/json
以及在评估block
时计算的所需内容:
@inline def wrap(block: => JsValue): StandardRoute =
complete(
try {
HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, success(block)))
} catch {
case e: Exception =>
HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, error(e.getMessage)))
}
)
我做了一个特性来封装这个方便的实用工具方法:
import akka.http.scaladsl.model.{ContentTypes, HttpEntity, HttpHeader, HttpResponse}
import akka.http.scaladsl.server.{Directives, MediaTypeNegotiator, Route, StandardRoute, UnsupportedRequestContentTypeRejection}
import akka.http.scaladsl.unmarshalling._
import spray.json._
import scala.collection.immutable.Seq
trait RestHttpSupport extends Directives {
@inline def error (msg: String): String = JsObject("error" -> JsString(msg)).prettyPrint
@inline def success(msg: String): String = JsObject("success" -> JsString(msg)).prettyPrint
@inline def error (msg: JsValue): String = JsObject("error" -> msg).prettyPrint
@inline def success(msg: JsValue): String = JsObject("success" -> msg).prettyPrint
@inline def wrap(block: => JsValue): StandardRoute =
complete(
try {
HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, success(block)))
} catch {
case e: Exception =>
HttpResponse(entity = HttpEntity(ContentTypes.`application/json`, error(e.getMessage)))
}
)
@inline def completeAsJson[T](requestHeaders: Seq[HttpHeader])
(body: T => StandardRoute)
(implicit um: FromRequestUnmarshaller[T]): Route = {
import akka.http.scaladsl.model.MediaTypes.`application/json`
if (new MediaTypeNegotiator(requestHeaders).isAccepted(`application/json`)) {
entity(as[T]) { body }
} else {
reject(UnsupportedRequestContentTypeRejection(Set(`application/json`)))
}
}
@inline def postAsJson[T](body: T => StandardRoute)
(implicit um: FromRequestUnmarshaller[T]): Route = {
(post & extract(_.request.headers)) { requestHeaders =>
completeAsJson[T](requestHeaders) { body }
}
}
}
混合了一个特征,假设从SprayJsonSupport with DefaultJsonProtocol
构建的隐式序列化程序在范围内,可以使用wrap
方法定义Akka HTTP路径。所有这些代码都来自EmpathyWorks™(非开源):
path("definedEvents") {
get { wrap(allDefinedEvents.toJson) }
} ~
path("listIndividuals") {
get { wrap(individuals.toJson) }
} ~
path("listSpecies") {
get { wrap(speciesAll.toJson) }
} ~
path("listSpeciesNames") {
get { wrap(speciesAll.collection.map(_.name).toJson) }
}