无效的Json:使用播放正文解析器时,由于输入结束,没有内容要映射 [英] Invalid Json: No content to map due to end-of-input when using play body parser

查看:75
本文介绍了无效的Json:使用播放正文解析器时,由于输入结束,没有内容要映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试测试一种控制器方法,该方法尝试解析请求中发送的JSON:

I'm trying to test a controller method that attempts to parse JSON sent in the request:

  def addRoomToProfileForTime = Action.async(parse.json[AddRoomToProfileForTimeRequest]) { request =>
    profileService.addRoomToProfileForTime(request.body.roomId, request.body.profileId, request.body.timeRange).value.map {
      case Xor.Right(_)  => Ok
      case Xor.Left(err) => BadRequest(Json.toJson(err))
    }
  }

这是代表请求的案例类:

This is the case class that represents the request:

final case class AddRoomToProfileForTimeRequest(
  roomId: Int,
  profileId: Int,
  timeRange: TimeRange
)

implicit val addRoomToProfileForTimeRequestFormat:Format[AddRoomToProfileForTimeRequest] = Json.format

此代码按我的要求正常工作:

This code works as expected with I make a request like so:

curl -H "Content-Type: application/json" -X POST -d '{"roomId":3,"profileId":1,"timeRange":{"id":1,"fromTime":"2000-01-01T01:01","toTime":"2000-01-01T02:01"}}' http://localhost:9000/api/profiles/addRoomToProfileForTime

但是我正在尝试为此方法编写测试(请注意,我正在使用macwire进行依赖项注入,因此不能使用WithApplication:

But I'm trying to write a test for this method (note that I am using macwire for dependency injection, hence cannot use WithApplication:

"add a room to profile for time" in new TestContext {
  val roomId = 1
  val profileId = 1
  val from = "2000-01-01T01:01"
  val to = "2000-01-01T02:01"
  val requestJson = Json.obj(
    "roomId"    -> roomId,
    "profileId" -> profileId,
    "timeRange" -> Json.obj(
      "id"       -> 1,
      "fromTime" -> from,
      "toTime"   -> to
    )
  )

  implicit val system = ActorSystem()
  implicit val materializer = ActorMaterializer()

  val fakeReq = FakeRequest(Helpers.POST, "api/profiles/addRoomToProfileForTime")
    .withHeaders(CONTENT_TYPE -> "application/json")
    .withJsonBody(requestJson)
  val result = profileController.addRoomToProfileForTime()(fakeReq).run

  val content  = contentAsString(result)
  println(content)
  status(result) must equalTo(OK)
}

但是,此测试因Play的错误请求而失败:

However, this test fails with a Bad Request from Play:

<body>                                             
    <h1>Bad Request</h1>                                

    <p id="detail">
        For request 'POST api/profiles/addRoomToProfileForTime' [Invalid Json: No content to map due to end-of-input at [Source: akka.util.ByteIterator$ByteArrayIterator$$anon$1@37d14073; line: 1, column: 0]]
    </p>

</body>

如果我使用request.body.asJson解析JSON,则该方法的行为将与预期的一样.我只是使用上面的body解析器方法得到此错误.

If I parse the JSON with request.body.asJson the method behaves as expected. It's only using the body parser method above that I get this error.

推荐答案

简短的答案是:在控制器测试中的FakeRequest上,使用withBody方法而不是withJsonBody.

The short answer is: on your FakeRequest in your controller test use the withBody method instead of withJsonBody.

我也遇到了这个问题,在解决这个问题之前,我尴尬地花了几个小时.长答案是FakeRequestwithJsonBody返回一个FakeRequest[AnyContentAsJson],并且由于您的控制器期望一个JsValue(不是一个AnyContentAsJson),因此在调用apply()时在您执行操作时,它与该apply方法不匹配,这是您想要的方法:

I had this issue as well, and I embarrassingly spent hours on it until I figured it out. The long answer is that FakeRequest's withJsonBody returns a FakeRequest[AnyContentAsJson], and since your controller is expecting a JsValue (not an AnyContentAsJson), when you call apply() on your action it fails to match this apply method, which is the one you want:

def apply(request: Request[A]): Future[Result]

相反命中此套用方法:

def apply(rh: RequestHeader): Accumulator[ByteString, Result]

,因此由于您没有再将任何字节传递给累加器,所以您会收到意外的end-of-input错误消息.

and thus since you're not then passing any Bytes to the Accumulator, you get the unexpected end-of-input error message you're getting.

这篇关于无效的Json:使用播放正文解析器时,由于输入结束,没有内容要映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆