1
votes

I'm unable to access form parameters from a POST request. I've tried every combination of middleware and config options I've seen in the docs, on SO, etc. (including the deprecated compojure/handler options) and I'm still unable to see the parameters. I'm sure I'm missing something very obvious, so any suggestions (no matter how slight) would be greatly appreciated.

Here's my latest attempt, wherein I try to use the site-defaults middleware and disable the anti-forgery/CSRF protection provided by default. (I know this is a bad idea.) However, when I try to view the page in question in a web browser, the browser tries to download the page, as if it were a file it wasn't capable of rendering. (Interestingly, the page is rendered as expected when using Curl.)

Here's the latest attempt:

(defroutes config-routes*
  (POST "/config" request post-config-handler))

(def config-routes
  (-> #'config-routes*
    (basic-authentication/wrap-basic-authentication authenticated?)
    (middleware-defaults/wrap-defaults (assoc middleware-defaults/site-defaults :security {:anti-forgery false}))))

Previous attempt:

(def config-routes
  (-> #'config-routes*
    (basic-authentication/wrap-basic-authentication authenticated?)
    middleware-params/wrap-params))

UPDATE: The parameters appear to be swallowed by the outer defroutes:

(defroutes app-routes
  (ANY "*" [] api-routes)
  (ANY "*" [] config-routes)
  (route/not-found "Not Found"))

So, my question now becomes: How can I thread the parameters through to the nested defroutes?

My temporary solve is based on this solution, but Steffen Frank's is much simpler. I will try that and follow-up.

UPDATE 2:

In trying to implement the suggestions provided by both of the current answers, I'm running into a new issue: route matches are overeager. e.g. given the following, POSTs to /something fail with a 401 response because of the wrap-basic-authentication middleware in config-routes.

(defroutes api-routes*
   (POST "/something" request post-somethings-handler))

(def api-routes
  (-> #'api-routes*
    (middleware-defaults/wrap-defaults middleware-defaults/api-defaults)
    middleware-json/wrap-json-params
    middleware-json/wrap-json-response))

(defroutes config-routes*
  (GET "/config" request get-config-handler)
  (POST "/config" request post-config-handler))

(def config-routes
  (-> #'config-routes*
    (basic-authentication/wrap-basic-authentication authenticated?)
    middleware-params/wrap-params))

(defroutes app-routes
  config-routes
  api-routes
  (route/not-found "Not Found"))

(def app app-routes)
3

3 Answers

2
votes

The issue is that when you define your routes in this way:

(defroutes app-routes
  (ANY "*" [] api-routes)
  (ANY "*" [] config-routes)
  (route/not-found "Not Found"))

then any request will be matched by api-routes as long as it returns non-nil response. Thus api-routes does not swallow your request params but rather stealing the whole request.

Instead you should define your app-routes as (preferred solution):

(defroutes app-routes
  api-routes
  config-routes
  (route/not-found "Not Found"))

or make sure that your api-routes returns nil for unmatched URL path (e.g. it shouldn't have not-found route defined).

1
votes

Just a guess, but have you tried this:

(defroutes app-routes
   api-routes
   config-routes
   (route/not-found "Not Found"))
0
votes

You may find the following post useful. It talks about mixing api and app routes such that they don't interfere with each other and you avoid adding middleware from one to the toher etc. Serving app and api routes with different middleware using Ring and Compojure