Akka-http-json“不支持的内容类型,支持:application/json”
问题描述:
我在使用自定义JSON编组器/解组器时遇到问题。此作品不多罚款:Akka-http-json“不支持的内容类型,支持:application/json”
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
}
}
}
}
答
到该溶液中,关键是要调用complete
具有所需ContentType
。下面是一个方法我写提供与Content-Type
application/json
与期望的内容,计算沿着HttpResponse
时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) }
}
答
的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)
)
我认为声明实体'HttpEntity('应用/ json',jsObject.toString)'会设置'内容Type' 。如果不是,我应该如何实现? 我修改了代码以匹配我目前正在进行的工作。 您认为应该是JSON的是toString输出,由IDE中的IntelliJ IDEA提供,我粘贴它以帮助理解。似乎只有困惑的事情。 你从哪里得到那个'HttpResponse'? –
此外,“内容类型”测试通过,这是混乱。 –
问题不在于您向Post(“/ addIndividual”,entity)调用提供的实体'entity',而是与应用程序返回给您的实体('response.entity')一起使用,这是类型'text/plain',并且不包含有效的JSON。你需要解决这个问题,这个问题不在你列出的代码片段中。我从你的代码片段中得到了'HttpResponse',在你最后一次编辑之前,它显示了响应的内容。 –