使用Akka HTTP(以前称为Spray)的GET请求的查询参数

19

Akka HTTP(以前称为Spray)的一个功能是能够自动将数据从JSON转换为案例类等,也可以自动将数据转换回JSON。我已经成功地使用这个功能。

目前,我正在尝试创建一个HTTP客户端,执行带有查询参数的GET请求。代码目前看起来像这样:

val httpResponse: Future[HttpResponse] =
  Http().singleRequest(HttpRequest(
    uri = s"""http://${config.getString("http.serverHost")}:${config.getInt("http.port")}/""" +
          s"query?seq=${seq}" +
          s"&max-mismatches=${maxMismatches}" +
          s"&pam-policy=${pamPolicy}"))

嗯,这并不太好看。如果我只需要传入一个包含查询参数的case class,并且让Akka HTTP自动地生成查询参数,就像它对json做的那样,那将是很好的。 (另外,Akka HTTP的服务器端有一种相当优雅的方法来解析GET查询参数,因此人们可能认为它也应该有一种相当优雅的方法来生成它们。)

我想做类似以下的事情:

val httpResponse: Future[HttpResponse] =
  Http().singleRequest(HttpRequest(
    uri = s"""http://${config.getString("http.serverHost")}:${config.getInt("http.port")}/query""",
    entity = QueryParams(seq = seq, maxMismatches = maxMismatches, pamPolicy = pamPolicy)))

上面的方法实际上并不起作用。

使用Akka HTTP是否可以以某种方式实现我想要的功能?还是我只需要按照老式的方式做事情?也就是说,明确生成查询参数,就像我在上面的第一个代码块中所做的那样。

(我知道如果我将其从GET更改为POST,我可能可以使它更像我想要的工作方式,因为然后我可以让POST请求的内容自动转换为JSON格式的案例类,但我这里并不真的希望这样做。)

2个回答

32

您可以利用Uri类来实现您想要的功能。它提供了多种使用withQuery方法将一组参数添加到查询字符串中的方式。例如,您可以执行以下操作:

val params = Map("foo" -> "bar", "hello" -> "world")
HttpRequest(Uri(hostAndPath).withQuery(params))

或者

HttpRequest(Uri(hostAndPath).withQuery(("foo" -> "bar"), ("hello" -> "world")))

3
使用这种方法的好处是,使用该方法将自动完成转义。 - jrudolph
21
在Akka-http 2.4.8中,这似乎需要用Query包装:.withQuery(Query(params))) - akauppi
1
这应该在文档中。 - coolboyjules
@coolboyjules 没错。这个答案很好,但可惜的是人们必须在互联网上四处搜索才能找到这里。这应该放在文档中。我将把它加入我的待办事项列表中——因为文档的代码是开源的。 - Niks
现在它已成为文档的一部分了 :) https://doc.akka.io/docs/akka-http/10.2/client-side/request-and-response.html 该文档链接到此处的具体示例 https://doc.akka.io/docs/akka-http/10.2/common/uri-model.html#query-string-in-uri - Niks

-1

显然,这可以通过扩展Akka HTTP的能力来完成,但对于您所需的(只是更整洁地构建查询字符串),您可以使用一些Scala乐趣来实现:

type QueryParams = Map[String, String]

object QueryParams {

  def apply(tuples: (String, String)*): QueryParams = Map(tuples:_*)
}

implicit class QueryParamExtensions(q: QueryParams) {

  def toQueryString = "?"+q.map{
    case (key,value) => s"$key=$value" //Need to do URL escaping here?
  }.mkString("&")
}

implicit class StringQueryExtensions(url: String) {
  def withParams(q: QueryParams) =
    url + q.toQueryString
}

val params = QueryParams(
  "abc" -> "def",
  "xyz" -> "qrs"
)

params.toQueryString // gives ?abc=def&xyz=qrs

"http://www.google.com".withParams(params) // gives http://www.google.com?abc=def&xyz=qrs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接