2016-02-02 4 views
1

В API, который я пишу, я хочу взять и вернуть JSON даже в случае ошибок. Я пытаюсь выяснить, как сохранить все поведение defaultRejectionHandler, но преобразовать коды состояния и текст в объект JSON. Поскольку поведение по умолчанию указывается в вызовах функций, а не как структура данных, кажется, что единственный способ сделать это - преобразовать HttpEntity результата, который он производит. Есть ли простой способ сделать это?Возврат ошибок JSON в API akka-http

ответ

1

У меня была своя собственная версия, но у меня были грубые края, которые мне не нравились. ruslan's answer дал мне несколько идей для улучшения. Вот что я придумал, синтезируя лучшее из обоих подходов:

/** 
    * Modifies the Akka-Http default rejection handler to wrap the default 
    * message in JSON wrapper, preserving the original status code. 
    * 
    * @param rejectionWrapper wraps the message in a structure to format the 
    *       resulting JSON object 
    * @param writer writer for the wrapper type 
    * @tparam WrapperType type of the wrapper 
    * @return the modified rejection handler 
    */ 
def defaultRejectionHandlerAsJson[WrapperType](rejectionWrapper: String => WrapperType)(implicit writer: JsonWriter[WrapperType]) = { 
    def rejectionModifier(originalMessage: String): String = { 
    writer.write(rejectionWrapper(originalMessage)).prettyPrint 
    } 
    modifiedDefaultRejectionHandler(rejectionModifier, ContentTypes.`application/json`) 
} 

/** 
    * Modifies the Akka-Http default rejection handler, converting the default 
    * message to some other textual representation. 
    * 
    * @param rejectionModifier the modifier function 
    * @param newContentType the new Content Type, defaulting to text/plain 
    *      UTF-8 
    * @return the modified rejection handler 
    */ 
def modifiedDefaultRejectionHandler(rejectionModifier: String => String, newContentType: ContentType.NonBinary = ContentTypes.`text/plain(UTF-8)`) = new RejectionHandler { 
    def repackageRouteResult(entity: ResponseEntity): ResponseEntity = entity match { 
    // If the entity isn't Strict (and it definitely will be), don't bother 
    // converting, just throw an error, because something's weird. 
    case strictEntity: HttpEntity.Strict => 
     val modifiedMessage = rejectionModifier(strictEntity.data.utf8String) 
     HttpEntity(newContentType, modifiedMessage) 
    case other => 
     throw new Exception("Unexpected entity type") 
    } 

    def apply(v1: Seq[Rejection]): Option[Route] = { 
    // The default rejection handler should handle all possible rejections, 
    // so if this isn't the case, return a 503. 
    val originalResult = RejectionHandler.default(v1).getOrElse(complete(StatusCodes.InternalServerError)) 
    Some(mapResponseEntity(repackageRouteResult) { 
     originalResult 
    }) 
    } 
} 
2

Вы можете написать что-то подобное в вашей HttpService

private val defaultRejectionHandler = RejectionHandler.default 

implicit def myRejectionHandler = 
    RejectionHandler.newBuilder() 
    .handleAll[Rejection] { rejections ⇒ 

    def prefixEntity(entity: ResponseEntity): ResponseEntity = entity match { 
     case HttpEntity.Strict(contentType, data) => { 
     import spray.json._ 
     val text = ErrorResponse(0, "Rejection", data.utf8String).toJson.prettyPrint 
     HttpEntity(ContentTypes.`application/json`, text) 
     } 
     case _ => 
     throw new IllegalStateException("Unexpected entity type") 
    } 

    mapResponseEntity(prefixEntity) { 
     defaultRejectionHandler(rejections).getOrElse { 
     complete(StatusCodes.InternalServerError) 
     } 
    } 
    }.handleNotFound { 
    complete(StatusCodes.Forbidden -> ErrorResponse(StatusCodes.NotFound.intValue, "NotFound", "Requested resource is not found")) 
    }.result() 

где ErrorResponse возможно

case class ErrorResponse(error: ErrorInfo) 
case class ErrorInfo(code: Int, `type`: String, message: String) 

, для которого вы можете определенной JSon marshallers.

+0

Черт, это сложно. О таком сложном, как то, как я сейчас это решаю. Кажется, что в API отсутствует что-то, что нет более простого способа. – acjay

+0

Дайте мне знать, если вы найдете более простой способ: – expert