This is a regression in Spring Framework 6.2:

    @Test
    public void mailToUrlIsExpandedProperly() {
        DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
        String mailToUri = "mailto:{to}?subject={subject}";
        String to = "user@example.com";
        String subject = "Test subject";
        URI uri = uriFactory.expand(mailToUri, Map.of("to", to, "subject", subject));
        assertThat(uri.toString()).isEqualTo("mailto:%s?subject=%s".formatted(to, subject));
    }

The functionality comes from Spring WS with Mail and JMS transports: https://docs.spring.io/spring-ws/docs/current/reference/html/#_jms_transport_2. So, same fails for ulrs like jms:{destination}?deliveryMode={deliveryMode}&priority={priority}.

Comment From: quaff

FYI, the test is failed with 6.1.8 too, because subject contains spaces.

java.lang.IllegalArgumentException: Illegal character in opaque part at index 36: mailto:user@example.com?subject=Test subject
    at java.base/java.net.URI.create(URI.java:906)
    at org.springframework.web.util.DefaultUriBuilderFactory$DefaultUriBuilder.createUri(DefaultUriBuilderFactory.java:436)
    at org.springframework.web.util.DefaultUriBuilderFactory$DefaultUriBuilder.build(DefaultUriBuilderFactory.java:417)
    at org.springframework.web.util.DefaultUriBuilderFactory.expand(DefaultUriBuilderFactory.java:174)
    at com.example.demo.UriBuilderFactoryTest.mailToUrlIsExpandedProperly(UriBuilderFactoryTest.java:19)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.net.URISyntaxException: Illegal character in opaque part at index 36: mailto:user@example.com?subject=Test subject
    at java.base/java.net.URI$Parser.fail(URI.java:2976)
    at java.base/java.net.URI$Parser.checkChars(URI.java:3147)
    at java.base/java.net.URI$Parser.parse(URI.java:3183)
    at java.base/java.net.URI.<init>(URI.java:623)
    at java.base/java.net.URI.create(URI.java:904)
    ... 7 more


Comment From: artembilan

Yeah... I see your point. Missed the part that non-characters have to be encoded. When I changed the test to this, it passes:

    @Test
    public void mailToUrlIsExpandedProperly() {
        DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
        uriFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
        String mailToUri = "mailto:{to}?subject={subject}";
        URI uri = uriFactory.expand(mailToUri, Map.of("to", "user@example.com", "subject", "Test subject"));
        assertThat(uri.toString()).isEqualTo("mailto:user%40example.com?subject=Test%20subject");
    }

However now is the question about a default encoding mode which is:

        /**
         * Pre-encode the URI template first, then strictly encode URI variables
         * when expanded, with the following rules:
         * <ul>
         * <li>For the URI template replace <em>only</em> non-ASCII and illegal
         * (within a given URI component type) characters with escaped octets.
         * <li>For URI variables do the same and also replace characters with
         * reserved meaning.
         * </ul>
         * <p>For most cases, this mode is most likely to give the expected
         * result because in treats URI variables as opaque data to be fully
         * encoded, while {@link #URI_COMPONENT} by comparison is useful only
         * if intentionally expanding URI variables with reserved characters.
         * @since 5.0.8
         * @see UriComponentsBuilder#encode()
         */
        TEMPLATE_AND_VALUES,

Doesn't look like it is taken into account when values are set in the build():

            if (encodingMode.equals(EncodingMode.VALUES_ONLY)) {
                uriVars = UriUtils.encodeUriVariables(uriVars);
            }

Probably separate issue as a back-port to 6.1.x.

Thank you for taking a look!