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

14

如何在ring处理程序中最方便地注入状态(而不使用全局变量)?

以下是一个示例:

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

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

我希望将the-state传递给main-routes的compojure handler中。该状态可能类似于使用以下代码创建的map:

我希望将the-state传递给main-routes的Compojure处理程序。该状态可能类似于使用以下代码创建的地图:

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

在非Ring应用程序中,我会在主函数中创建状态,并将其作为函数参数注入到应用程序的不同组件中,或者是它们的部分。

在不使用全局变量的情况下,能否使用Ring的:init函数实现类似的功能?


你是在寻找每个会话状态还是全局状态? - astine
全局变量 - 生命周期与:init和:destroy相同 - 4ZM
3个回答

22

我见过这样做的几种方式。第一种方法是使用中间件将状态作为请求映射中的一个新键注入。例如:

我见过这个以不同的方式实现。首先是使用中间件,在请求映射中注入状态作为一个新的键。例如:

(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的调用,这将在后台创建一个处理程序并将其分配给一个变量,而是使用一个函数来接受某些状态,然后创建路由,在路由定义中将状态作为参数注入函数调用中:

(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))

如果让我选择,我可能会选择第二种方法。


1
谢谢!第二种方法似乎很适合。第一种方法感觉有点不太正规。 - 4ZM

1
除了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

这个问题的“正确”解决方法是使用动态绑定变量。您可以通过以下方式定义一个变量:

(def ^:dynamic some-state nil)

然后您可以创建一些环形中间件,为每个处理程序调用绑定变量:

(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 ...})))

你将会使用动态方式进行大量的状态管理。这可能会在后期引起一些严重的问题。 - Virmundi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接