Componentes Uri
Spring MVC y Spring WebFlux
UriComponentsBuilder
ayuda a crear URI a
partir de plantillas de URI con variables, como se muestra en el siguiente ejemplo:
UriComponents uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel }")
.queryParam("q", "{q}")
.encode()
.build();
URI uri = uriComponents.expand("Westin", "123").toUri();
- Método de fábrica estático con una plantilla de URI.
- Agregar o reemplazar componentes de URI.
- Solicitud para codificar una plantilla de URI y variables de URI.
- Recopilar
UriComponents
. - Amplíe las variables y obtenga
URI
.
val uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam(" q", "{q}")
.encode()
.build()
val uri = uriComponents.expand("Westin", "123").toUri()
- Método de fábrica estático con una plantilla de URI.
- Agregar o reemplazar componentes de URI.
- Solicitud para codificar una plantilla de URI y variables de URI.
- Recopilar
UriComponents
. - Ampliamos las variables y obtenemos
URI
.
El código del ejemplo anterior se puede combinar en una sola cadena. y acortado usando
buildAndExpand
, como se muestra en el siguiente ejemplo:
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri();
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri()
Tú Podemos acortarlo aún más yendo directamente al URI (lo que implica una codificación), como se muestra en el siguiente ejemplo:
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")
Puedes acortarlo aún más usando una plantilla de URI completa , como se muestra en el siguiente ejemplo:
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123");
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123")
UriBuilder
Spring MVC y Spring WebFlux
UriComponentsBuilder
implementa UriBuilder
. A su vez, puedes crear un
UriBuilder
usando UriBuilderFactory
. Juntos, UriBuilderFactory
y UriBuilder
proporcionan
un mecanismo de complemento para generar URI a partir de plantillas de URI basadas en una configuración común, como
la URL base, las opciones de codificación y otros detalles.
Puede configurar RestTemplate
y
WebClient
usando UriBuilderFactory
para configurar la preparación de URI. DefaultUriBuilderFactory
es una implementación predeterminada de UriBuilderFactory
que utiliza UriComponentsBuilder
internamente y expone opciones de configuración generales.
En el siguiente ejemplo muestra cómo configurar dicho bean:
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
val restTemplate = RestTemplate()
restTemplate.uriTemplateHandler = factory
El siguiente ejemplo configura el WebClient
:
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
val client = WebClient.builder().uriBuilderFactory(factory).build()
Alternativamente, puedes usar DefaultUriBuilderFactory
directamente. Esto es similar al uso de
UriComponentsBuilder
, pero en lugar de métodos de fábrica estáticos, es una instancia real que almacena
la configuración y los parámetros, como se muestra en el siguiente ejemplo:
String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
val baseUrl = "https://example.com"
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)
val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")
Codificación de URI
Spring MVC y Spring WebFlux
UriComponentsBuilder
abre opciones de codificación en dos niveles:
UriComponentsBuilder#encode(): primero codifica previamente el patrón URI y luego codifica estrictamente las variables URI cuando se expande.
UriComponents#encode(): codifica los componentes URI después de la expansión de la variable URI.
Ambas opciones reemplazan ASCII y caracteres ilegales con octetos de escape. Sin embargo, la primera opción también reemplaza caracteres con un significado reservado que aparecen en las variables URI.
En la mayoría de los casos, la primera opción probablemente producirá el resultado esperado porque tiene en cuenta las variables del identificador URI. como datos opacos que deben estar completamente codificados, mientras que la segunda opción es útil si las variables URI contienen intencionalmente caracteres reservados. La segunda opción también funciona si no expandes las variables URI en absoluto, porque entonces cualquier cosa que parezca una variable URI está codificada.
El siguiente ejemplo utiliza la primera opción:
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand ("New York", "foo+bar")
.toUri();
// Result "/hotel%20list/New%20York?q=foo%2Bbar"
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("New York", "foo+bar")
.toUri ()
// Result "/hotel%20list/New%20York?q=foo%2Bbar"
Puedes acortar el código del ejemplo anterior yendo directamente a A URI (lo que implica una codificación), como se muestra en el siguiente ejemplo:
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar");
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar")
Puedes acortarlo aún más utilizando un comodín de URI completo, como se muestra en el siguiente ejemplo:
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar");
val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar")<
WebClient
y RestTemplate
expande y codifica patrones de URI internamente utilizando
la estrategia UriBuilderFactory
. Ambas opciones se pueden configurar usando una estrategia
personalizada, como se muestra en el siguiente ejemplo:
String baseUrl = "https://example.com";
DefaultUriBuilderFactory
factory = new DefaultUriBuilderFactory(baseUrl)
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
// Configurando RestTemplate...
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
// Configurar WebClient...
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
val baseUrl = "https://example.com"
val factory = DefaultUriBuilderFactory(baseUrl).apply {
encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}
// Configurando RestTemplate...
val restTemplate = RestTemplate().apply {
uriTemplateHandler = factory
}
// Configurar WebClient...
val client = WebClient.builder().uriBuilderFactory(factory).build()
La implementación DefaultUriBuilderFactory
utiliza UriComponentsBuilder
internamente para expandir y codificar patrones de URI. Como fábrica, proporciona un lugar único para configurar un
enfoque de codificación basado en uno de los siguientes modos de codificación:
TEMPLATE_AND_VALUES
: utilizaUriComponentsBuilder# encode()
, correspondiente a la primera opción de la lista anterior, para precodificar la plantilla de URI y codificar estrictamente las variables de URI al expandirse.VALUES_ONLY
: no codifica la plantilla de URI y, en su lugar, aplica una codificación estricta a las variables de URI a través deUriUtils#encodeUriVariables
antes de expandirlas a la plantilla.URI_COMPONENT
: utilizaUriComponents#encode()
, correspondiente a la segunda opción de la lista anterior, para codificar el valor del componente del URI después Extensiones de variables URI.NONE
: no se aplica ninguna codificación.
RestTemplate
está establecido en EncodingMode.URI_COMPONENT
por razones históricas
y por compatibilidad con versiones anteriores. WebClient
accede al valor predeterminado en DefaultUriBuilderFactory
,
que se cambió de EncodingMode.URI_COMPONENT
en 5.0.x a EncodingMode.TEMPLATE_AND_VALUES
en 5.1.
Solicitudes relativas a servlet
Puede utilizar ServletUriComponentsBuilder
para
crear URI relativos a la solicitud actual, como se muestra en el siguiente ejemplo:
HttpServletRequest request = ...
// Reutiliza esquema, host, puerto, ruta y cadena de consulta...
URI uri = ServletUriComponentsBuilder.fromRequest(request)
.replaceQueryParam("accountId", "{id}")
.build("123");
val request: HttpServletRequest = ...
// Reutiliza esquema, host, puerto, ruta y cadena de consulta
val uri = ServletUriComponentsBuilder.fromRequest(request)
.replaceQueryParam("accountId", "{id}")
.build ("123")
Puede crear URI relativos a la ruta de contexto, como se muestra en el siguiente ejemplo:
HttpServletRequest request = ...
// Reutilizar esquema, host, puerto y ruta de contexto...
URI uri = ServletUriComponentsBuilder.fromContextPath(request)
.path("/accounts")
.build()
.toUri();
val request: HttpServletRequest = ...
// Reutilizar el esquema, host, puerto y ruta de contexto...
val uri = ServletUriComponentsBuilder.fromContextPath(request)
.path("/accounts")
.build()
.toUri()
Puede crear URI relativos al servlet (por ejemplo, /main/*
), como se muestra en el siguiente
ejemplo:
HttpServletRequest request = ...
// Reutiliza el esquema, host, puerto, ruta de contexto y prefijo de mapeo de servlet...
URI uri = ServletUriComponentsBuilder.fromServletMapping(request)
.path("/accounts")
.build()
.toUri();
val request: HttpServletRequest = ...
// Reutilizaciones esquema, host, puerto, ruta de contexto y prefijo de mapeo de servlet...
val uri = ServletUriComponentsBuilder.fromServletMapping(request)
.path("/accounts")
.build()
.toUri()
ServletUriComponentsBuilder
ignora la información de Reenviado
y X-Forwarded-*
headers, que indican la dirección
del lado del cliente. Considere usar ForwardedHeaderFilter
para recuperar y usar o descartar dichos
encabezados.
Referencias de controlador
Spring MVC proporciona un mecanismo para preparar referencias a métodos de controlador. Por ejemplo, el siguiente controlador MVC le permite crear enlaces:
@Controller
@RequestMapping("/hotels/{hotel}")
public class BookingController {
@GetMapping(" /bookings/{booking}")
public ModelAndView getBooking(@PathVariable Long booking) {
// ...
}
}
@Controller
@RequestMapping("/hotels/{hotel}")
class BookingController {
@GetMapping("/bookings/{booking}")
fun getBooking(@PathVariable booking: Long): ModelAndView {
// ...
}
}
Puedes preparar una referencia accediendo al método por su nombre, como se muestra en el siguiente ejemplo:
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
val uriComponents = MvcUriComponentsBuilder
.fromMethodName (BookingController::class.java, "getBooking", 21).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
En el ejemplo anterior, especificamos los valores reales de los argumentos del método (en este caso, el valor
largo: 21
) que se usarán como una variable de ruta y se insertarán en la URL. Además, especificamos el
valor 42
para completar las variables de URI restantes, como la variable hotel
heredada de
la asignación de consulta a nivel de tipo. Si el método tuviera más argumentos, podríamos proporcionar nulo para los
argumentos que no sean necesarios para la URL. En general, sólo los argumentos con las anotaciones @PathVariable
y @RequestParam
son significativos para la construcción de URL.
Hay formas adicionales de
utilizar MvcUriComponentsBuilder
. Por ejemplo, para evitar llamar a un método de controlador por su
nombre, como se muestra en el siguiente ejemplo (el ejemplo asume una importación estática de MvcUriComponentsBuilder.on
),
puede usar una técnica similar a la prueba usando objetos simulados mediante un proxy:
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
val uriComponents = MvcUriComponentsBuilder
.fromMethodCall (on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
fromMethodCall
. Además de la necesidad de una firma de parámetro correcta, existe una limitación
técnica en el tipo de devolución (es decir, la creación de un proxy en tiempo de ejecución para las llamadas del vinculador), por lo que el tipo de devolución no debe ser
final
. En particular, el tipo de retorno normal
String
para nombres de vistas no funciona aquí. En su lugar, debería utilizar
ModelAndView
o incluso un simple Object
(con un valor de retorno String
).
Los ejemplos anteriores utilice métodos estáticos en MvcUriComponentsBuilder
. Internamente, llaman al
ServletUriComponentsBuilder
para preparar una URL base a partir del esquema, host, puerto, ruta de
contexto y ruta del servlet de la solicitud actual. Esto funciona muy bien en la mayoría de los casos. Sin embargo,
a veces esto puede no ser suficiente. Por ejemplo, es posible que esté fuera del contexto de la solicitud (por
ejemplo, en un proceso por lotes que prepara enlaces) o que necesite insertar un prefijo de ruta (por ejemplo, un
prefijo local que se eliminó de la ruta de la solicitud y necesita para insertarse nuevamente en los enlaces).
Para tales casos, puede usar sobrecargas estáticas de métodos fromXxx
que toman un UriComponentsBuilder
para usar la base URL. Alternativamente, puede crear una instancia de MvcUriComponentsBuilder
con una
URL base y luego usar métodos withXxx
basados en la instancia. Por ejemplo, el siguiente listado usa
withMethodCall
:
UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en");
MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base);
builder.withMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
val base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en")
val builder = MvcUriComponentsBuilder.relativeTo(base)
builder.withMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
MvcUriComponentsBuilder
ignora información
de los encabezados Fwarded
y X-Forwarded-*
, que indican la dirección del lado del cliente.
Considere usar un ForwardedHeaderFilter para recuperar y usar o descartar dichos encabezados.
Referencias en vistas
En vistas como Thymeleaf, FreeMarker o JSP, puede formar referencias a controladores anotados haciendo referencia un nombre asignado implícita o explícitamente para cada asignación de consulta.
Considere el siguiente ejemplo:
@RequestMapping("/people/{id}/addresses")
public class PersonAddressController {
@RequestMapping("/{country}")
public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... }
}
@RequestMapping("/people/{id}/addresses")
class PersonAddressController {
@RequestMapping("/{country}")
fun getAddress(@PathVariable country: String): HttpEntity<PersonAddress> { ... }
}
Dado el controlador anterior, puede preparar un enlace desde JSP de la siguiente forma:
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
...
<a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">Get Address</a>
En el ejemplo anterior, nos basamos en la función mvcUrl
declarada en la biblioteca de
etiquetas Spring (es decir, META-INF/spring. tld), pero definir su propia función o preparar una similar para otras
tecnologías de plantillas también es bastante sencillo.
Así es como funciona. Cuando ejecuta cada anotación
@RequestMapping
, se le asigna un nombre predeterminado a través de la estrategia HandlerMethodMappingNamingStrategy
,
cuya implementación predeterminada escribe en mayúscula el nombre de la clase y del método (por ejemplo, método
getThing
en ThingController
se convierte en "TC#getThing"). Si los nombres no
coinciden, puede utilizar @RequestMapping(name="..")
para asignar un nombre explícito o implementar su
propia estrategia de nomenclatura HandlerMethodMappingNamingStrategy
.