2

我正在使用 Scala 和 Akka 为电信公司构建应用程序,并且需要使用UCIP 协议与 Account Information 和 Refill 服务器进行通信。

UCIP 是一个简单的协议,建立在 XMLRPC 之上;我遇到的唯一问题是它要求客户端以User-Agent特定格式设置标头User-Agent: <client name>/<protocol version>/<client version>,这将喷雾解析为无效。

我尝试创建一个自定义User-Agent标头,继承自,spray.http.HttpHeader但它仍然不起作用。这是我到目前为止所得到的:

import akka.actor.ActorSystem
import akka.event.{Logging, LoggingAdapter}
import spray.client.pipelining._
import spray.http._
import spray.httpx._

case class `User-Agent`(value: String) extends HttpHeader {
    def lowercaseName: String = "user-agent"
    def name: String = "User-Agent"
    def render[R <: Rendering](r: R): r.type = r ~~ s"User-Agent: $value"
}

class UcipClient(val url: String, val protocol: String, username: String, password: String) (implicit system: ActorSystem) {

    val log = Logging.getLogger(system, this)
    val logRequest: HttpRequest => HttpRequest = { r => log.debug(r.toString); r }
    val logResponse: HttpResponse => HttpResponse = { r => log.debug(r.toString); r }

    val pipeline = (
        addHeader(`User-Agent`("USSD-UCIP/%s/1.0".format(protocol)))
        ~> addCredentials(BasicHttpCredentials(username, password))
        ~> logRequest
        ~> sendReceive
        ~> logResponse
    )

    def send(req: UcipRequest) = pipeline(Post(url, req.getRequest))
}

我的请求不断返回“抱歉,发生错误:403,无效的协议版本未定义”,但是,当我使用curl.

我错过了什么,这甚至可以通过喷雾客户端实现吗?我花了相当多的时间检查互联网(这使我转向了自定义标头路由),但仍然没有弄清楚这一点......非常感谢任何帮助:-)

4

1 回答 1

2

事实证明,我离答案不远了。在检查通过网络发送的标头时,我注意到它User-Agent被设置了两次:一次是我的代码,一次是 Spray(因为它认为我的标头无效)。

将 设置spray.can.client.user-agent-header为空字符串会""删除第二个标头,并且请求成功。这是自定义标头的最终版本:

import spray.http._

object CustomHttpHeaders {
    case class `User-Agent`(val value: String) extends HttpHeader with Product with Serializable {
        def lowercaseName: String = "user-agent"
        def name: String = "User-Agent"
        def render[R <: Rendering](r: R): r.type = r ~~ s"User-Agent: $value"
    }
}

以及最终的 UCIP 客户端:

import akka.actor.ActorRefFactory
import com.typesafe.config.Config
import scala.concurrent.ExecutionContext.Implicits.global
import scala.xml.NodeSeq
import spray.client.pipelining._
import spray.http._
import spray.httpx._

class UcipFault(val code: Int, msg: String) extends RuntimeException(s"$code: $msg")

class AirException(val code: Int) extends RuntimeException(s"$code")

class UcipClient(config: Config, val url: String)(implicit context: ActorRefFactory) {
    import CustomHttpHeaders._

    val throwOnFailure: NodeSeq => NodeSeq = {
        case f if (f \\ "fault").size != 0 =>
            val faultData = (f \\ "fault" \\ "member" \ "value")
            throw new UcipFault((faultData \\ "i4").text.toInt,
                                (faultData \\ "string").text)
        case el =>
            val responseCode = ((el \\ "member")
                .filter { n => (n \\ "name").text == "responseCode" }
                .map { n => (n \\ "i4").text.toInt }).head
            if (responseCode == 0) el else throw new AirException(responseCode)
    }

    val pipeline = (
        addHeader(`User-Agent`("USSD-UCIP/%s/1.0".format(config.getString("ucip.server-protocol"))))
        ~> addCredentials(BasicHttpCredentials(config.getString("ucip.server-username"), config.getString("ucip.server-password")))
        ~> sendReceive
        ~> unmarshal[NodeSeq]
        ~> throwOnFailure
    )

    def send(req: UcipRequest) = pipeline(Post(url, req.getRequest))
}
于 2015-09-09T13:45:21.333 回答