<commit_before># Interceptors (WIP)

Reitit has also support for [Pedestal](pedestal.io)-style [interceptors](http://pedestal.io/reference/interceptors) via `reitit.interceptor` package. Currently, there is no interceptor interpreter shipped, just a way to compose and manage the interceptor chains.

Plan is to have a full-featured `reitit-http` module with same features as the `reitit-ring` - enchanced interceptor maps & interceptor compilations. Stay tuned.

### TODO

* Figure out how to make a truly portable Interceptor definitions, e.g. Pedestal has namespaced keys for context errors, queues etc.
* Separate modules for interceptor interpreters (including cljs)
* Finalize `reitit-http` module as an alternative to `reitit-ring`

### Example

Current `reitit-http` draft (with data-specs):

```clj
(require '[reitit.http.coercion :as rhc])
(require '[reitit.http :as http])
(require '[reitit.coercion.spec])
(require '[clojure.set :as set])

(def auth-interceptor
  "Interceptor that mounts itself if route has `:roles` data. Expects `:roles`
  to be a set of keyword and the context to have `[:user :roles]` with user roles.
  responds with HTTP 403 if user doesn't have the roles defined, otherwise no-op."
  {:name ::auth
   :compile (fn [{:keys [roles]} _]
              (if (seq roles)
                {:description (str "requires roles " roles)
                 :spec {:roles #{keyword?}}
                 :context-spec {:user {:roles #{keyword}}}
                 :enter (fn [{{user-roles :roles} :user :as ctx}]
                          (if (not (set/subset? roles user-roles))
                            (assoc ctx :response {:status 403, :body "forbidden"})
                            ctx))}))})(require '[clojure.set :as set])

(def app
  (http/http-handler
    (http/router
      ["/api" {:interceptors [auth-interceptor]}
       ["/ping" {:name ::ping
                 :get (constantly
                        {:status 200
                         :body "pong"})}]
       ["/plus/:z" {:name ::plus
                    :post {:parameters {:query {:x int?}
                                        :body {:y int?}
                                        :path {:z int?}}
                           :responses {200 {:body {:total pos-int?}}}
                           :roles #{:admin}
                           :handler (fn [{:keys [parameters]}]
                                      (let [total (+ (-> parameters :query :x)
                                                     (-> parameters :body :y)
                                                     (-> parameters :path :z))]
                                        {:status 200
                                         :body {:total total}}))}}]]
      {:data {:coercion reitit.coercion.spec/coercion
              :interceptors [rhc/coerce-exceptions-interceptor
                             rhc/coerce-request-interceptor
                             rhc/coerce-response-interceptor]}})))
```<commit_msg>Interceptor docs<commit_after># Interceptors (WIP)

Reitit also support for [Pedestal](pedestal.io)-style [interceptors](http://pedestal.io/reference/interceptors) as an alternative to Middleware. Basic interceptor handling is implemented in `reitit.interceptor` package. 

There is no interceptor executor shipped, but you can use libraries like [Pedestal Interceptor](https://github.com/pedestal/pedestal/tree/master/interceptor) or [Sieppari](https://github.com/metosin/sieppari) to execute the chains.

## Reitit-http

An alternative to `reitit-ring`, using interceptors instead of middleware. Currently not finalized, you can track progress in [here](https://github.com/metosin/reitit/pull/124).

## Examples

### Pedestal

**TODO**

### Sieppari

* [Sieppari](https://github.com/metosin/sieppari)
* [Manifold](https://github.com/ztellman/manifold)
* [data-specs](https://github.com/metosin/spec-tools/blob/master/README.md#data-specs):

```clj
(require '[reitit.interceptor.sieppari :as sieppari])
(require '[reitit.http.coercion :as coercion])
(require '[reitit.http :as http])
(require '[reitit.ring :as ring])
(require '[reitit.coercion.spec])
(require '[clojure.set :as set])
(require '[manifold.deferred :as d])
(require '[ring.adapter.jetty :as jetty])

(def auth-interceptor
  "Interceptor that mounts itself if route has `:roles` data. Expects `:roles`
  to be a set of keyword and the context to have `[:user :roles]` with user roles.
  responds with HTTP 403 if user doesn't have the roles defined, otherwise no-op."
  {:name ::auth
   :compile (fn [{:keys [roles]} _]
              (if (seq roles)
                {:description (str "requires roles " roles)
                 :spec {:roles #{keyword?}}
                 :context-spec {:user {:roles #{keyword}}}
                 :enter (fn [{{user-roles :roles} :user :as ctx}]
                          (if (not (set/subset? roles user-roles))
                            (assoc ctx :response {:status 403, :body "forbidden"})
                            ctx))}))})

(def async-interceptor
  {:enter (fn [ctx] (d/future ctx))})

(def app
  (http/ring-handler
    (http/router
      ["/api" {:interceptors [async-interceptor auth-interceptor]}
       ["/ping" {:name ::ping
                 :get (constantly
                        {:status 200
                         :body "pong"})}]
       ["/plus/:z" {:name ::plus
                    :post {:parameters {:query {:x int?}
                                        :body {:y int?}
                                        :path {:z int?}}
                           :responses {200 {:body {:total pos-int?}}}
                           :roles #{:admin}
                           :handler (fn [{:keys [parameters]}]
                                      (let [total (+ (-> parameters :query :x)
                                                     (-> parameters :body :y)
                                                     (-> parameters :path :z))]
                                        {:status 200
                                         :body {:total total}}))}}]]
      {:data {:coercion reitit.coercion.spec/coercion
              :interceptors [coercion/coerce-exceptions-interceptor
                             coercion/coerce-request-interceptor
                             coercion/coerce-response-interceptor]}})
    (ring/create-default-handler)
    {:executor sieppari/executor}))

(jetty/run-jetty #'app {:port 3000, :join? false, :async? true})
```
{'fraction_non_alphanumeric': 0.13030724100663887, 'fraction_numerical': 0.0057125212289640265, 'mean_word_length': 1.8676405489154493, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '467132', 'n_tokens_mistral': 1910, 'n_tokens_neox': 1779, 'n_words': 524}