当我从处理程序访问 reitit 路由信息时避免循环依赖
avoid circular dependency when I access reitit route info from handler
假设我设置了某种路由器,将一些路由映射到类似这样的处理程序...
(ns myapp.user.api
(:require [reitit.core :as r]))
; define handlers here...
(def router
(r/router
[["/user" {:get {:name ::user-get-all
:handler get-all-users}}]
["/user/:id"
{:post {:name ::user-post
:handler user-post}}
{:get {:name ::user-get
:handler user-get}}]]))
然后那些处理程序调用需要访问路由信息的服务...
(ns myapp.user-service
(:require [myapp.user.api :as api]))
; how can I get access to the route properties inside here..?
(defn get-all-users [])
(println (r/route-names api/router)))
当我尝试将路由器从 api 文件导入服务时,我遇到了循环依赖问题,因为 api 需要处理程序,它需要服务,所以服务可以然后不需要 api.
避免这种循环依赖的最佳方法是什么?我可以从服务中查找路由器的值和属性吗?
你在某个地方犯了一个简单的错误。我的例子:
(ns demo.core
(:use tupelo.core)
(:require
[reitit.core :as r]
[schema.core :as s]
))
(defn get-all-users [& args] (println :get-all-users))
(defn user-post [& args] (println :user-post))
(defn user-get [& args] (println :user-get))
; define handlers here...
(def router
(r/router
[
["/dummy" :dummy]
["/user" {:get {:name ::user-get-all
:handler get-all-users}}]
["/user/:id"
{:post {:name ::user-post
:handler user-post}}
{:get {:name ::user-get
:handler user-get}}]
]))
并在此处使用:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[reitit.core :as r]
))
(dotest
(spyx-pretty (r/router-name router))
(spyx-pretty (r/route-names router))
(spyx-pretty (r/routes router))
)
结果:
*************** Running tests ***************
:reloading (demo.core tst.demo.core)
Testing _bootstrap
-----------------------------------
Clojure 1.10.3 Java 15.0.2
-----------------------------------
Testing tst.demo.core
(r/router-name router) =>
:lookup-router
(r/route-names router) =>
[:dummy]
(r/routes router) =>
[["/dummy" {:name :dummy}]
["/user"
{:get
{:name :demo.core/user-get-all,
:handler
#object[demo.core$get_all_users 0x235a3fc "demo.core$get_all_users@235a3fc"]}}]]
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
我使用六种通用方法来避免 clojure 中的循环依赖。它们都有不同的权衡,在某些情况下,一种比另一种更适合。我按从最喜欢到最不喜欢的顺序列出它们。
我在下面分别展示了一个例子。可能还有更多我没有想到的方法,但希望这能给你一些思考问题的方法。
重构代码以将常用引用的变量移除到新的命名空间中,并从两个原始命名空间中获取该命名空间。通常这是最好和最简单的方法。但不能在这里完成,因为根处理程序 var 是一个包含来自其他命名空间的 var 的文字。
在运行时将依赖值传递给函数,以避免必须逐字要求命名空间。
(ns circular.a)
(defn make-handler [routes]
(fn []
(println routes)))
(ns circular.b
(:require [circular.a :as a]))
(def routes
{:handler (a/make-handler routes)})
;; 'run' route to test
((:handler routes))
- 使用 multimethods 提供调度机制,然后从其他名称空间定义您的绑定。
(ns circular.a
(:require [circular.b :as b]))
(defmethod b/handler :my-handler [_]
(println b/routes))
(ns circular.b)
(defmulti handler identity)
(def routes
{:handler #(handler :my-handler)})
(ns circular.core
(:require [circular.b :as b]
;; now we bring in our handlers so as to define our method implementations
[circular.a :as a]))
;; 'run' route to test
((:handler b/routes))
- 使用在运行时解析的 var 文字
(ns circular.a)
(defn handler []
(println (var-get #'circular.b/routes)))
(ns circular.b
(:require [circular.a :as a]))
(def routes
{:handler a/handler})
;; 'run' route to test
((:handler routes))
- 将代码移动到相同的命名空间。
(ns circular.a)
(declare routes)
(defn handler []
(println routes))
(def routes
{:handler handler})
;; 'run' route to test
((:handler routes))
- 使用状态。在运行时将其中一个值存储在原子中。
(ns circular.a
(:require [circular.c :as c]))
(defn handler []
(println @c/routes))
(ns circular.b
(:require [circular.a :as a]
[circular.c :as c]))
(def routes
{:handler a/handler})
(reset! c/routes routes)
((:handler routes))
(ns circular.c)
(defonce routes (atom nil))
假设我设置了某种路由器,将一些路由映射到类似这样的处理程序...
(ns myapp.user.api
(:require [reitit.core :as r]))
; define handlers here...
(def router
(r/router
[["/user" {:get {:name ::user-get-all
:handler get-all-users}}]
["/user/:id"
{:post {:name ::user-post
:handler user-post}}
{:get {:name ::user-get
:handler user-get}}]]))
然后那些处理程序调用需要访问路由信息的服务...
(ns myapp.user-service
(:require [myapp.user.api :as api]))
; how can I get access to the route properties inside here..?
(defn get-all-users [])
(println (r/route-names api/router)))
当我尝试将路由器从 api 文件导入服务时,我遇到了循环依赖问题,因为 api 需要处理程序,它需要服务,所以服务可以然后不需要 api.
避免这种循环依赖的最佳方法是什么?我可以从服务中查找路由器的值和属性吗?
你在某个地方犯了一个简单的错误。我的例子:
(ns demo.core
(:use tupelo.core)
(:require
[reitit.core :as r]
[schema.core :as s]
))
(defn get-all-users [& args] (println :get-all-users))
(defn user-post [& args] (println :user-post))
(defn user-get [& args] (println :user-get))
; define handlers here...
(def router
(r/router
[
["/dummy" :dummy]
["/user" {:get {:name ::user-get-all
:handler get-all-users}}]
["/user/:id"
{:post {:name ::user-post
:handler user-post}}
{:get {:name ::user-get
:handler user-get}}]
]))
并在此处使用:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[reitit.core :as r]
))
(dotest
(spyx-pretty (r/router-name router))
(spyx-pretty (r/route-names router))
(spyx-pretty (r/routes router))
)
结果:
*************** Running tests ***************
:reloading (demo.core tst.demo.core)
Testing _bootstrap
-----------------------------------
Clojure 1.10.3 Java 15.0.2
-----------------------------------
Testing tst.demo.core
(r/router-name router) =>
:lookup-router
(r/route-names router) =>
[:dummy]
(r/routes router) =>
[["/dummy" {:name :dummy}]
["/user"
{:get
{:name :demo.core/user-get-all,
:handler
#object[demo.core$get_all_users 0x235a3fc "demo.core$get_all_users@235a3fc"]}}]]
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
我使用六种通用方法来避免 clojure 中的循环依赖。它们都有不同的权衡,在某些情况下,一种比另一种更适合。我按从最喜欢到最不喜欢的顺序列出它们。
我在下面分别展示了一个例子。可能还有更多我没有想到的方法,但希望这能给你一些思考问题的方法。
重构代码以将常用引用的变量移除到新的命名空间中,并从两个原始命名空间中获取该命名空间。通常这是最好和最简单的方法。但不能在这里完成,因为根处理程序 var 是一个包含来自其他命名空间的 var 的文字。
在运行时将依赖值传递给函数,以避免必须逐字要求命名空间。
(ns circular.a)
(defn make-handler [routes]
(fn []
(println routes)))
(ns circular.b
(:require [circular.a :as a]))
(def routes
{:handler (a/make-handler routes)})
;; 'run' route to test
((:handler routes))
- 使用 multimethods 提供调度机制,然后从其他名称空间定义您的绑定。
(ns circular.a
(:require [circular.b :as b]))
(defmethod b/handler :my-handler [_]
(println b/routes))
(ns circular.b)
(defmulti handler identity)
(def routes
{:handler #(handler :my-handler)})
(ns circular.core
(:require [circular.b :as b]
;; now we bring in our handlers so as to define our method implementations
[circular.a :as a]))
;; 'run' route to test
((:handler b/routes))
- 使用在运行时解析的 var 文字
(ns circular.a)
(defn handler []
(println (var-get #'circular.b/routes)))
(ns circular.b
(:require [circular.a :as a]))
(def routes
{:handler a/handler})
;; 'run' route to test
((:handler routes))
- 将代码移动到相同的命名空间。
(ns circular.a)
(declare routes)
(defn handler []
(println routes))
(def routes
{:handler handler})
;; 'run' route to test
((:handler routes))
- 使用状态。在运行时将其中一个值存储在原子中。
(ns circular.a
(:require [circular.c :as c]))
(defn handler []
(println @c/routes))
(ns circular.b
(:require [circular.a :as a]
[circular.c :as c]))
(def routes
{:handler a/handler})
(reset! c/routes routes)
((:handler routes))
(ns circular.c)
(defonce routes (atom nil))