【问题标题】:Serving app and api routes with different middleware using Ring and Compojure使用 Ring 和 Compojure 使用不同的中间件为应用程序和 api 路由提供服务
【发布时间】:2015-03-17 00:09:05
【问题描述】:

我有一个 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。

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

【问题讨论】:

标签: clojure ring compojure


【解决方案1】:

您是对的,排序很重要,您缺少一个微妙之处 - 您应用于 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
...

【讨论】:

  • 谢谢,你打动了我的心,更重要的是,你用 wrap-routes 指针为我指明了正确的方向。
  • 只需用这种方法标记一个警告。这不适用于使用 site-defaults 配置的 ring wrap-defaults 中间件。问题是它破坏了路由/未找到处理程序。似乎是由于 wrap-defaults 将响应标头插入到 nil 响应中,从而看起来处理程序已经处理了请求。结果返回代码为 200 且没有内容。已记录有关 ring-defaults 的问题。
  • 如果您可以选择创建两个环应用程序并将它们安装在不同的 Web 环境中。使用 immutant 您可以简单地创建两个处理程序,为两者使用不同的中间件,然后将它们安装在不同的路径中,例如(web/run app)(web/run api :path "/api")
【解决方案2】:

一个更简单的解决方案可能是......(我在我的应用程序中使用它,我将代码改编为您的示例)

(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)))))

【讨论】:

    猜你喜欢
    • 2014-05-11
    • 2014-11-21
    • 2012-12-31
    • 2013-05-25
    • 2011-12-10
    • 1970-01-01
    • 2013-07-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多