2016-02-14 4 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.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-методы или маршруты.

Буду признателен за любую помощь.

ответ

1

Если клиент делает HTTP-сообщение POST вашему действию с запросом не JSON, request.body.asJson.get выдает исключение.

  1. body.asJson имеет тип возвращаемого Option[JsValue] и возвращает None, если запрос не был JSON.
  2. Вызов get на None throws a java.util.NoSuchElementException.
  3. Это исключение проявляется как воспроизведение, возвращающее 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. Play будет генерировать 400 Bad Request, если POST не был JSON. Это более подходит, чем 500 Internal Server Error, вызванное вашим None.get.
  3. Будет сделано request.body a JsValue вместо AnyContent. Таким образом, вы можете заменить request.body.asJson.get просто request.body. В общем, вам следует избегать вызова 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") 
+0

Благодарим за подробный ответ. Я хотел бы задать еще одну вещь: почему контроллер с 'parse.json' НЕ анализирует тело' fakeRequest' (это строка), в то время как он автоматически анализирует тело запроса «POST» от клиента (что-то вроде «завитки»)? В чем разница? – 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 или application/json body]' со статусом 415). Как я могу решить эту ситуацию? – iTakeshi

+0

'val index = new Application(). Index() (FakeRequest(). WithBody (Joo.obj (" foo "->" bar ")))', за которым следует 'status (index), должен равныйTo (OK)' работает для меня (тест компилируется и проходит). – danielnixon

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

Любые другие предложения приветствуются.

 Смежные вопросы

  • Нет связанных вопросов^_^