#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...)
форме посмотреть, что она выдает.