Подтвердить что ты не робот

Обслуживание приложений и маршрутов api с различным промежуточным программным обеспечением с использованием Ring и Compojure

У меня есть приложение ring + compojure, и я хочу применить другое промежуточное программное обеспечение в зависимости от того, является ли маршрут частью веб-приложения или частью 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))))

Обратите внимание, что существует больше промежуточного программного обеспечения, чем показано здесь.

Единственное "ограничение", с которым я столкнулся, заключается в том, что, поскольку в маршрутах приложения есть маршрут, не найденный, он должен быть последним, или он будет запущен, прежде чем найти маршруты api.

Это кажется более простым и гибким, чем некоторые другие решения, которые я нашел, которые, как представляется, используют дополнительное условное промежуточное программное обеспечение, такое как ring.middleware.conditional или то, что мне кажется более сложным определением маршрутизации, где есть дополнительный слой defroutes и необходимость определять defroutes с ЛЮБОЙ "*" и т.д.

Я подозреваю, что есть что-то тонкое, которого я здесь не вижу, и, хотя мой подход, похоже, работает, это приведет к неожиданному поведению или результатам в некоторых ситуациях и т.д.

4b9b3361

Ответ 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
...

Ответ 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)))))