Skip to content

Unexpected query parameter order with UriComponentsBuilder #34788

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
christophejan opened this issue Apr 21, 2025 · 3 comments
Open

Unexpected query parameter order with UriComponentsBuilder #34788

christophejan opened this issue Apr 21, 2025 · 3 comments
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: waiting-for-triage An issue we've not yet triaged or decided on

Comments

@christophejan
Copy link
Contributor

With spring-web 6.2.5, I get the following behavior :

System.out.println(UriComponentsBuilder.fromUriString(“http://myhost?a={p1}&b={p2}&a={p3}”).buildAndExpand(“a1”, “b1”, “a2”));

Output :

http://myhost?a=a1&a=b1&b=a2

I was expecting the following output :

http://myhost?a=a1&b=b1&a=a2

It seems that query params are reordered.

Even if it’s not a good idea to rely on query parameter order, it seems to be legal (see stackoverflow > Is it legal or safe to depend on the ordering of URL query parameters?)… So I think that things like RestClient may require proper query parameter order handling.

Furthermore, query params reordering seems to happen before uri template variables are expanded so values get swapped when given from an array (ie not from map) as the order of variables is then significant.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 21, 2025
@sbrannen sbrannen added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Apr 22, 2025
@christophejan
Copy link
Contributor Author

If my understanding is correct, URI RFC doesn’t specify query parameters. In current WHATWG HTLM standard, 4.10.21.7 URL-encoded form data just point to WHATWG URL standard 5 application/x-www-form-urlencoded that state :

The application/x-www-form-urlencoded format provides a way to encode a list of tuples, each consisting of a name and a value.

This section also details in depth the related parsing and serializing.

I think that something based on a List of name value tuple could be more appropriate as internal model of query parameter in UriComponents than the current MultiValueMap<String,String>. I think UriComponents could then handle more use case and be closer to the specification. It should also fix the issues initially described.

I think a such change may be also the opportunity to potentially design a class for such list of name value tuple with all the helping methods (get the value for the first tuple of name, from / to MultiValueMap conversion etc.) to be possibly used elsewhere. It could be class dedicated to query parameters like HttpHeaders does or a more generic one.

It is, of course, possible I just misunderstood something.

@rstoyanchev
Copy link
Contributor

We generally preserve the order of query parameters and their values. The only exception is when multiple values for the same query parameter are not specified together (and have other query parameters in between). This is due to the use of a MultiValueMap for internal storage that groups by query parameter name.

We do support the WhatWG URL living standard as of the parsing algorithms, but that's just the parsing side.

Arguably, we could do better with this, but there are options for you to eliminate ambiguity if this is a URI template that's in your control. Either declare query parameter values together, or use the buildAndExpand variant with a Map, or initialize the query parameters in a MultiValueMap.

@christophejan
Copy link
Contributor Author

There is indeed workarounds but the current behavior is :

  • error prone (require some knowledge of the implementation storage)
  • limiting (some api may require to not follow the current params grouping)

Leaving the MultiValueMap storage for a list of tuple would solve that and be closer to the specification.

I think that it could be done without breaking the backward compatibility and without introducing any drawbacks.

UriComponent is widely used so please consider it may worth this effort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: waiting-for-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

No branches or pull requests

4 participants