play2: FakeRequest().withBody(body) 在控制器中自动转换为 Request[AnyContentAsEmpty]
play2: FakeRequest().withBody(body) automatically converted to Request[AnyContentAsEmpty] in controller
我正在开发一个 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$$anonfun$apply.apply(Application.scala:11)
[error] controllers.Application$$anonfun$index$$anonfun$apply.apply(Application.scala:11)
我在控制器中插入println(request.body)
,发现请求体是AnyContentAsEmpty
,意思是fakeJson
从fakeRequest
中移除。
如何将 JSON 正确附加到 FakeRequest?
*注意:虽然我可以像 FakeRequest(POST, '/', FakeHeaders(), fakeJson)
这样写,但我认为这不好,因为控制器规范不应该处理 HTTP 方法或路由。
如有任何帮助,我将不胜感激。
[自行解决]
又苦苦挣扎了几个小时,使用 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)
}
欢迎提出任何其他建议。
如果客户端对您的操作发出 HTTP POST 请求 不是 JSON,request.body.asJson.get
将抛出一个异常。
body.asJson
具有 return 类型 Option[JsValue]
,如果请求不是 JSON,则它 return 是 None
。
- 在
None
上调用 get
会引发 java.util.NoSuchElementException
。
- 此异常表现为播放 return
500 Internal Server Error
。
您应该将 def index = Action.async ...
替换为使用 JSON body parser 的操作:
import play.api.mvc.BodyParsers.parse
def index = Action.async(parse.json) ...
这实现了一些事情:
- 它更多的是自我记录(操作在方法声明中说 "I expect JSON")。
- 如果 POST 不是 JSON,Play 将生成
400 Bad Request
。这比你的 None.get
. 造成的 500 Internal Server Error
更合适
- 它将使
request.body
成为 JsValue
而不是 AnyContent
。所以你可以简单地用 request.body
替换 request.body.asJson.get
。一般来说,你应该避免调用 Option.get
,因为它不安全,而且通常有更好的方法来实现你想要的(在这种情况下,使用适当的正文解析器恰好是更好的方法)。
现在这个测试不再编译,而不是抛出由 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")
我正在开发一个 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$$anonfun$apply.apply(Application.scala:11)
[error] controllers.Application$$anonfun$index$$anonfun$apply.apply(Application.scala:11)
我在控制器中插入println(request.body)
,发现请求体是AnyContentAsEmpty
,意思是fakeJson
从fakeRequest
中移除。
如何将 JSON 正确附加到 FakeRequest?
*注意:虽然我可以像 FakeRequest(POST, '/', FakeHeaders(), fakeJson)
这样写,但我认为这不好,因为控制器规范不应该处理 HTTP 方法或路由。
如有任何帮助,我将不胜感激。
[自行解决]
又苦苦挣扎了几个小时,使用 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)
}
欢迎提出任何其他建议。
如果客户端对您的操作发出 HTTP POST 请求 不是 JSON,request.body.asJson.get
将抛出一个异常。
body.asJson
具有 return 类型Option[JsValue]
,如果请求不是 JSON,则它 return 是None
。- 在
None
上调用get
会引发java.util.NoSuchElementException
。 - 此异常表现为播放 return
500 Internal Server Error
。
您应该将 def index = Action.async ...
替换为使用 JSON body parser 的操作:
import play.api.mvc.BodyParsers.parse
def index = Action.async(parse.json) ...
这实现了一些事情:
- 它更多的是自我记录(操作在方法声明中说 "I expect JSON")。
- 如果 POST 不是 JSON,Play 将生成
400 Bad Request
。这比你的None.get
. 造成的 - 它将使
request.body
成为JsValue
而不是AnyContent
。所以你可以简单地用request.body
替换request.body.asJson.get
。一般来说,你应该避免调用Option.get
,因为它不安全,而且通常有更好的方法来实现你想要的(在这种情况下,使用适当的正文解析器恰好是更好的方法)。
500 Internal Server Error
更合适
现在这个测试不再编译,而不是抛出由 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")