Подтвердить что ты не робот

Почему необходимо заменить порядок промежуточного ПО Ring?

Я пишу несколько промежуточных программ для Ring, и я очень смущен тем, почему мне приходится менять порядок промежуточного программного обеспечения.

Я нашел этот пост в блоге, но это не объясняет, почему я должен его отменить.

Вот краткий отрывок из сообщения в блоге:

(def app
  (wrap-keyword-params (wrap-params my-handler)))

Ответ будет следующим:

{; Trimmed for brevity
 :params {"my_param" "54"}}

Обратите внимание, что параметры ключевого слова wrap не вызывались, потому что hash параметров еще не существовал. Но когда вы меняете порядок промежуточного ПО следующим образом:

(def app
  (wrap-params (wrap-keyword-params my-handler)))

{; Trimmed for brevity
 :params {:my_param "54"}}

Он работает.

Может кто-нибудь объяснить, почему вам нужно изменить порядок промежуточного ПО?

4b9b3361

Ответ 1

Это помогает визуализировать, что такое промежуточное ПО.

(defn middleware [handler]
  (fn [request]
    ;; ...
    ;; Do something to the request before sending it down the chain.
    ;; ...
    (let [response (handler request)]
      ;; ...
      ;; Do something to the response that coming back up the chain.
      ;; ...
      response)))

Это было в значительной степени для меня а-ха.

На первый взгляд, сбив с толку, что промежуточное ПО не применяется к запросу, о чем вы думаете.

Вспомните, что приложение Ring - это просто функция, которая принимает запрос и возвращает ответ (что означает его обработчик):

((fn [request] {:status 200, ...}) request)  ;=> response

Уменьшите немного. Мы получаем другой обработчик:

((GET "/" [] "Hello") request)  ;=> response

Позвольте уменьшить масштаб еще немного. Мы находим обработчик my-routes:

(my-routes request)  ;=> response

Что ж, если вы хотите что-то сделать перед отправкой запроса обработчику my-routes? Вы можете обернуть его другим обработчиком.

((fn [req] (println "Request came in!") (my-routes req)) request)  ;=> response

Это немного трудно читать, поэтому пусть прорывается для ясности. Мы можем определить функцию, которая возвращает этот обработчик. Среднее ПО - это функции, которые обрабатывают обработчик и переносят его другим обработчиком. Он не возвращает ответ. Он возвращает обработчик, который может вернуть ответ.

(defn println-middleware [wrapped-func]
  (fn [req]
    (println "Request came in!")
    (wrapped-func req)))

((println-middleware my-route) request)  ;=> response

И если нам нужно что-то сделать, прежде чем даже println-middleware получит запрос, мы можем снова его обернуть:

((outer-middleware (println-middleware my-routes)) request)  ;=> response

Ключ в том, что my-routes, как и ваш my-handler, является единственной именованной функцией, которая фактически принимает запрос в качестве аргумента.

Последняя демонстрация:

(handler3 (handler2 (handler1 request)))  ;=> response
((middleware1 (middleware2 (middleware3 handler1))) request)  ;=> response

Я пишу так много, потому что могу сочувствовать. Но прокрутите назад до моего первого примера middleware и, надеюсь, это имеет смысл.

Ответ 2

Кольцо промежуточного программного обеспечения представляет собой ряд функций, которые при укладке возвращают функцию обработчика.

Раздел статьи, который отвечает на ваш вопрос:

В случае оберток Ring, как правило, у нас есть "перед" декораторы, которые выполнить некоторые приготовления перед вызовом "реальной" бизнес-функции. Поскольку они представляют собой функции более высокого порядка, а не прямые вызовы функций, они применяются в обратном порядке. Если кто-то зависит от другого, зависимый должен находиться на "внутри".

Вот надуманный пример:

(let [post-wrap (fn [handler]
                  (fn [request]
                    (str (handler request) ", post-wrapped")))
      pre-wrap (fn [handler]
                 (fn [request]
                   (handler (str request ", pre-wrapped"))))
      around (fn [handler]
               (fn [request]
                 (str (handler (str request ", pre-around")) ", post-around")))
      handler (-> (pre-wrap identity)
                  post-wrap
                  around)]
  (println (handler "(this was the input)")))

Отпечатывает и возвращает:

(this was the input), pre-around, pre-wrapped, post-wrapped, post-around
nil

Ответ 3

Как вы знаете, кольцо app на самом деле является просто функцией, которая получает карту request и возвращает карту response.

В первом случае порядок, в котором применяются функции, таков:

request -> [wrap-keyword-params -> wrap-params -> my-handler] -> response

wrap-keyword-params ищет ключ :params в request, но он не существует с wrap-params - это тот, кто добавляет этот ключ на основе "urlencoded parameters из строки запроса и тела формы".

Когда вы инвертируете порядок этих двух:

request -> [wrap-params -> wrap-keyword-params -> my-handler] -> response

Вы получаете желаемый результат, так как после того, как request получает значение wrap-keyword-params, wrap-params уже добавлены соответствующие ключи.