play2:FakeRequest()。withBody(body)在控制器中自动转换为Request [AnyContentAsEmpty]

时间:2016-02-14 08:31:36

标签: scala playframework playframework-2.4

我正在开发一个play-2.4项目,并编写了一个控制器,如:

package controllers

import play.api._
import play.api.mvc._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

class Application extends Controller {
  def index = Action.async { implicit request =>
    Future { Ok(request.body.asJson.get) }
  }
}

中有POST / controllers.Application.index conf/routes

我通过执行curl --request POST --header "Content-type: application/json" --data '{"foo":"bar"}' http://localhost:9000/检查了这项工作正常。

现在我为这个控制器写了一个规范:

package controllers

import org.specs2.mutable._
import org.specs2.runner._
import org.junit.runner._

import play.api.test._
import play.api.test.Helpers._

@RunWith(classOf[JUnitRunner])
class ApplicationSpec extends Specification {
  "Application" should {
    val controller = new Application
    val fakeJson = """{ "foo":"bar" }"""
    val fakeRequest = FakeRequest()
      .withHeaders("Content-type" -> "application/json")
      .withBody(fakeJson)
    val index = controller.index()(fakeRequest).run
    status(index) must equalTo(OK)
  }
}

但这导致了运行时错误:

[error]    None.get (Application.scala:11)
[error] controllers.Application$$anonfun$index$1$$anonfun$apply$1.apply(Application.scala:11)
[error] controllers.Application$$anonfun$index$1$$anonfun$apply$1.apply(Application.scala:11)

我在控制器中插入了println(request.body),发现请求正文为AnyContentAsEmpty,意味着fakeJson已被fakeRequest删除。

如何正确地将JSON附加到FakeRequest?

*注意:虽然我可以写FakeRequest(POST, '/', FakeHeaders(), fakeJson),但我认为这不好,因为控制器规范不应该处理HTTP方法或路由。

我会感激任何帮助。

2 个答案:

答案 0 :(得分:1)

如果客户端使用不是 JSON的请求对您的操作进行HTTP POST,request.body.asJson.get将抛出异常。

  1. body.asJson返回类型为Option[JsValue],如果请求不是JSON,则返回None
  2. get上致电None会引发java.util.NoSuchElementException
  3. 此异常表现为Play返回500 Internal Server Error
  4. 您应该使用使用JSON body parser的操作替换def index = Action.async ...

    import play.api.mvc.BodyParsers.parse
    
    def index = Action.async(parse.json) ...
    

    这实现了一些目标:

    1. 它更自我记录(动作在方法声明中说“我期待JSON”)。
    2. 如果POST不是JSON,Play将生成400 Bad Request。这比500 Internal Server Error。{/ li>造成的None.get更合适
    3. 这将使request.body成为JsValue而不是AnyContent。因此,您只需使用request.body.asJson.get替换request.body即可。一般来说,你应该避免调用Option.get,因为它不安全,并且通常有更好的方法来实现你想要的东西(在这种情况下使用适当的身体解析器恰好是更好的方法)。
    4. 现在这个测试不再编译,而不是抛出由None.get引起的异常:

      val fakeJson = """{ "foo":"bar" }"""
      val fakeRequest = FakeRequest()
        .withHeaders("Content-type" -> "application/json")
        .withBody(fakeJson)
      val index = controller.index()(fakeRequest)
      status(index) must equalTo(OK)
      

      强制您将其替换为答案中的版本:

      val fakeJson = play.api.libs.json.Json.parse("""{ "foo":"bar" }""") 
      val fakeRequest = FakeRequest().withBody(fakeJson)              
      val index = controller.index()(fakeRequest)                         
      status(index) must equalTo(OK)
      

      我的最后建议是你使用Json.obj来清理你的测试:

      val fakeJson = Json.obj("foo" -> "bar")
      

答案 1 :(得分:0)

<强> [SELF-解决]

挣扎了几个小时,问题解决了withJsonBody

"Application" should {                                                
  val controller = new Application                                    
  val fakeJson = play.api.libs.json.Json.parse("""{ "foo":"bar" }""") 
  val fakeRequest = FakeRequest().withJsonBody(fakeJson)              
  val index = controller.index()(fakeRequest)                         
  status(index) must equalTo(OK)                                      
}

欢迎任何其他建议。