处理(序列化,实际上..)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()
}
这些都没有任何区别,DeserializationException
s 仍然被编码到 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。
我需要在我的 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()
}
这些都没有任何区别,DeserializationException
s 仍然被编码到 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。