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-Typeapplication/json与期望的内容,计算沿着HttpResponseblock被评估:

@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) 
) 
+0

我认为声明实体'HttpEntity('应用/ json',jsObject.toString)'会设置'内容Type' 。如果不是,我应该如何实现? 我修改了代码以匹配我目前正在进行的工作。 您认为应该是JSON的是toString输出,由IDE中的IntelliJ IDEA提供,我粘贴它以帮助理解。似乎只有困惑的事情。 你从哪里得到那个'HttpResponse'? –

+0

此外,“内容类型”测试通过,这是混乱。 –

+0

问题不在于您向Post(“/ addIndividual”,entity)调用提供的实体'entity',而是与应用程序返回给您的实体('response.entity')一起使用,这是类型'text/plain',并且不包含有效的JSON。你需要解决这个问题,这个问题不在你列出的代码片段中。我从你的代码片段中得到了'HttpResponse',在你最后一次编辑之前,它显示了响应的内容。 –