<commit_before>.. -*- RST -*-

.. _manual-decorators:

Decorators
==========

One of the attractive features of applications based on `WSGI`_ is the
use of "middleware" components. `RESTAS`_ has similar capabilities
provided by decorators.  This is done by using
:class:`routes:proxy-route` to create a wrapper over the routes and
redefining their behavior by specializing on the associated generic
functions.

**Decorator** - a function that takes the route and returns a different route.

Decorators can be used for things like:

* Authentication/authorization

* Fine-tuning the response headers

* Environment settings around processing of the request

For example, to completely prevent caching in browsers you can use this decorator:

.. code-block:: common-lisp

  (defclass no-cache-route (routes:proxy-route) ())

  (defmethod process-route :before ((route no-cache-route) bindings)
    (setf (hunchentoot:header-out :expires)
          (hunchentoot:rfc-1123-date))  
    (setf (hunchentoot:header-out :cache-control)
          "max-age=0, no-store, no-cache, must-revalidate"))

  (defun @no-cache (route)
    (make-instance 'no-cache-route :target route))

Now you can use this decorator when defining a route:

.. code-block:: common-lisp

  (restas:define-route main ("" :decorators '(@no-cahe))
    "<h1>Hello world!</h1>")

Also it can be applied to the whole module:

.. code-block:: common-lisp

  (restas:define-module #:restas.hello-world
    (:use :cl)
    (:decorators #'restas:@no-caсhe))

Or even when mounting a submodule:

.. code-block:: common-lisp

  (restas:mount-submodule test-hello-world (#:hello-world restas:@no-caсhe))

All routes defined through :macro:`restas:define-route` pass through a
series of decorators before being placed in the dispatch tree. The
decorators listed in :macro:`restas:define-route` are applied first,
then the decorators listed in :macro:`restas:define-module`, and
finally the decorators listed in
:macro:`restas:mount-submodule`. Thus, instead of the original
:class:`restas:route` object, a chain of nested proxy objects is
inserted in the dispatch tree.

To define a new decorator, you need to define a new class that
inherits from :class:`routes:proxy-route`, define a function to create
an object of this class from the original route, and specialize
methods on the following generic functions:

* :fun:`routes:route-check-conditions` (route bindings) - 
  called when the route template URL matches the request URL, allowing
  you to define additional restrictions. If this function returns :hs:`T`, then the route is
  considered to satisfy the request, in the case :hs:`NIL` the route is discarded and
  the system goes to try the next route.

* :fun:`restas:process-route` (route bindings) -
  called for the actual request handling.

Below is an example of using a decorator for HTTP authentication when browsing
a directory published via `restas-directory-publisher`_.

.. code-block:: common-lisp

  (defclass http-auth-route (routes:proxy-route) ())

  (defmethod routes:route-check-conditions ((route http-auth-route) bindings)
    (and (call-next-method)
         (multiple-value-bind (user password) (hunchentoot:authorization)<commit_msg>Fixed minor English and improved documentation for clarity.<commit_after>.. -*- RST -*-

.. _manual-decorators:

Decorators
==========

One of the attractive features of applications based on `WSGI`_ is the
use of "middleware" components. `RESTAS`_ has similar capabilities
provided by decorators.  This is done by using
:class:`routes:proxy-route` to create a wrapper over the routes and
redefining their behaviors by specializing on the associated generic
functions.

**Decorator** - a function that takes the route and returns a different route.

Decorators can be used for many purposes including:

* Authentication/authorization

* Fine-tuning response headers

* Environment settings used for request processing

For example, to completely prevent caching in browsers you can define
a decorator named :fun:`@no-cache` like so:

.. code-block:: common-lisp

  (defclass no-cache-route (routes:proxy-route) ())

  (defmethod process-route :before ((route no-cache-route) bindings)
    (setf (hunchentoot:header-out :expires)
          (hunchentoot:rfc-1123-date))
    (setf (hunchentoot:header-out :cache-control)
          "max-age=0, no-store, no-cache, must-revalidate"))

  (defun @no-cache (route)
    (make-instance 'no-cache-route :target route))

Now you can use the decorator when defining a route:

.. code-block:: common-lisp

  (restas:define-route main ("" :decorators '(@no-cahe))
    "<h1>Hello world!</h1>")

It can also be applied to the whole module:

.. code-block:: common-lisp

  (restas:define-module #:restas.hello-world
    (:use :cl)
    (:decorators #'restas:@no-caсhe))

Or even when mounting a submodule:

.. code-block:: common-lisp

  (restas:mount-submodule test-hello-world (#:hello-world restas:@no-caсhe))

All routes, which are defined through :macro:`restas:define-route`,
are passed through a series of decorators before being placed in the
dispatch tree.  The decorators listed in :macro:`restas:define-route`
are applied first, then the decorators listed in
:macro:`restas:define-module`, and finally the decorators listed in
:macro:`restas:mount-submodule`.  Thus, a chain of nested proxy
objects is inserted into the dispatch tree instead of the original
:class:`restas:route` object.

To define a new decorator, you need to define a new class that
inherits from :class:`routes:proxy-route`, then define a function to
create an object of this class from the original route, and define the
specialized methods on the following generic functions:

* :fun:`routes:route-check-conditions` (route bindings) -
  called when the route template URL matches the request URL, allowing
  you to define additional restrictions.  If this function returns :hs:`T`, the route is
  considered to satisfy the request; in case of :hs:`NIL` the route is discarded and
  the system moves on to try the next route.

* :fun:`restas:process-route` (route bindings) -
  called for actual request handling.

Below is an example of using a decorator for HTTP authentication when browsing
a directory published via `restas-directory-publisher`_.

.. code-block:: common-lisp

  (defclass http-auth-route (routes:proxy-route) ())

  (defmethod routes:route-check-conditions ((route http-auth-route) bindings)
    (and (call-next-method)
         (multiple-value-bind (user password) (hunchentoot:authorization)
{'fraction_non_alphanumeric': 0.10055012224938875, 'fraction_numerical': 0.002139364303178484, 'mean_word_length': 4.427031509121061, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2108484', 'n_tokens_mistral': 1946, 'n_tokens_neox': 1894, 'n_words': 734}