2017-03-04 20 views
0

カスタムJSONマーシャラー/アンマーシャラーの使用に問題があります。このくらいの作品罰金:Akka-http-json「サポートされていないコンテンツタイプ、サポートされているアプリケーション/ 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 
     } 
    } 
    } 
} 

答えて

0

解決の鍵は希望ContentTypecompleteを呼び出すことです。ここで私はそれがblockが評価されたときに計算され、所望のコンテンツと一緒にContent-Typeapplication/jsonHttpResponseを提供して書いた方法は次のとおりです。

@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 } 
    } 
    } 
} 

1つの特性が混在しており、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) } 
} 
1

あなたが/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) 
) 
+0

私は 'HttpEntityとしてエンティティを宣言する('アプリケーション/ json'、jsObject.toStringは) ''のContent-type'が設定だろうと思いました。もしそうでなければ、私はそれをどのように達成すべきですか? 進行中の現在の作業と一致するようにコードを修正しました。 あなたが考えるべきことJSONはIDEのIntelliJ IDEAによって提供されるtoString出力です。これを理解するために貼り付けました。あまり混乱していないようだ。 「HttpResponse」はどこから取得しましたか? –

+0

また、 'Content-Type'のテストが渡されました。これは混乱します。 –

+0

問題は 'Post("/addIndividual "、エンティティ)'呼び出しに提供するエンティティ 'entity'ではなく、アプリケーションが返すエンティティ(' response.entity')であり、これは型です'text/plain'であり、有効なJSONを含んでいません。それを修正する必要があります。そこにリストされているコードスニペットに問題はありません。最後の編集の前に、コードスニペットから 'HttpResponse'を取得しました。これはレスポンスの内容を示しています。 –

関連する問題