pekko/akka-docs-dev/rst/scala/http/routing-dsl/case-class-extraction.rst

68 lines
No EOL
4.1 KiB
ReStructuredText

.. _Case Class Extraction:
Case Class Extraction
=====================
The value extraction performed by :ref:`Directives` is a nice way of providing your route logic with interesting request
properties, all with proper type-safety and error handling. However, in some case you might want even more.
Consider this example:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-1
Here the :ref:`-parameters-` directives is employed to extract three ``Int`` values, which are then used to construct an
instance of the ``Color`` case class. So far so good. However, if the model classes we'd like to work with have more
than just a few parameters the overhead introduced by capturing the arguments as extractions only to feed them into the
model class constructor directly afterwards can somewhat clutter up your route definitions.
If your model classes are case classes, as in our example, Akka HTTP supports an even shorter and more concise
syntax. You can also write the example above like this:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-2
You can postfix any directive with extractions with an ``as(...)`` call. By simply passing the companion object of your
model case class to the ``as`` modifier method the underlying directive is transformed into an equivalent one, which
extracts only one value of the type of your model class. Note that there is no reflection involved and your case class
does not have to implement any special interfaces. The only requirement is that the directive you attach the ``as``
call to produces the right number of extractions, with the right types and in the right order.
If you'd like to construct a case class instance from extractions produced by *several* directives you can first join
the directives with the ``&`` operator before using the ``as`` call:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-3
Here the ``Color`` class has gotten another member, ``name``, which is supplied not as a parameter but as a path
element. By joining the ``path`` and ``parameters`` directives with ``&`` you create a directive extracting 4 values,
which directly fit the member list of the ``Color`` case class. Therefore you can use the ``as`` modifier to convert
the directive into one extracting only a single ``Color`` instance.
Generally, when you have routes that work with, say, more than 3 extractions it's a good idea to introduce a case class
for these and resort to case class extraction. Especially since it supports another nice feature: validation.
.. caution:: There is one quirk to look out for when using case class extraction: If you create an explicit companion
object for your case class, no matter whether you actually add any members to it or not, the syntax presented above
will not (quite) work anymore. Instead of ``as(Color)`` you will then have to say ``as(Color.apply)``. This behavior
appears as if it's not really intended, so this might be improved in future Scala versions.
Case Class Validation
---------------------
In many cases your web service needs to verify input parameters according to some logic before actually working with
them. E.g. in the example above the restriction might be that all color component values must be between 0 and 255.
You could get this done with a few :ref:`-validate-` directives but this would quickly become cumbersome and hard to
read.
If you use case class extraction you can put the verification logic into the constructor of your case class, where it
should be:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-4
If you write your validations like this Akka HTTP's case class extraction logic will properly pick up all error
messages and generate a ``ValidationRejection`` if something goes wrong. By default, ``ValidationRejections`` are
converted into ``400 Bad Request`` error response by the default :ref:`RejectionHandler <The RejectionHandler>`, if no
subsequent route successfully handles the request.