[英]Obtaining the client IP in Akka-http
我正在尝试编写一个 Akka HTTP 微服务(akka 版本 2.4.11,Scala 版本 2.11.8,在撰写本文时都是最新版本),它知道客户端服务的 IP(即远程地址),但我无法得到这个去工作。
我可以创建并运行一个显示“你好!”的服务。 使用这样的路线:
val routeHello: Route = path("SayHello") {
get {
entity(as[String]) {
body => complete {
HttpResponse(entity = HttpEntity("Hello!"))
}
}
}
}
我已经构建了一个与上面类似的路由,它被扩展以便知道客户端的 IP 地址。
我注意到我需要编辑 application.conf 文件并设置“remote-address-header = on”以启用添加包含客户端(远程)IP 地址的Remote-Address
标头。 如果需要,我已经这样做了。
这是路线:
val routeHelloIp: Route = path("SayHelloIp") {
get {
// extractClientIp appears to be working as a filter
// instead of an extractor - why?
extractClientIp {
clientIp => {
entity(as[String]) {
body => complete {
HttpResponse(entity = HttpEntity("Hello!"))
}
}
}
}
}
}
但是,当我运行这条路线时,我收到一条消息“找不到请求的资源。”。
看起来我在上面的示例中弄错了 Akka-http DSL 语法糖。 如果你能让我走上正确的道路,我将不胜感激!
编辑:
为了响应 Ramon 的有用回答,我尝试了以下程序。 不幸的是它没有编译,我看不到我需要做什么才能让它编译。
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.Http.IncomingConnection
import java.net.InetSocketAddress
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Sink
import akka.http.scaladsl.server.Directives._
import java.net.InetSocketAddress
object TestHttp {
def main(args: Array[String]) {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
// allow connections from any IP
val interface = "0.0.0.0"
//from the question
def createRoute(address: InetSocketAddress) = path("SayHelloIp") {
get {
extractRequestEntity { entity =>
entity(as[String]) { body =>
complete(entity = s"Hello ${address.getAddress().getHostAddress()}")
}
}
}
}
Http().bind(interface).runWith(Sink foreach { conn =>
val address = conn.remoteAddress
conn.handleWithAsyncHandler(createRoute(address))
})
}
}
我有以下 build.sbt 以确保使用最新版本的 Scala 和 akka-http:
import sbt.Keys._
name := "Find my IP"
version := "1.0"
scalaVersion := "2.11.8"
resolvers ++= Seq(
"Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
)
libraryDependencies ++= {
Seq(
"com.typesafe.akka" %% "akka-actor" % "2.4.11",
"com.typesafe.akka" %% "akka-stream" % "2.4.11",
"com.typesafe.akka" %% "akka-http-experimental" % "2.4.11",
"com.typesafe.akka" %% "akka-http-core" % "2.4.11"
)
}
我收到以下编译时错误:
[error] /Users/tilopa/temp/akka-test/src/main/scala/Test.scala:24: akka.http.scaladsl.model.RequestEntity does not take parameters
[error] entity(as[String]) { body =>
[error] ^
[error] /Users/tilopa/temp/akka-test/src/main/scala/Test.scala:25: reassignment to val
[error] complete(entity = s"Hello ${address.getAddress().getHostAddress()}")
[error] ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
使用 extractClientIp
extractClientIp
对您不起作用,因为发件人没有指定所需的标头字段之一。 从文档中:
提供 X-Forwarded-For、Remote-Address 或 X-Real-IP 标头的值作为 RemoteAddress 的实例。
您只需在发件人中打开正确的设置:
如果相应的设置 akka.http.server.remote-address-header 设置为 on,akka-http 服务器引擎会自动将 Remote-Address 标头添加到每个请求。 默认情况下,它设置为关闭。
通用解决方案
如果您希望它适用于任何 HttpRequest,而不仅仅是具有正确标头设置的那些,那么您必须在HttpExt
上使用bind
方法而不是bindAndHandle
:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.Http.IncomingConnection
import java.net.InetSocketAddress
implicit val actorSystem : ActorSystem = ???
implicit val actorMat = ActorMaterializer()
//alow connections from any IP
val interface = "0.0.0.0"
//from the question
def createRoute(address : InetSocketAddress) = path("SayHelloIp") {
get {
extractRequestEntity { entity =>
entity(as[String]) { body =>
complete(entity = s"Hello ${address.getAddress().getHostAddress()}")
}
}
}
}
Http().bind(interface).runWith(Sink foreach { conn =>
val address = conn.remoteAddress
conn.handleWithAsyncHandler(createRoute(address))
})
编辑
正如评论中所指出的:因为 akka 10.0.13
使用conn.handleWith
。
HttpExt.bind()
已被贬值。 这是connectionSource()
的解决方法:
val https: HttpsConnectionContext = /*...*/
Http(actorSystem).
newServerAt(interface,httpsPort).
enableHttps(https).
connectionSource().
to(Sink.foreach { connection =>
println("New Connection from remote address :: ${connection.remoteAddress}")
// handle connection here
}).run().onComplete {
case Success(binding)=>
binding.addToCoordinatedShutdown(hardTerminationDeadline = 10.seconds)
log.info("{} HTTPS Server running {}",errorContext,binding)
case Failure(ex)=>
log.error("{} HTTPS Server failed {}",errorContext,ex.toString)
sys.exit(10)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.