Как мне заставить промежуточное программное обеспечение помещать заголовок ответа только тогда, когда `:swagger {: устаревшее значение true}`?

#clojure #compojure #ring #compojure-api

#clojure #compojure #звонит #compojure-api

Вопрос:

Мы используем compojure-api, чтобы обеспечить нам приятную интеграцию swagger в наши приложения ring. :swagger {:deprecated true} Meta работает как чемпион для корректного отображения страницы swagger, но у меня есть требование, чтобы я помещал определенный заголовок в ответ, когда маршрут :swagger {:deprecated true} . Я изо всех сил пытаюсь понять, как это сделать с шаблоном промежуточного программного обеспечения, который я использовал для выполнения аналогичных манипуляций с заголовком ответа.

 (ns bob.routes
  (:require [clojure.tools.logging :as log]
            [compojure.api.sweet :refer :all]
            [ring.util.http-response :as status]
            [schema.core :as s]
            [ring.swagger.schema :as rs]))

(s/defschema BobResponse {:message (rs/describe String "Message")})

(defn wrap-bob-response-header [handler]
  (fn [request]
    (let [response (handler request)]
      ;; can I reach into the request or the response to see what
      ;; route served this and if it has the :swagger {:deprecated true}
      ;; meta on it and NOT emit the x-bob header if it does?
      (assoc-in response [:headers "x-bob"] "Robert"))))

(defroutes bob-routes
  (context "" []
    :middleware [wrap-bob-response-header]
    :tags ["bob"]
    :description ["Tease out how to do swagger driven response header"]
    (GET "/notdeprectated" [:as request]
      :swagger {:deprecated false}
      :new-relic-name "GET_notdeprecated"
      :return BobResponse
      (status/ok {:message "All is well"}))
    (GET "/isdeprecated" [:as request]
      :swagger {:deprecated true}
      :new-relic-name "GET_isdeprecated"
      :return BobResponse
      (status/ok {:message "You came to the wrong neighborhood."}))))
  

Как мне изменить, wrap-bob-response-header чтобы выдавать только x-bob на маршрутах с :swagger {:deprecated true} ?

Комментарии:

1. Я не думаю, что внутри оболочки можно что-то сделать, что могло бы это сделать, поскольку обработчики колец являются request -> response отображением, поэтому любая информация о том, какой маршрут был сопоставлен, неизвестна до того, как вы перейдете к обработчику, и теряется к тому времени, когда вы доберетесь до результата. Тогда решение состоит в том, чтобы изменить / заменить compojure.core / routing ( github.com/weavejester/compojure/blob/1.5.1/src/compojure / … ) чтобы связать соответствующий запрос с ответом, я полагаю.

2. Тогда проблема будет в следующем: как насчет вложенных маршрутов?

Ответ №1:

С помощью Compojure-API промежуточное программное обеспечение вызывается на месте, в контексте пути, в котором они определены. В вашем примере wrap-bob-response-header еще не знает, куда отправится запрос (или он вообще будет соответствовать чему-либо). Если бы оно знало, вы могли бы использовать введенную информацию о маршруте из запроса (см. https://github.com/metosin/compojure-api/blob/master/src/compojure/api/api.clj#L71-L73 ) чтобы определить, будут ли конечные точки иметь заданную информацию о swagger.

Что вы могли бы сделать, так это смонтировать промежуточное программное обеспечение с настройкой заголовка только для маршрутов, которым это необходимо.

Существует библиотека под названием reitit (также от Metosin), которая решает эту проблему, применяя архитектуру, основанную на маршруте: сначала выполняется поиск полного пути, а после этого применяется цепочка промежуточного программного обеспечения. Из-за этого все промежуточное программное обеспечение знает конечную точку, к которой они подключены. Промежуточное программное обеспечение может просто запрашивать данные конечной точки (либо во время запроса, либо во время компиляции) и действовать соответствующим образом. Они могут даже решить не подключаться к этому специальному маршруту.

Reitit аналогичен compojure-api, только с другим синтаксисом, например, полностью управляемый данными.

Хорошие примеры в блоге:https://www.metosin.fi/blog/reitit-ring /

PS. Я являюсь соавтором обеих библиотек.

Редактировать.

Решение для ввода данных в ответ после совпадения:

1) создайте промежуточное программное обеспечение, которое добавляет данные (или метаданные) к ответу

2) добавьте или измените обработчик реструктуризации, чтобы смонтировать промежуточное программное обеспечение с 1 в конечную точку с заданными данными (доступными в обработчике)

3) прочитайте данные в конвейере ответов и действуйте соответствующим образом

 (defn wrap-add-response-data [handler data]
  (let [with-data #(assoc % ::data data)]
    (fn
      ([request]
       (with-data (handler request)))
      ([request respond raise]
       (handler #(respond (with-data %)) raise)))))

(defmethod compojure.api.meta/restructure-param :swagger [_ swagger acc]
  (-> acc
      (assoc-in [:info :public :swagger] swagger)
      (update-in [:middleware] into `[[wrap-add-response-data ~swagger]])))

(def app
  (api
    (context "/api" []
      (GET "/:kikka" []
        :swagger {:deprecated? true}
        (ok "jeah")))))

(app {:request-method :get, :uri "/api/kukka"})
; {:status 200, :headers {}, :body "jeah", ::data {:deprecated? true}}
  

Комментарии:

1. Фигня. «монтируйте промежуточное программное обеспечение с настройкой заголовка только на те маршруты, которым это необходимо». это проблема, которую я пытаюсь преодолеть; дублирование кода для поддержки :swagger {:deprecated false} . Хотя это странно. В моем промежуточном программном обеспечении маршрут «произошел», я изменяю ответ ради бога, просто, кажется, нигде я не могу посмотреть, КАКОЙ маршрут только что был выполнен. Я смотрю не в том месте? Есть ли что-нибудь в ответе, что я могу использовать для поиска meta маршрута? То, что я пытаюсь добавить, заголовок ПРЕДУПРЕЖДЕНИЯ для устаревших ответов маршрута, имеет слишком низкое значение для изменения фреймворков

2. Хорошая правка. Я начал рассматривать (defmethod compojure.api.meta/restructure-param ... как средство достижения этого. Мой блок заключается в том, что я не до конца понимаю, что все происходит в (update-in [:middleware] into [[wrap-add-response-data ~swagger]])`. Я попробую это, когда у меня будет возможность. Я почти уверен, что смогу заставить это работать.

3. По сути, это генератор кода. Попробуйте macroexpand в (GET...) форме посмотреть, что она выдает.