2016-02-14 45 views
2

我工作的一齣戲 - 2.4項目,並寫了一個控制器,如:在 play2:FakeRequest()withBody(身體)自動轉換爲請求[AnyContentAsEmpty]在控制器

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.indexconf/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方法或路由。

我將不勝感激任何幫助。

回答

1

如果客戶端通過請求不是 JSON,request.body.asJson.get將發出異常,請求您的操作發出HTTP POST。

  1. body.asJson有返回類型Option[JsValue],它返回一個None如果請求不是JSON。
  2. 致電getNone拋出java.util.NoSuchElementException
  3. 此例外情況表現爲Play返回500 Internal Server Error

此時應更換def index = Action.async ...與使用JSON body parser而不是操作:

import play.api.mvc.BodyParsers.parse 

def index = Action.async(parse.json) ... 

這實現了幾件事情:

  1. 它更自我記錄(動作說:「我期待JSON「就在方法聲明中)。
  2. 如果POST不是JSON,播放將生成400 Bad Request。這比None.get引起的500 Internal Server Error更合適。
  3. 它將使request.body a JsValue而不是AnyContent。所以你可以簡單地用request.body代替request.body.asJson.get。一般來說,你應該避免調用Option.get,因爲它不安全,通常有更好的方法來實現你想要的東西(在這種情況下使用適當的body parser碰巧是更好的方法)。

現在這個測試不再編譯,而不是投擲造成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") 
+0

謝謝你的非常詳細的答案。我想再問一個問題:爲什麼具有'parse.json'的控制器不會解析'fakeRequest'(這是一個字符串)的主體,而它會自動解析來自客戶端的POST請求的主體(像'curl')?有什麼不同? – iTakeshi

+0

UPDATE:更改爲您的建議,但測試不會說編譯時指出'index'是'play.api.libs.iteratee.Iteratee [Array [Byte],play.api.mvc.Result]'的一個實例,但編譯器要求它是'scala.concurrent.Future [play.api.mvc.Result]',因爲Action.async期望。我嘗試將測試代碼更改爲status(index.run)並編譯,但運行時錯誤(HTML響應表示爲[預期文本/ json或應用程序/ json主體],狀態爲415)。我怎樣才能解決這種情況? – iTakeshi

+0

'val index = new Application()。index()(FakeRequest()。withBody(Json.obj(「foo」 - >「bar」)))''後面加上'status(index)must equalTo(OK)'works對我來說(測試編譯和傳遞)。 – danielnixon

0

[自拍解決]

另一個掙扎幾個小時,問題是用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)          
} 

任何其他建議都歡迎。