使用 Ring 和 Compojure 使用不同的中间件服务应用程序和 api 路由
Serving app and api routes with different middleware using Ring and Compojure
我有一个 ring+compojure 应用程序,我想根据路由是 Web 应用程序的一部分还是 api(基于 json)的一部分来应用不同的中间件。
我在 stack overflow 和其他论坛上找到了这个问题的一些答案,但这些答案似乎比我一直使用的解决方案更复杂。我想知道我的做法是否有缺点,以及我的解决方案中可能缺少什么。我正在做的一个非常简化的版本是
(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))))
请注意,中间件比我在这里展示的要多。
我遇到的唯一'restriction'是因为app-routes有未找到的路由,所以它需要排在最后,否则会在找到api路由之前触发。
这似乎比我发现的其他一些解决方案更简单、更灵活,这些解决方案似乎要么使用额外的条件中间件,例如 ring.middleware.conditional,要么在我看来是更复杂的路由定义是一个额外的 defroutes 层,需要用任何“*”等定义 defroutes。
我怀疑我在这里遗漏了一些微妙的东西,虽然我的方法似乎有效,但在某些情况下它会导致意外行为或结果等。
你是对的,顺序很重要,你遗漏了一个微妙之处 - 你应用于 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))))
并回复会话:
> (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
...
一个更简单的解决方案可能是...(我在我的应用程序中使用它,我根据您的示例调整了代码)
(defn make-api-handler
[]
(-> api-routes
(wrap-defaults api-defaults)))
(defn make-app-handler
[]
(-> app-routes
(wrap-defaults site-defaults)))
(def app
(let [api-handler-fn (make-api-handler)
app-handler-fn (make-app-handler)]
(fn [request]
(if (clojure.string/starts-with? (:uri request) "/api")
(api-handler-fn request)
(app-handler-fn request)))))
我有一个 ring+compojure 应用程序,我想根据路由是 Web 应用程序的一部分还是 api(基于 json)的一部分来应用不同的中间件。
我在 stack overflow 和其他论坛上找到了这个问题的一些答案,但这些答案似乎比我一直使用的解决方案更复杂。我想知道我的做法是否有缺点,以及我的解决方案中可能缺少什么。我正在做的一个非常简化的版本是
(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))))
请注意,中间件比我在这里展示的要多。
我遇到的唯一'restriction'是因为app-routes有未找到的路由,所以它需要排在最后,否则会在找到api路由之前触发。
这似乎比我发现的其他一些解决方案更简单、更灵活,这些解决方案似乎要么使用额外的条件中间件,例如 ring.middleware.conditional,要么在我看来是更复杂的路由定义是一个额外的 defroutes 层,需要用任何“*”等定义 defroutes。
我怀疑我在这里遗漏了一些微妙的东西,虽然我的方法似乎有效,但在某些情况下它会导致意外行为或结果等。
你是对的,顺序很重要,你遗漏了一个微妙之处 - 你应用于 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))))
并回复会话:
> (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
...
一个更简单的解决方案可能是...(我在我的应用程序中使用它,我根据您的示例调整了代码)
(defn make-api-handler
[]
(-> api-routes
(wrap-defaults api-defaults)))
(defn make-app-handler
[]
(-> app-routes
(wrap-defaults site-defaults)))
(def app
(let [api-handler-fn (make-api-handler)
app-handler-fn (make-app-handler)]
(fn [request]
(if (clojure.string/starts-with? (:uri request) "/api")
(api-handler-fn request)
(app-handler-fn request)))))