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.

Comment From: christophejan

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. 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.

Comment From: rstoyanchev

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.

Comment From: christophejan

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.

Comment From: rstoyanchev

Indeed there is room for improvement so I will mark the issue accordingly.

Comment From: Mamun-Al-Babu-Shikder

The current behavior of UriComponentsBuilder using MultiValueMap for query parameters introduces limitations when handling repeated query keys. This design flattens parameters by key, breaking the original order and making templates like ?a={p1}&b={p2}&a={p3} expand in an unexpected way (a=x&b=y&a=z instead of a=x&a=z&b=y).

To overcome this, an internal switch from MultiValueMap to a List> (or List>) could preserve the exact sequence of key-value pairs. This change would maintain compatibility while enabling deterministic URI generation, especially for APIs that rely on parameter order or allow repeated keys.

For example, instead of storing: { a: [x, z], b: [y] } We store: [(a, x), (a, z), (b, y)]

Then serialize the query string based on insertion order.

This would make URI expansion more intuitive and compliant with real-world API expectations, and could be avoid breaking existing consumers.

Comment From: SuganthiThomas

Hi, I’m interested in working on this issue. I have experience with Java and Spring Boot. Could I take this up?