How can I programmatically execute Route of reject

2019-08-12 06:40发布

问题:

How can I programmatically execute Route of rejection handler and get resulting HttpEntity ?

For example assuming that I have RequestContext object and Rejection object I'd like to execute RejectionHandler.default on it and get HttpEntity.

Here is example of what I'd like to do:

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")
    }

    val route: Route = extractRequestContext { ctx =>
      mapResponseEntity(prefixEntity) {
        // Here I want result of `complete` route from RejectionHandler.default
      }
    }
    route
  }
    .handleNotFound {
      complete((NotFound, "Not here!"))
    }
    .result()

回答1:

I think I get the gist of what you want. You can just apply the default rejection handler in there where your comment is. The only thing is that the apply there returns an Option that will be None if the rejections encountered don't hit on anything in that rejection handler. This is unlikely given that you are using the default handler and it pretty much handles everything, but you still need to account for it in the code (hence my getOrElse that results in a generic InternalServerError). The modified code would look like this:

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")
    }

    val route: Route = extractRequestContext { ctx =>
      mapResponseEntity(prefixEntity) {
        defaultRejectionHandler.apply(rejections).getOrElse{
          complete(StatusCodes.InternalServerError)
        }
      }
    }
    route
  }
  .handleNotFound {
    complete((NotFound, "Not here!"))
  }
  .result()

This compiles, but I did not actually test it to see if we get the desired effect. Also, I didn't need the RequestContext value ctx, so you might be able to remove that extractRequestContext layer in there.