aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authoradamw <adam@warski.org>2017-10-18 14:20:26 +0200
committeradamw <adam@warski.org>2017-10-18 14:20:26 +0200
commit21c4700bbe8cf37d7b9feacc5afdf64357604d8f (patch)
tree50cd74646955bae495296b1d9a212ac412a75b7c /docs
parent6e109a964383bfe5e2be04f65fa7cc1356a97cbe (diff)
downloadsttp-21c4700bbe8cf37d7b9feacc5afdf64357604d8f.tar.gz
sttp-21c4700bbe8cf37d7b9feacc5afdf64357604d8f.tar.bz2
sttp-21c4700bbe8cf37d7b9feacc5afdf64357604d8f.zip
More docs
Diffstat (limited to 'docs')
-rw-r--r--docs/backends/akkahttp.rst4
-rw-r--r--docs/backends/asynchttpclient.rst12
-rw-r--r--docs/backends/custom.rst63
-rw-r--r--docs/backends/httpurlconnection.rst4
-rw-r--r--docs/backends/okhttp.rst4
-rw-r--r--docs/backends/summary.rst4
-rw-r--r--docs/backends/tagging.rst9
-rw-r--r--docs/backends/testing.rst24
-rw-r--r--docs/conf/timeouts.rst4
-rw-r--r--docs/index.rst1
-rw-r--r--docs/json.rst12
-rw-r--r--docs/quickstart.rst8
-rw-r--r--docs/requests/authentication.rst2
-rw-r--r--docs/requests/basics.rst16
-rw-r--r--docs/requests/body.rst26
-rw-r--r--docs/requests/headers.rst2
-rw-r--r--docs/requests/multipart.rst14
-rw-r--r--docs/requests/streaming.rst8
-rw-r--r--docs/requests/type.rst19
-rw-r--r--docs/requests/uri.rst9
-rw-r--r--docs/responses/basics.rst8
-rw-r--r--docs/responses/body.rst12
22 files changed, 169 insertions, 96 deletions
diff --git a/docs/backends/akkahttp.rst b/docs/backends/akkahttp.rst
index 53ebfda..0cda636 100644
--- a/docs/backends/akkahttp.rst
+++ b/docs/backends/akkahttp.rst
@@ -1,7 +1,7 @@
.. _akkahttp:
-``AkkaHttpBackend``
-===================
+akka-http backend
+=================
To use, add the following dependency to your project::
diff --git a/docs/backends/asynchttpclient.rst b/docs/backends/asynchttpclient.rst
index 8408175..5afab37 100644
--- a/docs/backends/asynchttpclient.rst
+++ b/docs/backends/asynchttpclient.rst
@@ -1,5 +1,5 @@
-``AsyncHttpClientBackend``
-==========================
+async-http-client backend
+=========================
To use, add the following dependency to your project::
@@ -44,10 +44,7 @@ Next you'll need to add an implicit value::
Streaming using Monix
---------------------
-The Monix backend supports streaming (as both Monix and Async Http Client
-support reactive streams ``Publisher``s out of the box). The type of
-supported streams in this case is ``Observable[ByteBuffer]``. That is, you can
-set such an observable as a request body::
+The Monix backend supports streaming (as both Monix and Async Http Client support reactive streams ``Publisher`` s out of the box). The type of supported streams in this case is ``Observable[ByteBuffer]``. That is, you can set such an observable as a request body::
import com.softwaremill.sttp._
@@ -77,6 +74,5 @@ And receive responses as an observable stream::
.response(asStream[Observable[ByteBuffer]])
.send()
-It's also possible to use `fs2 <https://github.com/functional-streams-for-scala/fs2>`_
-streams for sending request & receiving responses.
+It's also possible to use `fs2 <https://github.com/functional-streams-for-scala/fs2>`_ streams for sending request & receiving responses.
diff --git a/docs/backends/custom.rst b/docs/backends/custom.rst
index 4dd5208..0820a26 100644
--- a/docs/backends/custom.rst
+++ b/docs/backends/custom.rst
@@ -3,7 +3,7 @@
Custom backends, logging, metrics
=================================
-It is also entirely possible to write custom own backend (if doing so, please consider contributing!) or wrapping an existing one. One can even write completely generic wrappers for any delegate backend, as each backend comes equipped with a monad for the response type. This brings the possibility to ``map`` and ``flatMap`` over responses.
+It is also entirely possible to write custom backends (if doing so, please consider contributing!) or wrap an existing one. One can even write completely generic wrappers for any delegate backend, as each backend comes equipped with a monad for the response type. This brings the possibility to ``map`` and ``flatMap`` over responses.
Possible use-cases for wrapper-backend include:
@@ -11,7 +11,64 @@ Possible use-cases for wrapper-backend include:
* capturing metrics
* request signing (transforming the request before sending it to the delegate)
-To pass some context to wrapper-backends, requests can be *tagged*. Each ``RequestT`` instance contains a ``tags: Map[String, Any]`` field. This is unused by http, but can be used e.g. to pass a metric name or logging context.
+Request tagging
+---------------
-Example backend logging
+Each request contains a ``tags: Map[String, Any]`` map. This map can be used to tag the request with any backend-specific information, and isn't used in any way by sttp itself.
+Tags can be added to a request using the ``def tag(k: String, v: Any)`` method, and read using the ``def tag(k: String): Option[Any]`` method.
+
+Backends, or backend wrappers can use tags e.g. for logging, passing a metric name, using different connection pools, or even different delegate backends.
+
+Example backend wrapper
+-----------------------
+
+Below is an example on how to implement a backend wrapper, which sends metrics for completed requests and wraps any ``Future``-based backend::
+
+ // the metrics infrastructure
+ trait MetricsServer {
+ def reportDuration(name: String, duration: Long): Unit
+ }
+
+ class CloudMetricsServer extends MetricsServer {
+ override def reportDuration(name: String, duration: Long): Unit = ???
+ }
+
+ // the backend wrapper
+ class MetricWrapper[S](delegate: SttpBackend[Future, S],
+ metrics: MetricsServer)
+ extends SttpBackend[Future, S] {
+
+ override def send[T](request: Request[T, S]): Future[Response[T]] = {
+ val start = System.currentTimeMillis()
+
+ def report(metricSuffix: String): Unit = {
+ val metricPrefix = request.tag("metric").getOrElse("?")
+ val end = System.currentTimeMillis()
+ metrics.reportDuration(metricPrefix + "-" + metricSuffix, end - start)
+ }
+
+ delegate.send(request).andThen {
+ case Success(response) if response.is200 => report("ok")
+ case Success(response) => report("notok")
+ case Failure(t) => report("exception")
+ }
+ }
+
+ override def close(): Unit = delegate.close()
+
+ override def responseMonad: MonadError[Future] = delegate.responseMonad
+ }
+
+ // example usage
+ implicit val backend = new MetricWrapper(
+ AkkaHttpBackend(),
+ new CloudMetricsServer()
+ )
+
+ sttp
+ .get(uri"http://company.com/api/service1")
+ .tag("metric", "service1")
+ .send()
+
+
diff --git a/docs/backends/httpurlconnection.rst b/docs/backends/httpurlconnection.rst
index fdea9b0..e0f6594 100644
--- a/docs/backends/httpurlconnection.rst
+++ b/docs/backends/httpurlconnection.rst
@@ -1,5 +1,5 @@
-``HttpURLConnectionBackend``
-============================
+HttpURLConnection backend
+=========================
The default **synchronous** backend. Sending a request returns a response wrapped in the identity type constructor, which is equivalent to no wrapper at all.
diff --git a/docs/backends/okhttp.rst b/docs/backends/okhttp.rst
index 52fbc49..04796f1 100644
--- a/docs/backends/okhttp.rst
+++ b/docs/backends/okhttp.rst
@@ -1,5 +1,5 @@
-``OkHttpClientBackend``
-=======================
+OkHttp backend
+==============
To use, add the following dependency to your project::
diff --git a/docs/backends/summary.rst b/docs/backends/summary.rst
index 36e50b0..d58bb54 100644
--- a/docs/backends/summary.rst
+++ b/docs/backends/summary.rst
@@ -5,7 +5,7 @@ Supported backends
sttp suports a number of synchornous and asynchronous backends. It's the backends that take care of managing connections, sending requests and receiving responses: sttp defines only the API to describe the requests to be send and handle the response data. It's the backends where all the heavy-lifting is done.
-Choosing the right backend depends on a number of factors: if you are using sttp to explore some data, or is it a production system; are you using a synchornous, blocking architecture or an asynchronous one; do you work mostly with Scala's ``Future``, or maybe you use some form of a ``Task`` abstraction; finally, if you want to stream requests/responses, or not.
+Choosing the right backend depends on a number of factors: if you are using sttp to explore some data, or is it a production system; are you using a synchronous, blocking architecture or an asynchronous one; do you work mostly with Scala's ``Future``, or maybe you use some form of a ``Task`` abstraction; finally, if you want to stream requests/responses, or not.
Each backend has two type parameters:
@@ -15,7 +15,7 @@ Each backend has two type parameters:
Below is a summary of all the backends. See the sections on individual backend implementations for more information.
================================ ============================ ================================================
-Class Result wrapper Supported stream type
+Class Response wrapper Supported stream type
================================ ============================ ================================================
``HttpURLConnectionBackend`` None (``Id``) n/a
``AkkaHttpBackend`` ``scala.concurrent.Future`` ``akka.stream.scaladsl.Source[ByteString, Any]``
diff --git a/docs/backends/tagging.rst b/docs/backends/tagging.rst
deleted file mode 100644
index 15773ff..0000000
--- a/docs/backends/tagging.rst
+++ /dev/null
@@ -1,9 +0,0 @@
-Request tagging
-===============
-
-Each request contains a ``tags: Map[String, Any]`` map. This map can be used to tag the request with any backend-specific information, and isn't used in any way by sttp itself.
-
-Tags can be added to a request using the ``def tag(k: String, v: Any)`` method, and read using the ``def tag(k: String): Option[Any]`` method.
-
-Backends, or :ref:`backend wrappers <custombackends>` can use tags e.g. for logging, passing a metric name, using different connection pools, or even different delegate backends.
-
diff --git a/docs/backends/testing.rst b/docs/backends/testing.rst
index 0ac21c2..c0cfc6f 100644
--- a/docs/backends/testing.rst
+++ b/docs/backends/testing.rst
@@ -1,13 +1,9 @@
Testing
=======
-If you need a stub backend for use in tests instead of a "real" backend (you
-probably don't want to make HTTP calls during unit tests), you can use the
-``SttpBackendStub`` class. It allows specifying how the backend should respond
-to requests matching given predicates.
+If you need a stub backend for use in tests instead of a "real" backend (you probably don't want to make HTTP calls during unit tests), you can use the ``SttpBackendStub`` class. It allows specifying how the backend should respond to requests matching given predicates.
-A backend stub can be created using an instance of a "real" backend, or by
-explicitly giving the response wrapper monad and supported streams type.
+A backend stub can be created using an instance of a "real" backend, or by explicitly giving the response wrapper monad and supported streams type.
For example::
@@ -23,18 +19,14 @@ For example::
val response2 = sttp.post(uri"http://example.org/d/e").send()
// response2.code will be 500
-However, this approach has one caveat: the responses are not type-safe. That
-is, the backend cannot match on or verify that the type included in the
-response matches the response type requested.
+However, this approach has one caveat: the responses are not type-safe. That is, the backend cannot match on or verify that the type included in the response matches the response type requested.
-It is also possible to create a stub backend which delegates calls to another
-(possibly "real") backend if none of the specified predicates match a request.
-This can be useful during development, to partially stub a yet incomplete
-API with which we integrate::
+It is also possible to create a stub backend which delegates calls to another (possibly "real") backend if none of the specified predicates match a request. This can be useful during development, to partially stub a yet incomplete API with which we integrate::
- implicit val testingBackend = SttpBackendStub.withFallback(HttpURLConnectionBackend())
- .whenRequestMatches(_.uri.path.startsWith(List("a")))
- .thenRespond("I'm a STUB!")
+ implicit val testingBackend =
+ SttpBackendStub.withFallback(HttpURLConnectionBackend())
+ .whenRequestMatches(_.uri.path.startsWith(List("a")))
+ .thenRespond("I'm a STUB!")
val response1 = sttp.get(uri"http://api.internal/a").send()
// response1.body will be Right("I'm a STUB")
diff --git a/docs/conf/timeouts.rst b/docs/conf/timeouts.rst
index 72b2f4a..4c046bc 100644
--- a/docs/conf/timeouts.rst
+++ b/docs/conf/timeouts.rst
@@ -1,7 +1,7 @@
Timeouts
========
-Sttp supports read and connection timeouts:
+sttp supports read and connection timeouts:
* Connection timeout - can be set globally (30 seconds by default)
* Read timeout - can be set per request (1 minute by default)
@@ -11,7 +11,7 @@ How to use::
import com.softwaremill.sttp._
import scala.concurrent.duration._
- // all backends provide a constructor that allows users to specify backend options
+ // all backends provide a constructor that allows to specify backend options
implicit val backend = HttpURLConnectionBackend(
options = SttpBackendOptions.connectionTimeout(1.minute))
diff --git a/docs/index.rst b/docs/index.rst
index d569a11..e030323 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -68,7 +68,6 @@ For more details, explore the subjects below!
backends/okhttp
backends/custom
backends/testing
- backends/tagging
.. toctree::
:maxdepth: 2
diff --git a/docs/json.rst b/docs/json.rst
index 086fa60..032c120 100644
--- a/docs/json.rst
+++ b/docs/json.rst
@@ -3,14 +3,16 @@
JSON
====
-JSON encoding of bodies and decoding of responses can be handled using
-`Circe <https://circe.github.io/circe/>`_ by the ``circe`` module. To use
-add the following dependency to your project::
+Adding support for JSON (or other format) bodies in requests/responses is a matter of providing a :ref:`body serializer <requestbody_custom>` and/or a :ref:`response body specification <responsebodyspec_custom>`. Both are quite straightforward to implement, so integrating with your favorite JSON library shouldn't be a problem. However, there are some integrations available out-of-the-box.
+
+Circe
+-----
+
+JSON encoding of bodies and decoding of responses can be handled using `Circe <https://circe.github.io/circe/>`_ by the ``circe`` module. To use add the following dependency to your project::
"com.softwaremill.sttp" %% "circe" % "0.0.20"
-This module adds a method to the request and a function that can be given to
-a request to decode the response to a specific object::
+This module adds a method to the request and a function that can be given to a request to decode the response to a specific object::
import com.softwaremill.sttp._
import com.softwaremill.sttp.circe._
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 4f04fdf..287feaa 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -3,7 +3,7 @@
Quickstart
==========
-The main sttp API comes in a single jar without transitive dependencies. This also includes a default, synchronous backend, which is based on java's ``HttpURLConnection``. For production usages, you'll often want to use an alternate backend (but what's important, is that the API remains the same!). See the section on :ref:`backends <backends_summary>` for additional instructions.
+The main sttp API comes in a single jar without transitive dependencies. This also includes a default, synchronous backend, which is based on Java's ``HttpURLConnection``. For production usages, you'll often want to use an alternate backend (but what's important is that the API remains the same!). See the section on :ref:`backends <backends_summary>` for additional instructions.
Using sbt
---------
@@ -27,11 +27,11 @@ If you are an `Ammonite <http://ammonite.io>`_ user, you can quickly start exper
Imports
-------
-Working with sttp is most convenient if you import the ``sttp`` package::
+Working with sttp is most convenient if you import the ``sttp`` package entirely::
import com.softwaremill.sttp._
-This brings into scope the starting point for defining requests and some convenience helper methods. All examples in this guide assume, that this import is in place.
+This brings into scope the starting point for defining requests and some helper methods. All examples in this guide assume that this import is in place.
-And that's all you need to start using sttp! To start exploring, import the above, type ``sttp.`` and see where your IDE's auto-complete gets you! Or, read on about the :ref:`basics of defining requests <request_basics>`.
+And that's all you need to start using sttp! To create and send your first request, import the above, type ``sttp.`` and see where your IDE's auto-complete gets you! Or, read on about the :ref:`basics of defining requests <request_basics>`.
diff --git a/docs/requests/authentication.rst b/docs/requests/authentication.rst
index f97f74a..5c60051 100644
--- a/docs/requests/authentication.rst
+++ b/docs/requests/authentication.rst
@@ -1,3 +1,5 @@
+.. _authentication:
+
Authentication
==============
diff --git a/docs/requests/basics.rst b/docs/requests/basics.rst
index fc15f36..6ed8872 100644
--- a/docs/requests/basics.rst
+++ b/docs/requests/basics.rst
@@ -7,25 +7,25 @@ As mentioned in the :ref:`quickstart <quickstart>`, the following import will be
import com.softwaremill.sttp._
-This brings into scope ``sttp``, the starting request. This request can be customised, each time yielding a new, immutable request description (unless a mutable body is set on the request, such as a byte array). As the request description is immutable, it can be freely stored in values, shared across threads, and customized multiple times.
+This brings into scope ``sttp``, the starting request. This request can be customised, each time yielding a new, immutable request definition (unless a mutable body is set on the request, such as a byte array). As the request definition is immutable, it can be freely stored in values, shared across threads, and customized multiple times in various ways.
-For example, we can set a cookie, string-body and specify that this should be a ``POST`` request to a given URI::
+For example, we can set a cookie, ``String`` -body and specify that this should be a ``POST`` request to a given URI::
val request = sttp
.cookie("login", "me")
.body("This is a test")
.post(uri"http://endpoint.com/secret")
-The request parameters (headers, cookies, body etc.) can be specified **in any order**. It doesn't matter if the request method, the body, the headers or connection options are specified in this sequence or another. This way you can build arbitrary request templates, capturing all that's common among your requests, and customizing as needed. Remember, that each time a modifier is applied to a request, you get a new immutable object.
+The request parameters (headers, cookies, body etc.) can be specified **in any order**. It doesn't matter if the request method, the body, the headers or connection options are specified in this sequence or another. This way you can build arbitrary request templates, capturing all that's common among your requests, and customizing as needed. Remember that each time a modifier is applied to a request, you get a new immutable object.
There's a lot of ways in which you can customize a request, which are covered in this guide. Another option is to just explore the API: most of the methods are self-explanatory and carry scaladocs if needed.
-Using the modifiers, each time we get a new request description, but it's just description: a data object; nothing is sent over the network until the ``send()`` method is invoked.
+Using the modifiers, each time we get a new request definition, but it's just a description: a data object; nothing is sent over the network until the ``send()`` method is invoked.
Sending a request
-----------------
-A request description can be created without knowing how it will be sent. But to send a request, a backend is needed. A default, synchronous backend based on Java's ``HttpURLConnection`` is provided out-of-the box.
+A request definition can be created without knowing how it will be sent. But to send a request, a backend is needed. A default, synchronous backend based on Java's ``HttpURLConnection`` is provided out-of-the box.
To invoke the ``send()`` method on a request description, an implicit value of type ``SttpBackend`` needs to be in scope::
@@ -35,6 +35,10 @@ To invoke the ``send()`` method on a request description, an implicit value of t
The default backend doesn't wrap the response into any container, but other asynchronous backends might do so. See the section on :ref:`backends <backends_summary>` for more details.
+.. note::
+
+ Only requests with the request method and uri can be sent. If trying to send a request without these components specified, a compile-time error will be reported. On how this is implemented, see the documentation on the :ref:`type of request definitions <request_type>`.
+
Starting requests
-----------------
@@ -43,5 +47,5 @@ sttp provides two starting requests:
* ``sttp``, which is an empty request with the ``Accept-Encoding: gzip, deflate`` header added. That's the one that is most commonly used.
* ``empty``, a completely empty request, with no headers at all.
-Both of these requests will by default read the response body into a UTF-8 ``String``. How the response body is handled is also part of the request description. See the section on :ref:`response body specifications <responsebodyspec>` for more details on how to customize that.
+Both of these requests will by default read the response body into a UTF-8 ``String``. How the response body is handled is also part of the request definition. See the section on :ref:`response body specifications <responsebodyspec>` for more details on how to customize that.
diff --git a/docs/requests/body.rst b/docs/requests/body.rst
index bae7526..20dd5d6 100644
--- a/docs/requests/body.rst
+++ b/docs/requests/body.rst
@@ -1,5 +1,7 @@
-Request bodies
-==============
+.. _requestbody:
+
+Setting the request body
+========================
Text data
---------
@@ -7,8 +9,8 @@ Text data
In its simplest form, the request's body can be set as a ``String``. By default, this method will:
* use the UTF-8 encoding to convert the string to a byte array
-* if not specified, set ``Content-Type: text/plain``
-* if not specified, set ``Content-Length`` to the number of bytes in the array
+* if not specified before, set ``Content-Type: text/plain``
+* if not specified before, set ``Content-Length`` to the number of bytes in the array
A ``String`` body can be set on a request as follows::
@@ -28,21 +30,21 @@ To set a binary-data body, the following methods are available::
def body(b: ByteBuffer)
def body(b: InputStream)
-If not specified, these methods will set the conten type to ``application/octet-stream``. When using a byte array, additionally the content length will be set to the length of the array (unless specified explicitly).
+If not specified before, these methods will set the content type to ``application/octet-stream``. When using a byte array, additionally the content length will be set to the length of the array (unless specified explicitly).
.. note::
- While the object defining a request is immutable, setting a mutable request body will make the whole request definition mutable as well. With ``InputStream``, the request can be sent only once, as input streams can be consumed once.
+ While the object defining a request is immutable, setting a mutable request body will make the whole request definition mutable as well. With ``InputStream``, the request can be moreover sent only once, as input streams can be consumed once.
Uploading files
---------------
-To upload a file, simply set the request body as a ``File``, ``Path``::
+To upload a file, simply set the request body as a ``File`` or ``Path``::
def body(b: File)
def body(b: Path)
-As with binary body methods, the content type will default to ``application/octet-stream``, and the content length will be set to the lenght of the file (unless specified explicitly).
+As with binary body methods, the content type will default to ``application/octet-stream``, and the content length will be set to the length of the file (unless specified explicitly).
See also :ref:`multi-part <multipart>` and :ref:`streaming <streaming>` support.
@@ -58,14 +60,18 @@ By default, the ``UTF-8`` encoding is used, but can be also specified explicitly
def body(fs: (String, String)*)
def body(fs: Seq[(String, String)], encoding: String)
+.. _requestbody_custom:
+
Custom body serializers
-----------------------
-It is also possible to set custom types as request bodies, as long as there's an implicit ``BodySerializer[B]`` value in scope, which is just an alias for a function::
+It is also possible to set custom types as request bodies, as long as there's an implicit ``BodySerializer[B]`` value in scope, which is simply an alias for a function::
type BodySerializer[B] = B => BasicRequestBody
-A ``BasicRequestBody`` is a wrapper for a ``String``/byte array/input stream body. For example, here's how to write custom serializer for a case class, with serializer-specific default content type::
+A ``BasicRequestBody`` is a wrapper for one of the supported request body types: a ``String``/byte array or an input stream.
+
+For example, here's how to write a custom serializer for a case class, with serializer-specific default content type::
case class Person(name: String, surname: String, age: Int)
diff --git a/docs/requests/headers.rst b/docs/requests/headers.rst
index 1135377..ad2e695 100644
--- a/docs/requests/headers.rst
+++ b/docs/requests/headers.rst
@@ -27,4 +27,4 @@ For some common headers, dedicated methods are provided::
def contentLength(l: Long)
def acceptEncoding(encoding: String)
-See also [cookies], [authentication].
+See also documentation on setting :ref:`cookies <cookies>` and :ref:`authentication <authentication>`.
diff --git a/docs/requests/multipart.rst b/docs/requests/multipart.rst
index 5a1d11d..ac921fc 100644
--- a/docs/requests/multipart.rst
+++ b/docs/requests/multipart.rst
@@ -3,9 +3,9 @@
Multipart requests
==================
-To set a multipart body on a request, the ``multipartBody`` method should be used (instead of ``body``). Each body part is represented as an instance of ``Multipart``, which can be conveniently constructed using ``multipart`` methods.
+To set a multipart body on a request, the ``multipartBody`` method should be used (instead of ``body``). Each body part is represented as an instance of ``Multipart``, which can be conveniently constructed using ``multipart`` methods coming from the ``com.softwaremill.sttp`` package.
-A single part of a multipart request can be of type:
+A single part of a multipart request consist of a mandatory name and a payload of type:
* ``String``
* ``Array[Byte]``
@@ -13,6 +13,8 @@ A single part of a multipart request can be of type:
* ``InputStream``
* ``File``
* ``Path``
+* ``Map[String, String]``
+* ``Seq[(String, String)]``
The content type of each part is by default the same as when setting simple bodies: ``text/plain`` for parts of type ``String``, ``application/x-www-form-urlencoded`` for parts of key-value pairs (form data) and ``application/octet-stream`` otherwise (for binary data).
@@ -32,7 +34,7 @@ For example::
Customising part meta-data
--------------------------
-For each part, an optional filename can be specified, as well as a custom content type and additional headers. The following methods are available on ``Multipart``::
+For each part, an optional filename can be specified, as well as a custom content type and additional headers. The following methods are available on ``Multipart`` instances::
case class Multipart {
def fileName(v: String): Multipart
@@ -40,3 +42,9 @@ For each part, an optional filename can be specified, as well as a custom conten
def header(k: String, v: String): Multipart
}
+For example::
+
+ sttp.multipartBody(
+ multipart("logo", logoFile).fileName("logo.jpg").contentType("image/jpg"),
+ multipart("text", docFile).fileName("text.doc")
+ )
diff --git a/docs/requests/streaming.rst b/docs/requests/streaming.rst
index 600630c..85bf2b6 100644
--- a/docs/requests/streaming.rst
+++ b/docs/requests/streaming.rst
@@ -3,9 +3,13 @@
Streaming
=========
-Some backends (see :ref:`backends summary <backends_summary>`) support streaming bodies. If that's the case, you can set a stream of the supported type as a request body using the ``streamBody`` method.
+Some backends (see :ref:`backends summary <backends_summary>`) support streaming bodies. If that's the case, you can set a stream of the supported type as a request body using the ``streamBody`` method, instead of the usual ``body`` method.
-For example, using the :ref:`akka-http backend <akkahttp>`, a request with a stream body can be defined as follows::
+.. note::
+
+ Here, streaming refers to (usually) non-blocking, asynchronous streams of data. To send data which is available as an ``InputStream``, or a file from local storage (which is available as a ``File`` or ``Path``), no special backend support is needed. See the documenttation on :ref:`setting the request body <requestbody>`.
+
+For example, using the :ref:`akka-http backend <akkahttp>`, a request with a streaming body can be defined as follows::
import com.softwaremill.sttp._
import com.softwaremill.sttp.akkahttp._
diff --git a/docs/requests/type.rst b/docs/requests/type.rst
index 3cefe97..e246def 100644
--- a/docs/requests/type.rst
+++ b/docs/requests/type.rst
@@ -1,17 +1,22 @@
-Request type
-============
+.. _request_type:
-All request descriptions have type ``RequestT[U, T, S]`` (T as in Template).
-If this looks a bit complex, don't worry, what the three type parameters stand
-for is the only thing you'll hopefully have to remember when using the API!
+The type of request definitions
+===============================
+
+All request definitions have type ``RequestT[U, T, S]`` (RequestT as in Request Template). If this looks a bit complex, don't worry, what the three type parameters stand for is the only thing you'll hopefully have to remember when using the API!
Going one-by-one:
* ``U[_]`` specifies if the request method and URL are specified. Using the API, this can be either ``type Empty[X] = None``, meaning that the request has neither a method nor an URI. Or, it can be ``type Id[X] = X`` (type-level identity), meaning that the request has both a method and an URI specified. Only requests with a specified URI & method can be sent.
-* ``T`` specifies the type to which the response will be read. By default, this is ``String``. But it can also be e.g. ``Array[Byte]`` or ``Unit``, if the response should be ignored. Response body handling can be changed by calling the ``.response`` method. With backends which support streaming, this can also be a supported stream type.
-* ``S`` specifies the stream type that this request uses. Most of the time this will be ``Nothing``, meaning that this request does not send a streaming body or receive a streaming response. So most of the times you can just ignore that parameter. But, if you are using a streaming backend and want to send/receive a stream, the ``.streamBody`` or ``response(asStream[S])`` will change the type parameter.
+* ``T`` specifies the type to which the response will be read. By default, this is ``String``. But it can also be e.g. ``Array[Byte]`` or ``Unit``, if the response should be ignored. Response body handling can be changed by calling the ``.response`` method. With backends which support streaming, this can also be a supported stream type. See :ref:`response body specifications <responsebodyspec>` for more details.
+* ``S`` specifies the stream type that this request uses. Most of the time this will be ``Nothing``, meaning that this request does not send a streaming body or receive a streaming response. So most of the time you can just ignore that parameter. But, if you are using a streaming backend and want to send/receive a stream, the ``.streamBody`` or ``response(asStream[S])`` will change the type parameter.
There are two type aliases for the request template that are used:
* ``type Request[T, S] = RequestT[Id, T, S]``. A sendable request.
* ``type PartialRequest[T, S] = RequestT[Empty, T, S]``
+
+As ``sttp``, the starting request, by default reads the body into a ``String``, its type is::
+
+ sttp: PartialRequest[String, Nothing]
+
diff --git a/docs/requests/uri.rst b/docs/requests/uri.rst
index 17e0a2c..93c7b47 100644
--- a/docs/requests/uri.rst
+++ b/docs/requests/uri.rst
@@ -3,9 +3,9 @@ URIs
A request can only be sent if the request method & URI are defined. To represent URIs, sttp comes with a ``Uri`` case class, which captures all of the parts of an address.
-To specify the request method and URI, use one of the methods on the request description corresponding to the name of the method: ``.post``, ``.get``, ``.put`` etc. All of them accept a single parameter, the URI to which the request should be send, and yield a request description which, when sent, will use the given address (these methods only modify the request description; they don't send the requests).
+To specify the request method and URI, use one of the methods on the request definition corresponding to the name of the desired HTTP method: ``.post``, ``.get``, ``.put`` etc. All of them accept a single parameter, the URI to which the request should be sent (these methods only modify the request definition; they don't send the requests).
-The ``Uri`` class is immutable, and can be constructed by hand, but in many cases the URI interpolator will be useful.
+The ``Uri`` class is immutable, and can be constructed by hand, but in many cases the URI interpolator will be easier to use.
URI interpolator
----------------
@@ -19,7 +19,8 @@ Using the URI interpolator it's possible to conveniently create ``Uri`` instance
val endpoint: Uri = uri"http://example.com/$user/skills?filter=$filter"
- assert(endpoint.toString == "http://example.com/Mary%20Smith/skills?filter=programming+languages")
+ assert(endpoint.toString ==
+ "http://example.com/Mary%20Smith/skills?filter=programming+languages")
Note the ``uri`` prefix before the string and the standard Scala string-embedding syntax (``$user``, ``$filter``).
@@ -62,7 +63,7 @@ Special cases
If a string containing the protocol is embedded *as the very beginning*, it will not be escaped, allowing to embed entire addresses as prefixes, e.g.: ``uri"$endpoint/login"``, where ``val endpoint = "http://example.com/api"``.
-This is useful when a base URI is stored in a value, and can then be used as a base for constructing URIs.
+This is useful when a base URI is stored in a value, and can then be used as a base for constructing more specific URIs.
All features combined
---------------------
diff --git a/docs/responses/basics.rst b/docs/responses/basics.rst
index 6157647..3afc2f2 100644
--- a/docs/responses/basics.rst
+++ b/docs/responses/basics.rst
@@ -1,13 +1,13 @@
Responses
=========
-Responses are represented as instances of the case class ``Response[T]``, where ``T`` is the type of the response body. When sending a request, the response will be returned in a wrapper. For example, for asynchronous backends, we will get a ``Future[Response[T]]``, while for the default synchronous backend, the wrapper backend will be the no-op wrapper, ``Id``, which is the same as no wrapper at all.
+Responses are represented as instances of the case class ``Response[T]``, where ``T`` is the type of the response body. When sending a request, the response will be returned in a wrapper. For example, for asynchronous backends, we can get a ``Future[Response[T]]``, while for the default synchronous backend, the wrapper will be a no-op, ``Id``, which is the same as no wrapper at all.
If sending the request fails, either due to client or connection errors, an exception will be thrown (synchronous backends), or an error will be represented in the wrapper (e.g. a failed future).
.. note::
- If the request completes, but results in a non-2xx return code, the request is still considered successful, that is, a ``Response[T]`` will be returned. See the next subsection for details regarding body handling.
+ If the request completes, but results in a non-2xx return code, the request is still considered successful, that is, a ``Response[T]`` will be returned. See :ref:`response body specifications <responsebodyspec>` for details on how such cases are handled.
Response code
-------------
@@ -40,11 +40,11 @@ Obtaining the response body
The response body can be obtained through the ``.body`` property, which has type ``Either[String, T]``. ``T`` is the body deserialized as specified in the request - see the next section on :ref:`response body specifications <responsebodyspec>`.
-The response body is an either as the response body can only be deserialized if the server responded with code 200. Otherwise, the response body is most probably an error message.
+The response body is an either as the body can only be deserialized if the server responded with a success code (2xx). Otherwise, the response body is most probably an error message.
Hence, the ``response.body`` will be a:
* ``Left(errorMessage)`` if the request is successful, but response code is not 2xx.
* ``Right(deserializedBody``) if the request is successful and the response code is 2xx.
-You can also forcibly get the deserialized body, risking an excepiton being thrown, using the ``response.unsafeBody`` method.
+You can also forcibly get the deserialized body, regardless of the response code and risking an excepiton being thrown, using the ``response.unsafeBody`` method.
diff --git a/docs/responses/body.rst b/docs/responses/body.rst
index da3009c..10658ed 100644
--- a/docs/responses/body.rst
+++ b/docs/responses/body.rst
@@ -5,12 +5,12 @@ Response body specification
By default, the received response body will be read as a ``String``, using the ``UTF-8`` encoding. This is of course configurable: response bodies can be ignored, deserialized into custom types, recevied as a stream or saved to a file.
-How the response body will be read is part of the request definition, as already when sending the request, the backend needs to know what to do with the response. The type to which the response body should be deserialized is the second type parameter of ``RequestT``, and stored in the request definition as ``request.response``, which has type ``ResponseAs[T, S]``.
+How the response body will be read is part of the request definition, as already when sending the request, the backend needs to know what to do with the response. The type to which the response body should be deserialized is the second type parameter of ``RequestT``, and stored in the request definition as the ``request.response: ResponseAs[T, S]`` property.
Basic response specifications
-----------------------------
-To conveniently specify how to deserialize the response body, a number of ``as[Type]`` methods are available. They can be used to provide a value for the requet's ``response`` modifier::
+To conveniently specify how to deserialize the response body, a number of ``asXxx`` methods are available. They can be used to provide a value for the request definition's ``response`` modifier::
sttp.response(asByteArray)
@@ -31,12 +31,14 @@ Hence, to discard the response body, simply specify::
And to save the response to a file::
- sttp.respone(asFile(someFile))
+ sttp.response(asFile(someFile))
.. note::
As the handling of response is specified upfront, there's no need to "consume" the response body. It can be safely discarded if not needed.
+.. _responsebodyspec_custom:
+
Custom body deserializers
-------------------------
@@ -55,6 +57,10 @@ To integrate with a third-party JSON library::
def parseJson(json: String): Either[JsonError, JsonAST] = ...
val asJson: ResponseAs[Either[JsonError, JsonAST], Nothing] = asString.map(parseJson)
+ sttp
+ .response(asJson)
+ ...
+
For some mapped response specifications available out-of-the-box, see :ref:`json support <json>`.
Streaming