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:

Java
UriComponents uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel }") 
        .queryParam("q", "{q}") 
        .encode() 
        .build(); 
URI uri = uriComponents.expand("Westin", "123").toUri(); 
  1. Método de fábrica estático con una plantilla de URI.
  2. Agregar o reemplazar componentes de URI.
  3. Solicitud para codificar una plantilla de URI y variables de URI.
  4. Recopilar UriComponents.
  5. Amplíe las variables y obtenga URI.
Kotlin

        val uriComponents = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}") 
        .queryParam(" q", "{q}") 
        .encode() 
        .build() 
            val uri = uriComponents.expand("Westin", "123").toUri() 
  1. Método de fábrica estático con una plantilla de URI.
  2. Agregar o reemplazar componentes de URI.
  3. Solicitud para codificar una plantilla de URI y variables de URI.
  4. Recopilar UriComponents.
  5. 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:

Java

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .encode()
        .buildAndExpand("Westin", "123")
        .toUri();
Kotlin

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:

Java

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123");
Kotlin

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:

Java

URI uri = UriComponentsBuilder
        .fromUriString("https://example.com/hotels/{hotel}?q={q}")
        .build("Westin", "123");
Kotlin

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:

Java

// 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);
Kotlin

// 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:

Java

// 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();
Kotlin

// 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:

Java

String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
        .queryParam("q", "{q}")
        .build("Westin", "123");
Kotlin

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:

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.

Considere ";", que es válido en una ruta, pero tiene un valor reservado. La primera opción reemplaza ";" a "%3B" en las variables de URI, pero no en la plantilla de URI. Por el contrario, la segunda opción nunca reemplaza ";" porque es un carácter válido en la ruta.

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:

Java

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"
Kotlin
 
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:

Java

URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
        .queryParam("q", "{q}")
        .build("New York", "foo+bar");
Kotlin

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:

Java

URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
        .build("New York", "foo+bar");
Kotlin

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:

Java

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();
Kotlin

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: utiliza UriComponentsBuilder# 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 de UriUtils#encodeUriVariables antes de expandirlas a la plantilla.

  • URI_COMPONENT: utiliza UriComponents#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:

Java

HttpServletRequest request = ...
// Reutiliza esquema, host, puerto, ruta y cadena de consulta...
URI uri = ServletUriComponentsBuilder.fromRequest(request)
        .replaceQueryParam("accountId", "{id}")
        .build("123");
Kotlin

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:

Java

HttpServletRequest request = ...
// Reutilizar esquema, host, puerto y ruta de contexto...
URI uri = ServletUriComponentsBuilder.fromContextPath(request)
        .path("/accounts")
        .build()
        .toUri();
Kotlin

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:

Java

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();
Kotlin

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()
A partir de la versión 5.1, 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:

Java

@Controller
@RequestMapping("/hotels/{hotel}")
public class BookingController {
    @GetMapping(" /bookings/{booking}")
    public ModelAndView getBooking(@PathVariable Long booking) {
        // ...
    }
}
Kotlin

@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:

Java

UriComponents uriComponents = MvcUriComponentsBuilder
    .fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
Kotlin

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:

Java

UriComponents uriComponents = MvcUriComponentsBuilder
    .fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
Kotlin

val uriComponents = MvcUriComponentsBuilder
    .fromMethodCall (on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
Las firmas de los métodos del controlador tienen un diseño limitado si están destinadas a crear referencias usando 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:

Java

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();
Kotlin

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()
Desde la versión 5.1, 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:

Java

@RequestMapping("/people/{id}/addresses")
public class PersonAddressController {
    @RequestMapping("/{country}")
    public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... }
}
Kotlin

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