如何使中间件仅在`:swagger {:deprecated true}`时放置响应标头?

时间:2019-03-27 00:07:57

标签: clojure compojure ring compojure-api

我们使用compojure-api在环形应用程序中为我们提供了一些不错的整合。 :swagger {:deprecated true}元像冠军一样工作,以正确地显示摇摇欲坠的页面,但是我有一个要求,当路由为: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 个答案:

答案 0 :(得分:1)

使用Compojure-API,在定义它们的路径上下文中就地调用中间件。在您的示例中,wrap-bob-response-header尚不知道请求将要到达的位置(或者甚至将匹配任何内容)。如果知道,则可以使用从请求中注入的路由信息​​(请参见https://github.com/metosin/compojure-api/blob/master/src/compojure/api/api.clj#L71-L73)来确定端点是否设置了摇摇欲坠的信息。

您可以做的是将标头设置中间件仅安装到需要它的路由上。

有一个名为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}}