It's important to first understand that some middleware is side-effectful, because it consumes the request body, and the request body is an input stream. In your code you apply wrap-params twice; once via site-defaults, and once via api-defaults. The site-defaults wrap-params will consume the body, leaving nothing left for api-defaults.
So the first change is to ensure that all common middleware is factored out. Fortunately, site-defaults is a superset of api-defaults, so we can write:
(def app
(-> (routes (wrap-defaults app-routes site-defaults) api-routes)
(wrap-defaults api-defaults)))
The next problem we have is how to deal with wrap-anti-forgery. If this detects a POST without the anti-forgery-token, it will return an access error, but crucially it checks before the routes are matched, because middleware have no concept of route matching. This means it will return an access error even if the route is not in app-routes.
The simplest way around this is to put api-routes first:
(def app
(-> (routes api-routes (wrap-defaults app-routes site-defaults))
(wrap-defaults api-defaults)))
Another way is to use the compojure.core/wrap-routes function, which applies middleware after the route has been matched, but before its body has been processed. For example:
(-> app-routes
(wrap-routes wrap-anti-forgery)
(wrap-defaults (dissoc-in site-defaults [:security :anti-forgery]))
- James