Stratos K
Stratos K

Reputation: 462

Scala and Akka HTTP: Processing form-data requests

Suppose we have the following request:

curl --location --request POST 'localhost:8080/api' \
--header 'Content-Type: multipart/form-data' \
--form 'field1=value1' \
--form 'field2=value2'

The request handler below gets the whole entity, but I am struggling to see how I could instead get value1 and value2.

val requestHandler: Flow[HttpRequest, HttpResponse, _] = Flow[HttpRequest].mapAsync(1) {
  case HttpRequest(HttpMethods.POST, Uri.Path("/api"), _, entity, _) =>
    val entityTextFuture: Future[String] = entity.toStrict(3 seconds).map(_.data.utf8String)
      entityTextFuture.flatMap { text =>
        Future(HttpResponse(
          StatusCodes.OK,
          entity = text
        ))
      }
}

Important: I have to use the Akka HTTP low-level server API, so I cannot use routes.

Many thanks for your time and help in advance!

Upvotes: 1

Views: 431

Answers (1)

Alec
Alec

Reputation: 32339

If all you want are the string values in the form data, you just need to unmarshal to StrictForm, and then unmarshal each of the field values as strings.

Here's a proof of concept Ammonite script that responds to your curl request with value1 & value2:

import $ivy.`com.typesafe.akka::akka-actor:2.6.3`
import $ivy.`com.typesafe.akka::akka-stream:2.6.3`
import $ivy.`com.typesafe.akka::akka-http:10.1.11`

import scala.concurrent.Future

import akka.actor.ActorSystem
import akka.stream.scaladsl.Flow
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.http.scaladsl.common.StrictForm

implicit val system = ActorSystem()
implicit val ec = system.dispatcher

val requestHandler: Flow[HttpRequest, HttpResponse, _] = Flow[HttpRequest].mapAsync(1) {
  case HttpRequest(HttpMethods.POST, Uri.Path("/api"), _, entity, _) =>
    for {
      strictForm <- Unmarshal(entity).to[StrictForm]
      fieldsSeq <- Future.traverse(strictForm.fields) {
        case (n, v) => Unmarshal(v).to[String].map(n -> _)
      }
      fields = fieldsSeq.toMap
      response = fields("field1") + " & " + fields("field2")
    } yield HttpResponse(StatusCodes.OK, entity = response)
}

Http().bindAndHandle(requestHandler, "localhost", 8080)

Upvotes: 1

Related Questions