使用 Action(parse.json) 时如何禁用 HTML 错误响应?
How to disable HTML error responses when using Action(parse.json)?
在使用 Play Framework (2.4) 实现的 REST API 中,我使用 Action(parse.json)
从传入的 POST 请求正文中解析 JSON。
使用我当前的代码(见下文),
发布有效 JSON 且缺少字段(例如 {"foo": ""}
)会产生
400 响应正文 {"error":"Missing input fields"}
。这很好并且在意料之中。
发帖完全无效JSON(如{,,,}
或{[=18=]}
)产生
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))
长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 的情况下管理依赖项,则必须在 ApplicationLoader
中提供您的自定义 HttpErrorHandler
Json.parse(bytes.iterator.asInputStream)
Json.parse
委托给 Jacksons 的 parseJsValue
,这会引发异常。我不能正确地遵循代码,为什么在 Iteratee
框架中没有正确捕获异常。
最直接的解决方案是复制 ContentTypes.scala
中的代码,捕获 Json.parse
附近的异常,并将其正确转换为 BodyParser
的 Left
值 Iteratee
。不幸的是,Play 不会公开构建块,因此如果您想像在 Play 中那样做,就需要很多 copy-paste。
或者你可以在累积的字节数组上做哑 Iteratee.fold
和直接 Json.parse
,那不好;您可能想要检查接受 headers ,使用 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
在使用 Play Framework (2.4) 实现的 REST API 中,我使用 Action(parse.json)
从传入的 POST 请求正文中解析 JSON。
使用我当前的代码(见下文),
发布有效 JSON 且缺少字段(例如
{"foo": ""}
)会产生 400 响应正文{"error":"Missing input fields"}
。这很好并且在意料之中。发帖完全无效JSON(如
{,,,}
或{[=18=]}
)产生 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))
长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 的情况下管理依赖项,则必须在 ApplicationLoader
HttpErrorHandler
Json.parse(bytes.iterator.asInputStream)
Json.parse
委托给 Jacksons 的 parseJsValue
,这会引发异常。我不能正确地遵循代码,为什么在 Iteratee
框架中没有正确捕获异常。
最直接的解决方案是复制 ContentTypes.scala
中的代码,捕获 Json.parse
附近的异常,并将其正确转换为 BodyParser
的 Left
值 Iteratee
。不幸的是,Play 不会公开构建块,因此如果您想像在 Play 中那样做,就需要很多 copy-paste。
或者你可以在累积的字节数组上做哑 Iteratee.fold
和直接 Json.parse
,那不好;您可能想要检查接受 headers ,使用 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