Akka-http-json "Unsupported Content-Type, supported: application/json"
Akka-http-json "Unsupported Content-Type, supported: application/json"
我在使用自定义 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
}
}
}
}
您通过将您的实体发布到 /addIndividual
从 new RestHttp()(speciesDefaults).route
路由器获得的 HttpResponse
响应(如记录,见下文)具有 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)
)
解决的关键是用所需的 ContentType
调用 complete
。这是我写的一个方法,它提供 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) }
}
如果您不能更改内容类型,您可以:
val stringR : String = Await.result(Unmarshal(r).to[String],Duration.Inf)
val ind : Individual = Unmarshal(stringR).to[Individual]
我在使用自定义 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
}
}
}
}
您通过将您的实体发布到 /addIndividual
从 new RestHttp()(speciesDefaults).route
路由器获得的 HttpResponse
响应(如记录,见下文)具有 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)
)
解决的关键是用所需的 ContentType
调用 complete
。这是我写的一个方法,它提供 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) }
}
如果您不能更改内容类型,您可以:
val stringR : String = Await.result(Unmarshal(r).to[String],Duration.Inf)
val ind : Individual = Unmarshal(stringR).to[Individual]