将状态作为参数传递给环形处理程序?

问题描述 投票:13回答:3

如何将状态最方便地注入到环形处理程序中(不使用全局变量)?

下面是一个例子。

(defroutes main-routes
  (GET "/api/fu" [] (rest-of-the-app the-state)))

(def app
  (-> (handler/api main-routes)))

我想得到 the-state 的compojure处理程序中。main-routes. 状态可能是类似于用.NET创建的地图的东西。

(defn create-app-state []
  {:db (connect-to-db)
   :log (create-log)})

在一个非环形应用程序中,我会在一个主函数中创建状态,然后开始注入它,或者它的一部分,作为函数参数注入到应用程序的不同组件中。

类似的事情可以用环的 :init 函数而不使用全局变量?

dependency-injection clojure compojure ring
3个回答
21
投票

我见过几种方法。第一种是使用中间件,将状态作为一个新的键注入到请求地图中。比如说,在请求映射中注入一个新的键。

(defroutes main-routes
  (GET "/api/fu" [:as request]
    (rest-of-the-app (:app-state request))))

(defn app-middleware [f state]
  (fn [request]
    (f (assoc request :app-state state))))

(def app
  (-> main-routes
      (app-middleware (create-app-state))
      handler/api))

另一种方法是用调用来代替 defroutes在幕后,它会创建一个处理程序,并将其分配给一个var,其中的一个函数会接受一些状态,然后创建路由,将状态作为参数注入到路由定义的函数调用中。

(defn app-routes [the-state]
  (compojure.core/routes
    (GET "/api/fu" [] (rest-of-the-app the-state))))

(def app
  (-> (create-app-state)
      app-routes
      api/handler))

如果要选择的话,我可能会选择第二种方法。


0
投票

除了Alex所描述的一些路由框架外,还有一些用于 ring 有一个附加参数的地方,所有处理程序都可以访问这些参数。在 reitit 这可以通过将自定义对象放在 :data:

 (reiti.ring/ring-handler
   (reiti.ring/router
    [ ["/api"
      ["/math" {:get {:parameters {:query {:x int?, :y int?}}
                      :responses  {200 {:body {:total pos-int?}}}
                      :handler    (fn [{{{:keys [x y]} :query} :parameters}]
                                    {:status 200
                                     :body   {:total (+ x y)}})}}]] ]
    {:syntax    :bracket
     :exception pretty/exception
     :data      {:your-custom-data your-custom-data
                 :coercion   reitit.coercion.spec/coercion
                 :muuntaja   m/instance
                 :middleware []}}))

在你的处理程序中,你应该只使用 :parameters但您可以通过选择以下方式访问您的自定义数据 :reitit.core/match:data. 处理程序收到的参数完全基于此。

(defrecord Match [template data result path-params path])
(defrecord PartialMatch [template data result path-params required])

-1
投票

"正确 "的方法是使用一个动态绑定的var,你用..:

(def ^:dynamic some-state nil)

然后你创建一些环形中间件,为每个处理程序的调用绑定这个var。

(defn wrap-some-state-middleware [handler some-state-value]
  (fn [request]
    (bind [some-state some-state-value]
      (handler request))))

你可以在启动服务器的 "main "函数中使用这个来注入依赖关系。

(def app (-> handler
             (wrap-some-state-middleware {:db ... :log ...})))
© www.soinside.com 2019 - 2024. All rights reserved.