【问题标题】:Obtaining the client IP in Akka-http在 Akka-http 中获取客户端 IP
【发布时间】:2016-10-19 13:05:28
【问题描述】:

我正在尝试编写一个 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

【问题讨论】:

    标签: scala dsl akka-http


    【解决方案1】:

    使用 extractClientIp

    extractClientIp 不适合您,因为发件人未指定必需的标头字段之一。来自documentation

    提供 X-Forwarded-For、Remote-Address 或 X-Real-IP 的值 标头作为 RemoteAddress 的实例。

    您只需在发件人中打开正确的设置:

    akka-http 服务器引擎将 Remote-Address 标头添加到每个 如果相应的设置自动请求 akka.http.server.remote-address-header 设置为 on。默认情况下是 设置为关闭。

    通用解决方案

    如果您希望它适用于任何 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))
    })
    

    编辑

    如 cmets 中所述:因为 akka 10.0.13 使用 conn.handleWith

    【讨论】:

    • 嗨拉蒙,谢谢你的回答!不幸的是,我仍然无法让它发挥作用。我创建了以下程序来测试您的答案:
    • 抱歉 - 我无法将我所有的文字都作为对您答案的回复,所以我将其与原始问题一起发布!
    • 我仍然无法编译您的示例。我收到编译消息:
    • 错误:(27,9) not found: value extractEntity
    • @ClickUpvote 无需道歉。据我所知extractClientIP 解决方案和bind 解决方案仍然符合最新的 API...
    猜你喜欢
    • 1970-01-01
    • 2015-03-01
    • 2019-04-11
    • 2016-10-07
    • 2015-06-06
    • 2020-11-10
    • 2020-12-08
    • 1970-01-01
    • 2017-08-11
    相关资源
    最近更新 更多