将状态作为参数传递给环处理程序?
Posted
技术标签:
【中文标题】将状态作为参数传递给环处理程序?【英文标题】: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 相同的生命周期 【参考方案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 ...)))
【讨论】:
您将使用动态进行大量状态管理。这可能会在以后导致一些严重的问题。以上是关于将状态作为参数传递给环处理程序?的主要内容,如果未能解决你的问题,请参考以下文章