A human-readable scala http client API compatible with:
Add a dependency in your build.sbt:
libraryDependencies += "fr.hmil" %%% "roshttp" % "1.0.1"
The following is a simplified usage guide. You may find useful information in the API doc too.
import fr.hmil.roshttp.HttpRequest
import scala.concurrent.ExecutionContext.Implicits.global
/* ... */
// Runs consistently on the jvm, in node.js and in the browser!
val request = HttpRequest("https://schema.org/WebPage")
request.send().map(response => println(response.body))
When you send()
a request, you get a Future[HttpResponse]
which resolves to
an HttpResponse if everything went fine or fails with an HttpException if a
network error occurred or if a statusCode > 400 was received.
When applicable, the response body of a failed request can be read:
HttpRequest("http://hmil.github.io/foobar")
.send()
.onFailure {
case e:HttpResponseError =>
s"Got a status: ${e.response.statusCode}" ==> "Got a status: 404"
}
Every aspect of a request can be customized using .withXXX
methods. These are
meant to be chained, they do not modify the original request.
The URI can be built using .withProtocol
, .withHost
, .withPort
,
.withPath
, and .withQuery...
. The latter is a bit more complex and
is detailed below.
HttpRequest()
.withProtocol("HTTP")
.withHost("localhost")
.withPort(3000)
.withPath("/weather")
.withQueryParameter("city", "London")
.send() // GET http://localhost:3000/weather?city=London
The whole querystring can be set to a custom value like this:
// Sets the query string such that the target url ends in "?hello%20world"
request.withQueryString("hello world")
.withQueryString(string)
urlencodes string and replaces the whole query string
with the result.
To bypass encoding, use .withQueryStringRaw(rawString)
.
Most of the time, the query string is used to pass key/value pairs in the
application/x-www-form-urlencoded
format.
HttpRequest
offers an API to add, update and delete keys in the query string.
request
.withQueryParameter("foo", "bar")
.withQueryArrayParameter("table", "a", "b", "c")
.withQueryObjectParameter("map",
"d" -> "dval",
"e" -> "e value"
)
.withQueryParameters(
"license" -> "MIT",
"copy" -> "© 2016"
)
/* Query is now:
foo=bar&table=a&table=b&table=c&map[d]=dval&map[e]=e%20value&license=MIT©=%C2%A9%202016
*/
Set individual headers using .withHeader
request.withHeader("Accept", "text/html")
Or multiple headers at once using .withHeaders
request.withHeaders(
"Accept" -> "text/html",
"Cookie" -> "sessionid=f00ba242cafe"
)
A map of response headers is available on the [[HttpResponse]] object:
request.send().map({res =>
println(res.headers("Set-Cookie"))
})
An HTTP request can send data wrapped in an implementation of BodyPart
. The most common
formats are already provided but you can create your own as well.
A set of implicit conversions is provided in body.Implicits
for convenience.
You can post
or put
some data with your favorite encoding.
import fr.hmil.roshttp.body.Implicits._
val data = URLEncodedBody(
"answer" -> "42",
"platform" -> "jvm"
)
request.post(data)
// or
request.put(data)
Create JSON requests easily using implicit conversions.
import fr.hmil.roshttp.body.Implicits._
val data = JSONObject(
"answer" -> 42,
"platform" -> "node"
)
request.post(data)
To send file data you must turn a file into a ByteBuffer and then send it in a StreamBody. For instance, on the jvm you could do:
import fr.hmil.roshttp.body.Implicits._
val bytes = Source.fromFile("icon.png")(scala.io.Codec.ISO8859).map(_.toByte).toArray
request.post(ByteBuffer.wrap(bytes))
Note that the codec argument is important to read the file as-is and avoid side-effects due to character interpretation.
Use the MultiPartBody
to compose request bodies arbitrarily. It allows for instance
to send binary data with some textual data.
The following example illustrates how you could send a form to update a user profile made of a variety of data types.
import fr.hmil.roshttp.body.Implicits._
request.post(MultiPartBody(
// The name part is sent as plain text
"name" -> PlainTextBody("John"),
// The skills part is a complex nested structure sent as JSON
"skills" -> JSONObject(
"programming" -> JSONObject(
"C" -> 3,
"PHP" -> 1,
"Scala" -> 5
),
"design" -> 2
),
// The picture is sent using a StreamBody, assuming image_bytes is a ByteBuffer containing the image
"picture" -> StreamBody(image_bytes, "image/jpeg")
))
// Set the request method to GET, POST, PUT, etc...
request.withMethod(Method.PUT).send()
Watch the issues for upcoming features. Feedback is very welcome so feel free to file an issue if you see something that is missing.
- Some headers cannot be set in the browser (list).
- There is no way to avoid redirects in the browser. This is a W3C spec.
- Chrome does not allow userspace handling of a 407 status code. It is treated like a network error. See chromium issue.
- The
TRACE
HTTP method does not work in browsers andPATCH
does not work in the JVM.
Please read the contributing guide.
v1.0.1
- Fix NPE when reading empty error response
v1.0.0
- Using semantic versioning from now on
- Renamed RösHTTP
- Add .withBody()
v0.3.0
- Remove general purpose StringBody
- Add missing patch method
- Make Method constructor public
- Disambiguate
withQueryArrayParameter
andwithQueryObjectParameter
- Remove map parameters from
.withQueryParameter(s)
and.withHeaders
v0.2.0
- Support request body with
post()
,put()
andoptions()
- Add
withHttpMethod()
- Support HTTPS
v0.1.0
- First release
MIT