简体   繁体   中英

Scala future with akka-http

I need to write simple web service with akka-http and reactivemongo.

Function to save data looks like this

 def saveRoute(route: Route):Future[WriteResult] = {
    collection.insert(route)
 }

a code that calls this function looks like this

val userRoutes = {
    logRequestResult("akka-http-microservice") {
      path("routes") {
        (post & entity(as[Route])) { route =>
          Database.saveRoute(route)
        }
      }
    }
  }

I need to return result with inserted ID of Route and do this without making the thread to wait. if try

Database.saveRoute(route).onComplete{
            case Success(r) => complete(r.toString)
            case Failure(e) => complete(e.getMessage)
          }

It cannot compile, because it doesn't return value. I know how to make it in dirty way, but really want to make in appropriate manner.

What should be done in this case?

Seems like I've found most efficient way to do this. It's built in onComplete directive

(path("routes" / "add") & post & entity(as[Route])) {
    route =>
      onComplete(routesController.addRoute(route)) {
        case Success(result) => complete(StatusCodes.Created, "OK")
        case Failure(ex) => complete(new ErrorResponse(StatusCodes.InternalServerError.intValue, ErrorResponse.ERROR, ex.getMessage))
      }
  }

Use onSuccess to handle the valid response when the future finishes and handleExceptions to handle when the future does not succeed.

   val userRoutes = {
    handleExceptions(mongoDbExceptionHandler) {
      logRequestResult("akka-http-microservice") {
        path("routes") {
          (post & entity(as[Route])) { route =>
            onSuccess(Database.saveRoute(route)) { result =>
              complete(result)
            }
          }
        }
      }
    }
  }

  // Something like this for whatever the exceptions you expect are
  val mongoDbExceptionHandler = ExceptionHandler {
    case ex: MongoDbReadException => complete(HttpResponse(InternalServerError, "No database")))
  }

onSuccess: http://doc.akka.io/docs/akka/2.4.9/scala/http/routing-dsl/directives/future-directives/onSuccess.html

handleExceptions: http://doc.akka.io/docs/akka/2.4.9/scala/http/routing-dsl/exception-handling.html

You can map over the future and then complete the request like below.

val future = Database.saveRoute(route)
val response = future.map(_.getId).recover(_.getMessage)
complete(response)

On a side note, for handling exceptions, it is a good practice to have a ExceptionHandler and wrap it with your route. You can find example here .

You have few option i will try to put the most commonly used ones for REST API based solutions:

OnSuccess use it when you want your expectations to be bubbled and handled by expectionHandler

  concat(
    path("success") {
      onSuccess(Future { "Ok" }) { extraction =>
        complete(extraction)
      }
    },
    path("failure") {
      onSuccess(Future.failed[String](TestException)) { extraction =>
        complete(extraction)
      }
    }
  )

https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/future-directives/onSuccess.html

onComplete: When you want to manually handle the exception. Try Monad wrapped.

val route =
  path("divide" / IntNumber / IntNumber) { (a, b) =>
    onComplete(divide(a, b)) {
      case Success(value) => complete(s"The result was $value")
      case Failure(ex)    => complete((InternalServerError, s"An error occurred: ${ex.getMessage}"))
    }
  }

https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/future-directives/onComplete.html

How about this, replace:

Database.saveRoute(route)

with:

complete(Database.saveRoute(route).map(_.toString).recover(_.getMessage))

When you use RequestContext you should use something like this:

import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.RouteResult.{Complete, Rejected}
...
  val myRoute: Route = (path("my-path") & get) { req: RequestContext =>
    val futureResp: Future[HttpResponse] = ???
    futureResp.map(resp => RouteResult.Complete(resp))
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM