我有一个ring + compojure应用程序,我想应用不同的中间件,具体取决于路由是Web应用程序的一部分还是api的一部分(基于json)。
我在堆栈溢出和其他论坛上找到了这个问题的一些答案,但这些答案似乎比我一直使用的解决方案更复杂。我想知道我是如何做的,以及我在解决方案中可能缺少的东西是否存在缺陷。我正在做的一个非常简化的版本是
(defroutes app-routes
(GET "/" [req] dump-req)
(route/not-found "Not Found"))
(defroutes api-routes
(GET "/api" [req] dump-req))
(def app
(routes (-> api-routes
(wrap-defaults api-defaults))
(-> app-routes
(wrap-defaults site-defaults))))
请注意,中间件比我在此处显示的更多。
我遇到的唯一“限制”是,由于app-routes有未找到的路由,它需要到达最后或者在找到api路由之前触发。
这比我发现的其他一些解决方案看起来更简单,更灵活,它们似乎要么使用额外的条件中间件,例如ring.middleware.conditional,或者在我看来是更复杂的路由定义,其中有一个额外的defroutes层和需要用任何“*”等来定义defroutes。
我怀疑我在这里缺少一些微妙的东西,虽然我的方法似乎有效,但在某些情况下会导致意外行为或结果等。
答案 0 :(得分:18)
您是对的,订单很重要并且您遗漏了一些细微之处 - 您对api-routes
应用的中间件将针对所有请求执行。
考虑以下代码:
(defn wrap-app-middleware
[handler]
(fn [req]
(println "App Middleware")
(handler req)))
(defn wrap-api-middleware
[handler]
(fn [req]
(println "API Middleware")
(handler req)))
(defroutes app-routes
(GET "/" _ "App")
(route/not-found "Not Found"))
(defroutes api-routes
(GET "/api" _ "API"))
(def app
(routes (-> api-routes
(wrap-api-middleware))
(-> app-routes
(wrap-app-middleware))))
和repl session:
> (require '[ring.mock.request :as mock])
> (app (mock/request :get "/api"))
API Middleware
...
> (app (mock/request :get "/"))
API Middleware
App Middleware
...
Compojure有一个很好的功能和帮助器,可以在匹配后将路由应用到中间件 - wrap-routes
(def app
(routes (-> api-routes
(wrap-routes wrap-api-middleware))
(-> app-routes
(wrap-routes wrap-app-middleware))
(route/not-found "Not Found")))
> (app (mock/request :get "/api"))
API Middleware
...
> (app (mock/request :get "/"))
App Middleware
...