Skip to content

Instantly share code, notes, and snippets.

@magnusart
Created July 17, 2015 08:24
Show Gist options
  • Save magnusart/a3b9321d6e391889d59a to your computer and use it in GitHub Desktop.
Save magnusart/a3b9321d6e391889d59a to your computer and use it in GitHub Desktop.
Route DSL type errors
[error] found : akka.http.scaladsl.server.RequestContext => scala.concurrent.Future[akka.http.scaladsl.server.RouteResult]
[error] required: scala.concurrent.Future[akka.http.scaladsl.server.RouteResult]
[error] eitherRejectionAsync( upload( ctx.request ) )( executionContext ) { response: HttpResponse ⇒
def eitherRejection[T]( result: Either[Rejection, T] ): Directive1[T] = result match {
case Left( rej ) ⇒ reject( rej )
case Right( t ) ⇒ provide( t )
}
def eitherRejectionAsync[T]( result: Future[Either[Rejection, T]] )(
implicit
executionContext: ExecutionContext
): Directive1[T] = onSuccess( result ).flatMap( eitherRejection )
def upload( req: HttpRequest ): Future[Either[Rejection,HttpResponse]] =
Source.single( Right( req ) ).
via( storageClient.upload( uploadFlow ) ).
runWith( Sink.head )
val route:Route = path( "upload" ) {
( get & authenticateJwt( validator ) ) { userContext ⇒ ctx ⇒
eitherRejectionAsync( upload( ctx.request ) )( executionContext ) { response: HttpResponse ⇒
complete( response )
}
}
}
@jrudolph
Copy link

Here's how you could rewrite it (stripped the irrelevant parts):

  import akka.http.scaladsl._
  import model._
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext
  import akka.stream.scaladsl.Source

  def eitherRejection[T]( result: Either[Rejection, T] ): Directive1[T] = result match {
    case Left( rej )  reject( rej )
    case Right( t )   provide( t )
  }

  def eitherRejectionAsync[T](result: Future[Either[Rejection, T]]): Directive1[T] =
    onSuccess(result).flatMap(eitherRejection) // no ExecutionContext needed

  def upload( req: HttpRequest ): Future[Either[Rejection,HttpResponse]] = ???

  val route:Route = path( "upload" ) {
    get {
      extractRequest { request =>
        val uploaded = upload(request)
        eitherRejectionAsync(uploaded) { response: HttpResponse 
          complete(response)
        }
      }
    }
  }

Some best practices:

  • try to avoid accessing the RequestContext manually by writing a Route manually, it's easy to get it wrong. This is what happened here: By using ctx => you signalled that you want to write a route manually (remember that type Route = RequestContext => Future[RouteResult]), so you would have to return a Future[RouteResult] but you tried to return a Route (the result of eitherRejectionAsync(...).apply(res => complete(response))). You could fix it by actually executing the route with ctx => eitherRejectionAsync(...).apply(res => complete(response)).apply(ctx) but if you find yourself doing that, you can just remove ctx => ....apply(ctx).
  • instead of accessing the RequestContext manually, use extraction directives to access parts of the request context you are interested in
  • don't require implicit ExecutionContext when working with routes: if necessary (which it seems wasn't here), you can always extract one from the RequestContext. Use the extractExecutionContext {implicit ec => } directive. (Same for the materializer.)

HTH

@magnusart
Copy link
Author

Aha, thanks! I forgot I could use extractRequest so I figured I'd access it directly. It worked now and thanks for the background as well, it helps a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment