如何在使用Action(parse.json)时禁用HTML错误响应?

时间:2016-09-30 14:59:28

标签: json scala playframework playframework-2.4

在使用Play Framework(2.4)实现的REST API中,我使用Action(parse.json)从传入的POST请求主体解析JSON。

使用我当前的代码(见下文),

  • 发布包含缺少字段的有效JSON(例如{"foo": ""}) 400与响应主体{"error":"Missing input fields"}。这是好的和预期的。

  • 发布完全无效的JSON(例如{,,,}{\00})会产生 400 long HTML response body。这来自于某个地方 parse.json

在后一种情况下,如何摆脱HTML响应主体?我希望响应主体包含一个简短的JSON错误消息(例如{"error":"Invalid JSON input"} ),或者什么也没有。 Play是否有此配置选项,还是需要创建自定义操作?什么是最简单的方法?

控制器方法:

def test = Action(parse.json) { request =>
  request.body.validate[Input].map(i => {
    Ok(i.foo)
  }).getOrElse(BadRequest(errorJson("Missing input fields")))
}

上面使用的其他内容:

case class Input(foo: String, bar: String)

object Input {
  implicit val reads = Json.reads[Input]
}

case class ErrorJson(error: String)

object ErrorJson {
  implicit val writes = Json.writes[ErrorJson]
}

private def errorJson(message: String) = Json.toJson(ErrorJson(message))

2 个答案:

答案 0 :(得分:3)

长html由默认HttpErrorHandler生成。您可以按照this guide提供自己的服务。引用示例代码:

class ErrorHandler extends HttpErrorHandler {

  def onClientError(request: RequestHeader, statusCode: Int, message: String) = {
    Future.successful(
      Status(statusCode)("A client error occurred: " + message)
    )
  }

  def onServerError(request: RequestHeader, exception: Throwable) = {
    Future.successful(
      InternalServerError("A server error occurred: " + exception.getMessage)
    )
  }
}

注意:如果您在没有Guice的情况下管理您的依赖项,则必须在HttpErrorHandler

中提供自定义ApplicationLoader

答案 1 :(得分:2)

简短回答:在https://github.com/playframework/playframework/blob/48a76b851946261a952a2edcc4b8dbeeb303e07b/framework/src/play/src/main/scala/play/api/mvc/ContentTypes.scala#L379

Json.parse(bytes.iterator.asInputStream)

Json.parse代表Jacksons的parseJsValue,它会抛出异常。我无法正确地遵循代码,为什么在Iteratee框架内没有正确捕获异常。

最简单的解决方案是复制ContentTypes.scala中的代码,抓住Json.parse周围的异常,并将其正确地转换为Left BodyParser的{​​{1}}值Iteratee }}。不幸的是Play不会暴露构建块,所以如果你想在Play中这样做,就需要大量的复制粘贴。

或者,您可以对累积的bytearray执行哑Iteratee.fold和直接Json.parse,这样做并不好;您可能想要检查接受标头,使用bytearray构建器,并限制输入的最大大小

val betterJson: BodyParser[JsValue] = BodyParser("better json") { _request =>
  import play.api.libs.iteratee.Execution.Implicits.defaultExecutionContext

  Iteratee.fold(new Array[Byte](0)) { (bytes: Array[Byte], acc: Array[Byte]) =>
    bytes ++ acc
  } map { bytes =>
    val res: Either[Result, JsValue] = Try(Json.parse(bytes)) match {
      case Success(v) => Right(v)
      case Failure(e) => Left(BadRequest("bad json"))
    }
    res
  }
}

在控制器中使用betterJson

def test = Action(betterJson) { request =>
  request.body.validate[Int].map(i => {
    Ok(i.toString)
  }).getOrElse(BadRequest("my error"))
}

经过测试:

// works:
// $ curl -H "Content-Type: application/json" -X POST -d '123' localhost:9000/test
// 123
// $ curl -H "Content-Type: application/json" -X POST -d '{}' localhost:9000/test
// my error
//
// issue:
// $ curl -H "Content-Type: application/json" -X POST -d '.' localhost:9000/test
// bad json