mvc form example docs spring resttemplate

example - ¿Cuál es la forma correcta de evitar las variables de URL con Spring''s RestTemplate cuando se llama a Spring RestController?



spring mvc example (5)

Al llamar a RestTemplate.exchange para realizar una solicitud de obtención, como:

String foo = "fo+o"; String bar = "ba r"; restTemplate.exchange("http://example.com/?foo={foo}&bar={bar}", HttpMethod.GET, null, foo, bar)

¿cuál es el adecuado para que las variables de URL se hayan escapado correctamente para la solicitud de obtención?

Específicamente, ¿cómo puedo obtener ventajas ( + ) escapadas correctamente porque Spring interpreta como espacios , por lo tanto, necesito codificarlos?

Intenté usar UriComponentsBuilder esta manera:

String foo = "fo+o"; String bar = "ba r"; UriComponentsBuilder ucb = UriComponentsBuilder.fromUriString("http://example.com/?foo={foo}&bar={bar}"); System.out.println(ucb.build().expand(foo, bar).toUri()); System.out.println(ucb.build().expand(foo, bar).toString()); System.out.println(ucb.build().expand(foo, bar).toUriString()); System.out.println(ucb.build().expand(foo, bar).encode().toUri()); System.out.println(ucb.build().expand(foo, bar).encode().toString()); System.out.println(ucb.build().expand(foo, bar).encode().toUriString()); System.out.println(ucb.buildAndExpand(foo, bar).toUri()); System.out.println(ucb.buildAndExpand(foo, bar).toString()); System.out.println(ucb.buildAndExpand(foo, bar).toUriString()); System.out.println(ucb.buildAndExpand(foo, bar).encode().toUri()); System.out.println(ucb.buildAndExpand(foo, bar).encode().toString()); System.out.println(ucb.buildAndExpand(foo, bar).encode().toUriString());

y que imprimió:

http://example.com/?foo=fo+o&bar=ba%20r http://example.com/?foo=fo+o&bar=ba r http://example.com/?foo=fo+o&bar=ba r http://example.com/?foo=fo+o&bar=ba%20r http://example.com/?foo=fo+o&bar=ba%20r http://example.com/?foo=fo+o&bar=ba%20r http://example.com/?foo=fo+o&bar=ba%20r http://example.com/?foo=fo+o&bar=ba r http://example.com/?foo=fo+o&bar=ba r http://example.com/?foo=fo+o&bar=ba%20r http://example.com/?foo=fo+o&bar=ba%20r http://example.com/?foo=fo+o&bar=ba%20r

El espacio se ha escapado correctamente en algunos casos, pero la ventaja nunca se escapa.

También probé UriTemplate esta manera:

String foo = "fo+o"; String bar = "ba r"; UriTemplate uriTemplate = new UriTemplate("http://example.com/?foo={foo}&bar={bar}"); Map<String, String> vars = new HashMap<>(); vars.put("foo", foo); vars.put("bar", bar); URI uri = uriTemplate.expand(vars); System.out.println(uri);

con el mismo resultado exacto:

http://example.com/?foo=fo+o&bar=ba%20r


Aparentemente, la forma correcta de hacerlo es definiendo una fábrica y cambiando el modo de codificación:

String foo = "fo+o"; String bar = "ba r"; DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY); URI uri = factory.uriString("http://example.com/?foo={foo}&bar={bar}").build(foo, bar); System.out.println(uri);

Eso imprime:

http://example.com/?foo=fo%2Bo&bar=ba%20r

Esto se documenta aquí: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#web-uri-encoding


Creo que su problema aquí es que el RFC 3986 , en el que se UriComponents y, por extensión, UriTemplate , no obliga a escapar de + en una cadena de consulta.

La opinión de la especificación sobre esto es simplemente:

sub-delims = "!" / "$" / "&" / "''" / "(" / ")" / "*" / "+" / "," / ";" / "=" pchar = unreserved / pct-encoded / sub-delims / ":" / "@" query = *( pchar / "/" / "?" ) URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]

Si su marco web (Spring MVC, por ejemplo!) Está interpretando + como un espacio, entonces esa es su decisión y no se requiere según las especificaciones de URI.

Con referencia a lo anterior, también verá que !$''()*+,; UriTemplate no escapa. = y & se escapan porque Spring ha tomado una vista "opinada" de cómo se ve una cadena de consulta: una secuencia de pares clave = valor.

Del mismo modo, #[] y los espacios en blanco se escapan porque son ilegales en una cadena de consulta bajo la especificación.

Concedido, es probable que nada de esto le sirva de consuelo si simplemente desea que se escapen los parámetros de consulta.

Para codificar realmente los parámetros de consulta para que su marco web pueda tolerarlos, puede usar algo como org.springframework.web.util.UriUtils.encode(foo, charset) .


Estoy empezando a creer que esto es un error y lo informé aquí: https://jira.spring.io/browse/SPR-16860

Actualmente, mi solución es la siguiente:

String foo = "fo+o"; String bar = "ba r"; String uri = UriComponentsBuilder. fromUriString("http://example.com/?foo={foo}&bar={bar}"). buildAndExpand(vars).toUriString(); uri = uri.replace("+", "%2B"); // This is the horrible hack. try { return new URI(uriString); } catch (URISyntaxException e) { throw new RuntimeException("UriComponentsBuilder generated an invalid URI.", e); }

que es un hack horrible que podría fallar en algunas situaciones.


Para este, prefiero que la codificación se resuelva utilizando un método adecuado en lugar de usar un Hack como lo hiciste tú. Solo usaría algo como abajo

String foo = "fo+o"; String bar = "ba r"; MyUriComponentsBuilder ucb = MyUriComponentsBuilder.fromUriString("http://example.com/?foo={foo}&bar={bar}"); UriComponents uriString = ucb.buildAndExpand(foo, bar); // http://example.com/?foo=fo%252Bo&bar=ba+r URI x = uriString.toUri(); // http://example.com/?foo=fo%2Bo&bar=ba+r String y = uriString.toUriString(); // http://example.com/?foo=fo%2Bo&bar=ba+r String z = uriString.toString();

Y por supuesto la clase es como abajo.

class MyUriComponentsBuilder extends UriComponentsBuilder { protected UriComponentsBuilder originalBuilder; public MyUriComponentsBuilder(UriComponentsBuilder builder) { // TODO Auto-generated constructor stub originalBuilder = builder; } public static MyUriComponentsBuilder fromUriString(String uri) { return new MyUriComponentsBuilder(UriComponentsBuilder.fromUriString(uri)); } @Override public UriComponents buildAndExpand(Object... values) { // TODO Auto-generated method stub for (int i = 0; i< values.length; i ++) { try { values[i] = URLEncoder.encode((String) values[i], StandardCharsets.UTF_8.toString()); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return originalBuilder.buildAndExpand(values); } }

Aún no es la forma más limpia posible, pero es mejor que hacer un enfoque de reemplazo codificado


Podría usar UriComponentsBuilder en Spring (org.springframework.web.util.UriComponentsBuilder)

String url = UriComponentsBuilder .fromUriString("http://example.com/") .queryParam("foo", "fo+o") .queryParam("bar", "ba r") .build().toUriString(); restTemplate.exchange(url , HttpMethod.GET, httpEntity);