akka/akka-http

Custom headers should be parsed to the custom types instead of `RawHeader`

Open

#1.196 aberto em 13 de jun. de 2017

Ver no GitHub
 (1 comment) (4 reactions) (0 assignees)Scala (598 forks)batch import
1 - triagedhelp wantedt:coret:model

Métricas do repositório

Stars
 (1.311 stars)
Métricas de merge de PR
 (Mesclagem média 1h 17m) (1 fundiu PR em 30d)

Description

Akka HTTP should allow registering custom headers so that a custom header can be rendered to the custom type instead of RawHeader. Custom methods, mediatypes and status codes are allowed to be passed to ParserSettings.

It should be possible to read a custom header by request.header[ApiTokenHeader]. Please see below a reproducer.

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.headers.{ModeledCustomHeader, ModeledCustomHeaderCompanion, `User-Agent`}
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Flow
import akka.testkit.TestKit
import org.scalatest.{AsyncFlatSpecLike, Matchers}

import scala.concurrent.duration._
import scala.util.Try

class CustomHeaderSpec extends TestKit(ActorSystem("CustomHeaderSpec")) with AsyncFlatSpecLike with Matchers {

  implicit val ac = ActorMaterializer()

  val flow = Flow[HttpRequest].map { request =>
    val userAgent = request.header[`User-Agent`].map(_.value).getOrElse("N/A")
    val apiKey = request.header[ApiTokenHeader].map(_.value).getOrElse("N/A")
    val apiKeyAsRaw =
      request.headers
        .collectFirst { case h if h.lowercaseName == ApiTokenHeader.lowercaseName => h.value }
        .getOrElse("N/A")

    HttpResponse(entity = s"User-Agent: $userAgent apiKey: $apiKey apiKeyAsRaw: $apiKeyAsRaw")
  }

  val binding = Http().bindAndHandle(flow, "localhost", port = 0)

  it should "parse to custom header object" in {

    binding flatMap { b =>
      Http()
        .singleRequest(HttpRequest(uri = s"http://localhost:${b.localAddress.getPort}/")
          .withHeaders(`User-Agent`("abc"), ApiTokenHeader("myToken")))
    } flatMap { response =>
      response.entity.toStrict(30 seconds)
    } flatMap { e =>
      e.data.utf8String shouldBe "User-Agent: abc apiKey: myToken apiKeyAsRaw: myToken"
    }
  }
}

final class ApiTokenHeader(token: String) extends ModeledCustomHeader[ApiTokenHeader] {
  override def renderInRequests = true
  override def renderInResponses = false
  override val companion = ApiTokenHeader
  override def value: String = token
}
object ApiTokenHeader extends ModeledCustomHeaderCompanion[ApiTokenHeader] {
  override val name = "apiKey"
  override def parse(value: String) = Try(new ApiTokenHeader(value))
}

Guia do colaborador