2016-09-30 6 views
2

В API REST, реализованном с помощью Play Framework (2.4), я использую Action(parse.json) для анализа JSON из входящего запроса запроса POST.Как отключить ответы об ошибках HTML при использовании Action (parse.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

Есть вы пытались переопределить метод перехватчика onError для Global и уловить исключение, настроенное для настройки сообщения об ошибке? – Richeek

+0

В моем приложении нет Global; это [устарело] (https://www.playframework.com/documentation/2.4.x/GlobalSettings) в последних версиях Play. Вместо этого предоставление [custom HttpErrorHandler] (http://stackoverflow.com/a/39794930/56285) - это путь. – Jonik

+0

А я вижу. Я использую игру 2.4 – Richeek

ответ

2

Долгий 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

+0

play 2.4 имеет плохую поддержку для DI ... – phadej

+0

Спасибо! Я взял на себя смелость добавить пример кода прямо здесь. Я использую Guice (подход по умолчанию в Play 2.4+), поэтому проблем нет. – Jonik

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 «s Iteratee. К сожалению, Play не раскрывает строительные блоки, поэтому требуется много копий-папок, если вы хотите сделать это, как в Play.

В качестве альтернативы вы можете сделать немой Iteratee.fold и направить Json.parse на накопленный bytearray, это нехорошо; Вы, вероятно, хотите, чтобы проверить принимать заголовки, использовать 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 
Смежные вопросы