Clojure 使用Ring和Compojure使用不同的中间件服务应用程序和api路由

Clojure 使用Ring和Compojure使用不同的中间件服务应用程序和api路由,clojure,ring,compojure,Clojure,Ring,Compojure,我有一个ring+compojure应用程序,我想根据路由是web应用程序的一部分还是api的一部分(基于json)应用不同的中间件 我在stack overflow和其他论坛上找到了这个问题的一些答案,但这些答案似乎比我使用的解决方案更复杂。我想知道我的做法是否有缺陷,以及我的解决方案中可能缺少什么。我正在做的一个非常简化的版本是 (defroutes app-routes (GET "/" [req] dump-req) (route/not-found "Not Fou

我有一个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))))
注意,中间件比我在这里展示的要多

我遇到的唯一“限制”是,由于应用程序路由具有未找到的路由,因此它必须位于最后,否则将在找到api路由之前触发

这似乎比我发现的其他一些解决方案更简单、更灵活,它们要么使用附加的条件中间件,如ring.middleware.conditional,要么在我看来是更复杂的路由定义,其中有附加的defroutes层,并且需要使用任何“*”等定义defroutes


我怀疑我在这里遗漏了一些微妙的东西,虽然我的方法似乎有效,但在某些情况下,它会导致意外行为或结果等。

您是正确的,排序很重要,而且您遗漏了一个微妙之处-您应用于
api路由的中间件将针对所有请求执行

考虑以下代码:

(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会议:

> (require '[ring.mock.request :as mock])
> (app (mock/request :get "/api"))
API Middleware
...
> (app (mock/request :get "/"))
API Middleware
App Middleware
...
Compojure有一个很好的特性和助手,在路由匹配后将中间件应用于路由-


一个更简单的解决方案可能是。。。(我在我的应用程序中使用它,我根据您的示例修改了代码)


谢谢,你击中了我的头,更重要的是,你用包裹路线指针给我指出了正确的方向。用这种方法标记一个警告。使用SiteDefaults配置的环包装默认中间件无法正常工作。问题是它破坏了路由/未找到处理程序。似乎是由于将响应头插入到nil响应中,从而使处理程序似乎已经处理了请求。结果返回代码为200,没有内容。已记录了一个戒指默认值问题。如果您有此选项,您只需创建两个戒指应用程序并在不同的web上下文中挂载它们。使用immutant,您只需创建两个处理程序,对这两个处理程序使用不同的中间件,然后将它们装载到不同的路径中,例如
(web/run app)
(web/run api:path)/api”)
这里的
路由是什么?-明白了:
(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)))))