18

In Scala how do I build up a URL with query string parameters programmatically?

Also how can I parse a String containing a URL with query string parameters into a structure that allows me to edit the query string parameters programmatically?

theon
  • 14,170
  • 5
  • 51
  • 74

7 Answers7

15

Spray has a very efficient URI parser. Usage is like so:

import spray.http.Uri
val uri = Uri("http://example.com/test?param=param")

You can set the query params like so:

uri withQuery ("param2" -> "2", "param3" -> 3")
theon
  • 14,170
  • 5
  • 51
  • 74
  • Is there a way to use this outside of spray? Looking at the source seems like it's pretty coupled into other parts of spray. – Falmarri Dec 11 '13 at 19:12
  • You will only need the [`spray-http`](http://spray.io/documentation/1.2.0/spray-http/) module which is pretty small. It only contains the model for HTTP requests and responses. It doesn't contain any server or client code. – theon Dec 12 '13 at 18:04
  • @theon how do we use this Spray Routing? Here is my code val request: HttpRequest = Get(api) val pipeline: Future[SendReceive] = { for { Http.HostConnectorInfo(connector, _) <- IO(Http) ? Http.HostConnectorSetup(AppnexusSegmentService.AppnexusBaseUrl) } yield sendReceive(connector) } Await.result(pipeline.flatMap { client => client.apply(request)}, 15.seconds) – Anand Nov 10 '14 at 06:28
  • 3
    spray-http depends on Akka :-( ... Pulling this sort of dependency just for the sake of parsing a URI looks like overengineering. – Richard Gomes Feb 27 '15 at 03:12
  • Good point @RichardGomes. I was surprised considering `spray-http` is only meant to be the model of HTTP data structures. Why does it [need Akka](http://spray.io/documentation/1.2.3/spray-http)? – theon Jun 12 '15 at 11:56
  • Since it is depended scope, you might be able omit it and have the Uri parsing code might still work. Still odd though. – theon Jun 12 '15 at 11:57
13

The following library can help you parse and build URLs with query string parameters (Disclaimer: This is my own library): https://github.com/lemonlabsuk/scala-uri

It provides a DSL for building URLs with query strings:

val uri = "http://example.com" ? ("one" -> 1) & ("two" -> 2)

You can parse a uri and get the parameters into a Map[String,List[String]] like so:

val uri = parseUri("http://example.com?one=1&two=2").query.params
theon
  • 14,170
  • 5
  • 51
  • 74
4

Theon's library looks pretty nice. But if you just want a quickie encode method, I have this one. It deals with optional parameters, and also will recognize JsValues from spray-json and compact print them before encoding. (Those happen to be the two things I have to worry about, but you could easily extend the match block for other cases you want to give special handling to)

import java.net.URLEncoder
def buildEncodedQueryString(params: Map[String, Any]): String = {
  val encoded = for {
    (name, value) <- params if value != None
    encodedValue = value match {
      case Some(x:JsValue) => URLEncoder.encode(x.compactPrint, "UTF8")
      case x:JsValue       => URLEncoder.encode(x.compactPrint, "UTF8")
      case Some(x)         => URLEncoder.encode(x.toString, "UTF8")
      case x               => URLEncoder.encode(x.toString, "UTF8")
    }
  } yield name + "=" + encodedValue

  encoded.mkString("?", "&", "")
}
ryryguy
  • 610
  • 4
  • 15
2

sttp provides a great URI interpolator for this.

See here the Documentation

Here its example:

import sttp.client._
import sttp.model._

val user = "Mary Smith"
val filter = "programming languages"

val endpoint: Uri = uri"http://example.com/$user/skills?filter=$filter"

assert(endpoint.toString ==
  "http://example.com/Mary%20Smith/skills?filter=programming+languages")

As you can see it automatically encodes the needed parts.

pme
  • 14,156
  • 3
  • 52
  • 95
0

Dispatch hasn't been mentioned yet.

https://dispatchhttp.org/Defining+requests.html

val myRequest = host("somehost.com") / "some" / "path" <<? Map("id" -> "12345")
theon
  • 14,170
  • 5
  • 51
  • 74
Ashalynd
  • 12,363
  • 2
  • 34
  • 37
0

also useful: https://github.com/mobiworx/urlifier

val url = (http || "some-domain".de) ? german & version(1) & foobar
url.toString
tfh
  • 620
  • 1
  • 4
  • 14
0

Try KFoundation's URL class. It is both a builder and a parser.

E.g.

val url1 = URL("http://exampel.net/path")
val url2 = url1/"subpath"        // -> http://exampel.net/path/subpath
val url3 = url2?("key"->"value") // -> http://exampel.net/path/subpath?key=value

API Doc: https://mscp.co/resouces/apidoc/kfoundation/scala/0.3/net/kfoundation/scala/io/URL.html Dependency: https://search.maven.org/artifact/net.kfoundation/kfoundation-scala_2.13/0.3.1/jar

Captain K
  • 27
  • 3