ring-clojure / ring

Clojure HTTP server abstraction
MIT License
3.76k stars 520 forks source link

`wrap-nested-params` does not parse `query-params` to produce a nested structure. #458

Closed mschaef closed 1 year ago

mschaef commented 2 years ago

I am not sure if this is a bug or a deliberate design choice, but wrap-nested-params only returns nested structures on the :params key - not on :query-params (which it leaves in flat, unprocessed state).

eg, given the URL (http://localhost:8080/list/0?modal[id]=update-from), the following are the observed values in the two request keys in question:

15217 [qtp1776502451-20] INFO  toto.view.query - (:params req)
=> {:modal {:id "update-from"}}
15234 [qtp1776502451-20] INFO  toto.view.query - (:query-params req)
=> {"modal[id]" "update-from"}

I'd expect (hope?) that there would be a way to configure middleware such that :query-params map would reflect the nested structure: {:modal {:id "update-from"}}

Understanding that wrap-nested-params cannot be changed to do this without breaking backward compatibility, it would be a useful addition to have at least nest-params (https://github.com/ring-clojure/ring/blob/1.9.0/ring-core/src/ring/middleware/nested_params.clj#L47) be publicly visible so that the same parsing logic used for params can be reused by user code to parse query-params too. Either that, or an alternative middleware that could be added to apply the same parsing logic to :query-params that the current middleware applies to :params.

(The particular scenario here is using query parameters to control the display of a modal within a web page. Nested query parameters makes it considerably easier to group the parameters specific to the modal and remove them all in one shot when the modal is dismissed. So the idea is to look at just the :query-params nesting and build a new URL from that.)

weavejester commented 1 year ago

Apologies for not responding a year ago - this issue slipped through my inbox. The belated answer is that it's a deliberate design choice: the :query-params, :form-params, :multipart-params, etc. keys contain the raw str -> str map of parameters. The :params key is a merged map of all the former, plus any changes to make it easier to use.

I don't quite understand the use-case here. Why would you not just use :params in this situation?

mschaef commented 1 year ago

The main goal is to logically group query parameters. ie: I'm using a modal query parameter to fetch a page with a modal displayed, but the modal dialog needs a couple extra query parameters specific to it. Ideally, I'd like to use hierarchy within the query parameters to identify the fact those three parameters are related. Among other purposes, this makes it easier to compute the URL for the page without the modal query, since all three modal query parameters are in the same group - remove that group and all three are gone in one shot. (Otherwise, you're stuck doing something like modal, modal-param1, modal-param2 and removing all the query parameters matching a prefix.)

The implication of this is that the use case requires grouping, but restricted to just the query parameters. (Since those are the only parameters that belong in an updated computed URL.)

weavejester commented 1 year ago

But wouldn't (-> req :params :model) work just as well in this case? Or or you foresee having a different type of parameter that is also called :model? And if so, why?