【问题标题】:Passing state as parameter to a ring handler?将状态作为参数传递给环处理程序?
【发布时间】:2013-11-15 14:06:04
【问题描述】:

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

这是一个例子:

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

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

我想让the-state 进入main-routes 的复合处理程序。状态可能类似于使用以下内容创建的地图:

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

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

可以在不使用全局变量的情况下使用 ring 的 :init 函数完成类似的操作吗?

【问题讨论】:

  • 您是在寻找每个会话状态还是全局状态?
  • global - 与 :init 和 :destroy 相同的生命周期

标签: dependency-injection clojure compojure ring


【解决方案1】:

我已经看到了几种方法。第一种是使用中间件,将状态作为新键注入到请求映射中。例如:

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

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

【讨论】:

  • 谢谢!第二种方法似乎很合适。第一个感觉有点hack-ish。
【解决方案2】:

除了 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])

【讨论】:

    【解决方案3】:

    执行此操作的“正确”方法是使用动态绑定的 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))))
    

    您可以在启动服务器的“主”函数中使用它来注入依赖项:

    (def app (-> handler
                 (wrap-some-state-middleware {:db ... :log ...})))
    

    【讨论】:

    • 您将使用动态进行大量状态管理。这可能会在以后导致一些严重的问题。
    猜你喜欢
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-30
    • 1970-01-01
    • 2015-01-30
    相关资源
    最近更新 更多