akka/akka-http
在 GitHub 查看Custom headers should be parsed to the custom types instead of `RawHeader`
Open
#1,196 创建于 2017年6月13日
1 - triagedhelp wantedt:coret:model
描述
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))
}