在使用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))
答案 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)
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