处理(序列化,实际上..)akka-http中的DeserializationException

Handling (serializing, actually..) DeserializationException in akka-http

我需要在我的 http 响应中将异常消息编码为特定的 JSON 格式。我想知道如何在我的路线中或路线之外捕获 DeserializationException 或对 DeserializationException 进行编码。

我尝试了以下方法:

1) 我的路线周围的异常处理程序:

val exceptionHandler = ExceptionHandler {
    case e: DeserializationException => complete(StatusCodes.BadRequest, ServiceBrokerError(e.getMessage))
}

2) JSON 格式在 DeserializationException

的隐式范围内
implicit object DeserializationExceptionFormat extends DefaultJsonProtocol with RootJsonFormat[DeserializationException] {

  def write(e: DeserializationException) = JsObject("message" -> JsString(e.getMessage))
  def read(v: JsValue) = throw new NotImplementedError()

}

这些都没有任何区别,DeserializationExceptions 仍然被编码到 http 响应正文中,如下所示:

HTTP/1.1 400 Bad Request
Content-Length: 74
Content-Type: text/plain; charset=UTF-8
Date: Thu, 20 Apr 2017 21:23:11 GMT
Server: akka-http/10.0.1

The request content was malformed:
Node count may not be a floating number

非常感谢任何建议。

一些额外的上下文——我的路由依赖于 spray-json 集成来将请求实体映射到对象,例如:

// service instance management related routes
put {
  entity(as[CreateInstance]) { createInstance => handleCreateInstance(s"cluster-$clusterId", createInstance) }
}

如果将异常处理程序更改为

会怎样

val exceptionHandler = ExceptionHandler { case e: DeserializationException => complete(StatusCodes.BadRequest, e) }

我应该看看 MarshallingDirectives.entity 因为它确实吞下了与序列化相关的异常并将它们转化为拒绝:

/**
    * Unmarshalls the requests entity to the given type passes it to its inner Route.
   * If there is a problem with unmarshalling the request is rejected with the [[Rejection]]
   * produced by the unmarshaller.
   *
   * @group marshalling
   */
  def entity[T](um: FromRequestUnmarshaller[T]): Directive1[T] =
    extractRequestContext.flatMap[Tuple1[T]] { ctx ⇒
      import ctx.executionContext
      import ctx.materializer
      onComplete(um(ctx.request)) flatMap {
        case Success(value) ⇒ provide(value)
        case Failure(RejectionError(r)) ⇒ reject(r)
        case Failure(Unmarshaller.NoContentException) ⇒ reject(RequestEntityExpectedRejection)
        case Failure(Unmarshaller.UnsupportedContentTypeException(x)) ⇒ reject(UnsupportedRequestContentTypeRejection(x))
        case Failure(x: IllegalArgumentException) ⇒ reject(ValidationRejection(x.getMessage.nullAsEmpty, Some(x)))
        case Failure(x) ⇒ reject(MalformedRequestContentRejection(x.getMessage.nullAsEmpty, x))
      }
    } & cancelRejections(RequestEntityExpectedRejection.getClass, classOf[UnsupportedRequestContentTypeRejection])

因此我必须处理的是 Rejection,而不是 DeserializationException。