Akka HTTP route post entity string, complete with Future - scala

I have an Akka HTTP daemon. Assume that I want to receive some client data in JSON format and save it into a database asynchronously. I wrote a route in a POST branch:
path("product") {
entity(as[String]) { json =>
val saveFuture: Future[Unit] = Serialization.read[Product](json).save()
complete("")
}
}
I've found that complete can be put into an onSuccess statement like:
path("success") {
onSuccess(Future { "Ok" }) { extraction =>
complete(extraction)
}
}
But I can't understand how to glue them together.

You can nest the directives:
path("product") {
entity(as[String]) { json =>
val saveFuture: Future[Unit] = Serialization.read[Product](json).save()
onSuccess(saveFuture) {
complete("json was saved")
}
}
}

Related

akka http handleNotFound rejection is only working for POST method

i have the following akka http rejection handling code taken from https://doc.akka.io/docs/akka-http/current/routing-dsl/rejections.html
val message = "The requested resource could not be found."
implicit def myRejectionHandler = RejectionHandler.newBuilder()
.handleNotFound {
complete(HttpResponse(NotFound
,entity = HttpEntity(ContentTypes.`application/json`, s"""{"rejection": "$message"}"""
)))
}.result()
val route: Route = handleRejections(myRejectionHandler) {
handleExceptions(myExceptionHandler) {
concat(
path("event-by-id") {
get {
parameters('id.as[String]) {
id =>
complete("id")
}
}
}
,
post {
path("create-event") {
entity(as[Event]) {
event =>
complete(OK, "inserted")
}
}
}
)
}
}
}
val bindingFuture = Http().bindAndHandle(route, hostName, port)
when i hit localhost:8080/random
i got the message
HTTP method not allowed, supported methods: POST
and when i select POST and hit localhost:8080/random
i got the message
{
"rejection": "The requested resource could not be found."
}
why i did not get the same message when my route request was GET ?
in the docs the handleNotFound was working with GET request https://doc.akka.io/docs/akka-http/current/routing-dsl/rejections.html
This is happens, probably because of order of directives, you are using: in your configuration if incoming request does not match with event-by-id URL path, then it goes to the next handler, which expects that request should have POST method first of all, because post directive goes first, before path("create-event").
What you can try to do is change directives order to the next one, for second route:
path("create-event") {
post {
entity(as[Event]) { event =>
complete(OK, "inserted")
}
}
}
Hope this helps!

Basic Authentication in play framework

How to implement Basic Authentication for web sockets using play framework.
I am creating a web socket using play framework.
I would like to do basic authentication and send 401 if authentication fails.
Below is my code and i am not able to send "{code=401, message=unauthorized access}" as response
def ChatServer(): WebSocket = WebSocket.accept[String, String] { request =>
if (Util.doBasicAuthentication(request.headers)) {
ActorFlow.actorRef { out =>
ChatActor.props(out)
}
} else throw new RuntimeException("Unauthorized Access")
}
Whenever authentication fails, i am not able to send the response back as "unauthorized access" instead i am ending up with exceptions
As described in the Play documentation, use WebSocket.acceptOrResult:
def socket = WebSocket.acceptOrResult[String, String] { request =>
Future.successful {
if (Util.doBasicAuthentication(request.headers)) {
Right(ActorFlow.actorRef { out =>
ChatActor.props(out)
})
} else {
Left(Unauthorized)
}
}
}

akka-http mapResponse ignores some responses

Question about akka-http server. I have a top-level mapResponse which wraps all other routes. This mapResponse wraps all responses in the json. And recently I found that sometimes my server returns 503 error with plain-text explanation. I found that the reason is akka.http.server.request-timeout config value - it was too small. But why I didn't catch these responses with the top-level mapResponse? I found that this 503 response is created in HttpServerBluePrint.TimeoutAccessImpl.apply and it somehow ignores my mapResponse.
Here is the minimal reproduction code. I wrap all my routes in logAndReturn200 function. I expect that all my routes will return StatusCodes.OK. But /bar returns timeout error.
val logAndReturn200 = mapResponse { response =>
println(response)
HttpResponse(StatusCodes.OK)
}
val route = {
logAndReturn200 {
get {
path("foo") {
complete("OK")
} ~
path("bar") {
val promise = Promise[Unit]()
actorSystem.scheduler.scheduleOnce(10.seconds) {
promise.success(())
}
onSuccess(promise.future) {
complete("OK")
}
}
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

spray authenticate directive returns different HTTP status codes

I am trying a basic authentication on post request in spray.io 1.3.2 using authenticate directive. My code looks following:
val route: Route = {
pathPrefix("ato") {
pathPrefix("v1") {
path("orders" / "updateStatus") {
post {
authenticate(BasicAuth(userPasswordAuthenticator _, realm = "bd ato import api")) {
user =>
entity(as[String]) {e =>
complete {
s"Hello $e "
}
}
}
}
}
}
}
}
def userPasswordAuthenticator(userPass: Option[UserPass]): Future[Option[String]] =
Future {
if (userPass.exists(up => up.user == ato_import_v1_usr && up.pass == ato_import_v1_pwd)) Some("ato_v1")
else None
}
This works perfectly fine, authorized Status Ok 200, unauthorized 401. However when the order of directives is changed as follows:
val route: Route = {
pathPrefix("ato") {
pathPrefix("v1") {
authenticate(BasicAuth(userPasswordAuthenticator _, realm = "bd ato import api")) {
user =>
path("orders" / "updateStatus") {
post {
entity(as[String]) {e =>
complete {
s"Hello $e "
}
}
}
}
}
}
}
}
I am getting Status 405, HTTP method not allowed for unauthorized access. I am not sure why that happens. From certain point it make sense, path is not matched because of missing credentials etc.
Could someone please clarify that?
The reason why I wanted to put authorization at v1 level is that I wanted to make every version protected by different password. Is there a way how to achieve that? What is the best practice in chaining directives?
I would like to follow DRY principle.
Thanks

Unable to run a POST action in play framework 2.2

I have a similar problem to this person and I am unsure why.
my post action is called from a form submission and the controller code
def processFreeDoc = UserAwareAction {
implicit request => {
userEmailForm.bindFromRequest.fold(
formWithErrors => {
Logger.error("Error processing the form %s "format formWithErrors.errors)
Redirect(routes.Application.index)
},
data => {
val sessionProduct = getSessionUnProcessedProduct(data.sessionId, data.documentName)
if(sessionProduct != null){
Logger.info("Found")
sessionProduct.updateProcessing(data.emailAddress, data.sessionId)
Redirect(routes.Application.index)
}
else
Logger.info("Nothing found")
Redirect(routes.Application.checkoutFree(data.sessionId))
}
)
}
}
is skipped entirely. There are no errors in the IDE(IDEA) console and the breakpoint at the entry of the method is not reached so none of the log messages are seen.
EDIT :
The relevant route in the routes file - POST /processFreeDoc controllers.Application.processFreeDoc