Primavera WebFlux proporciona un modelo de programación basado en anotaciones, donde los componentes @Controller y @RestController usan anotaciones para expresar asignaciones de consultas, entrada de consultas, manejo de excepciones y más. Los controladores anotados tienen firmas de métodos flexibles y no necesariamente necesitan extender clases base o implementar interfaces específicas.

La siguiente lista muestra un ejemplo básico:

Java

@RestController
public classHelloController {
@GetMapping("/hello") 
public String handle (){
return "Hello WebFlux";
}
}
Kotlin

@RestController
class HelloController {
@GetMapping("/hello")
fun handle() = "Hello WebFlux"
}

En el ejemplo anterior, el método devuelve una String a escriba en la respuesta.

@Controller

Puede definir beans controladores utilizando la definición estándar de Spring Bean. El estereotipo @Controller permite el descubrimiento automático y es consistente con el soporte general de Spring para descubrir clases con la anotación @Component en el classpath y registrar automáticamente definiciones de beans para ellas. También actúa como un estereotipo para la clase anotada, indicando su función como componente web.

Para habilitar la detección automática de dichos beans con la anotación @Controller, puede agregar el componente escaneo a la configuración Java, como se muestra en el siguiente ejemplo:

Java

@Configuration
@ComponentScan("org.example.web") 
public class WebConfig {
// ...
}
Kotlin

@Configuration
 @ComponentScan("org.example.web") 
class WebConfig {
// ...
}

@RestController es una anotación compuesta que a su vez está metaanotada con las anotaciones @Controller y @ResponseBody, lo que indica un controlador cuyo cada método hereda @ResponseBody anotación a nivel de tipo y, por lo tanto, escribe directamente en el cuerpo de la respuesta en lugar de reconocer la representación y renderizar usando una plantilla HTML.

Solicitar mapeo

El La anotación @RequestMapping se utiliza para asignar solicitudes a métodos de controlador. Tiene varios atributos para hacer coincidir por URL, método HTTP, parámetros de solicitud, encabezados y tipos de medios. Puede usarlo a nivel de clase para expresar asignaciones generales o a nivel de método para limitarlo a una asignación de punto final específica.

También hay opciones específicas del método HTTP para acortar el anotación @RequestMapping:

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

Las anotaciones anteriores son anotaciones personalizadas que se especifican porque la mayoría de los métodos del controlador Es probable que deba asignarse a un método HTTP en lugar de utilizar la anotación @RequestMapping, que está asignada a todos los métodos HTTP de forma predeterminada. Sin embargo, la anotación @RequestMapping todavía es necesaria a nivel de clase para expresar asignaciones generales.

El siguiente ejemplo utiliza asignaciones a nivel de tipo y método:

Java
@RestController
@RequestMapping("/persons") 
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id)  {
// ...
}
@PostMapping
@ResponseStatus( HttpStatus.CREATED) 
public void add(@RequestBody Person person) {
// ...
}
}
Kotlin
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}") 
fun getPerson(@PathVariable id: Long): Person {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun add(@RequestBody person: Person) {
 // ...
}
}

Plantillas URI

Puede mostrar consultas utilizando máscaras estándar (también conocidas como patrón de búsqueda (patrones globales ) y comodines:

Plantilla Descripción Ejemplo

?

Coincide con un solo carácter

"/pages/t?st.html" corresponde a "/pages/test.html" y "/pages/t3st.html"

*

Coincide con cero o más caracteres en el segmento de ruta

"/resources/*.png" corresponde a "/resources/file.png"

"/projects/*/versions" coincide con "/projects/spring/versions" pero no coincide con "/projects/spring/boot/versions"

**

Coincide con cero o más rutas segmentos hasta el final de la ruta

"/resources/**" coincide con "/resources/file.png" y "/resources/images/file.png"

"/resources/**/file.png" no es válido, ya que ** solo se permite al final de la ruta.

{name}

Coincide con un segmento de ruta y lo escribe en una variable llamada "nombre".

"/projects/{project }/versions" coincide con "/projects/spring/versions" y captura project=spring

{name:[a-z]+}

Coincide con la expresión "[a-z]+ " como una variable de ruta denominada "nombre".

"/projects/{project:[a-z]+}/versions" coincide con "/projects/spring/versions" pero no "/projects/spring1/versions"

{*path}

Coincide con cero o más segmentos de ruta con el final de la ruta y los escribe en una variable denominada "ruta".

"/resources/{*file}" corresponde a "/resources/images/file.png" y captura file=/images/file.png

Se puede acceder a las variables URI capturadas mediante una anotación @PathVariable, como se muestra en el siguiente ejemplo:

Java

@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
Kotlin

@GetMapping("/owners/{ownerId}/pets/{petId}") 
fun findPet(@PathVariable ownerId: Long , @PathVariable petId: Long): Pet {
// ... 

Puede declarar variables URI a nivel de clase y método, como se muestra en la siguiente ejemplo:

Java

@Controller
@RequestMapping("/owners/{ownerId}") 
public class OwnerController {
@GetMapping("/pets/{petId}")  
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
 // ...
}
}
  1. Mostrar un URI a nivel de clase.
  2. Mostrar URI a nivel de método.
Kotlin

@Controller 
@RequestMapping("/owners/{ownerId}") 
class OwnerController {
@GetMapping("/pets/{petId}")   
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
// ...
}
}
  1. Pantalla URI a nivel de clase.
  2. Asignación de URI a nivel de método.

Las variables URI se convierten al tipo apropiado; de lo contrario, se Se produce un error TypeMismatchException . Los tipos simples (int, long, Date, etc.) son compatibles de forma predeterminada, pero puede registrar la compatibilidad con cualquier otro tipo de datos.

Puede nombrar explícitamente variables URI (por ejemplo, @PathVariable("customId")), pero también puede omitir esta información si los nombres son los mismos y su código se compila. usando información de depuración o con el indicador del compilador -parameters en Java 8.

La sintaxis {*varName} declara una variable URI que coincide con cero o más segmentos restantes formas. Por ejemplo, /resources/{*path} coincide con todos los archivos en el directorio /resources/ y la variable "path" refleja la información completa. ruta en /resources.

Sintaxis {varName:regex} declara una variable URI con una expresión regular, que tiene la sintaxis: {varName:regex}. Por ejemplo, dada la URL /spring-web-3.0.5.jar, el siguiente método recupera el nombre del archivo, la versión y la extensión:

Java

@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\ \.[a-z]+}") 
public void handle(@PathVariable String version, @PathVariable String ext) {
// ...
} 
Kotlin

@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}") 
fun handle(@PathVariable version: String, @PathVariable ext: String) {
// ... }

Los patrones de ruta de URI también pueden contener marcadores de posición en línea ${...}, que son reconocido cuando se ejecuta con PropertySourcesPlaceholderConfigurer en local, fuentes de propiedades del sistema, fuentes de propiedades del entorno, etc. Puede usar esto, por ejemplo, para parametrizar una URL base basada en alguna configuración externa.

Spring WebFlux usa PathPattern y PathPatternParser para admitir el mapeo de rutas URI. Ambas clases se encuentran en spring-web y están diseñadas específicamente para su uso con rutas URL HTTP en aplicaciones web donde una gran cantidad de patrones de ruta URI coinciden en tiempo de ejecución.

Spring WebFlux no admite la asignación de sufijos a una imagen, a diferencia de Spring MVC, donde una asignación como /person también coincide con /person.*. Para negociar contenido basado en una URL, si es necesario, recomendamos utilizar un parámetro de consulta que sea más simple, más explícito y menos vulnerable a exploits basados en rutas URL.

Comparación de patrones

Si varios patrones coinciden con una URL, se deben comparar para encontrar la mejor coincidencia. Esto se hace usando PathPattern.SPECIFICITY_COMPARATOR, que busca patrones que son más específicos.

Para cada patrón, se calcula una puntuación en función del número de identificadores URI y variables comodín. Donde la variable URI puntúa por debajo del comodín. Gana la muestra con la puntuación total más baja. Si dos muestras tienen las mismas puntuaciones, se selecciona la más larga.

Muestras generales (por ejemplo, **, {*varName}) se excluyen del recuento y siempre se ordenan en último lugar. Si dos muestras son generales, se elige la más larga.

Tipos de medios consumidos

Puede limitar la visualización de la consulta según el Content-Type de la consulta, como se muestra en el siguiente ejemplo:

Java

@PostMapping(path = "/pets", consumes = "application /json") 
public void addPet(@RequestBody Pet pet) {
// ...
}
Kotlin

@PostMapping("/pets", consumes = ["application/json"])
fun addPet(@RequestBody pet: Pet) {
// ...
}

El atributo "consume" también admite expresiones de negación; por ejemplo, !text/plain significa cualquier tipo de contenido que no sea text/plain.

Puedes declarar un atributo genérico consume a nivel de clase. Sin embargo, a diferencia de la mayoría de los otros atributos de mapeo de solicitudes, cuando se usa a nivel de clase, el atributo consume a nivel de método anula en lugar de extender la declaración a nivel de clase.

MediaType proporciona constantes para los tipos de medios utilizados habitualmente, como APPLICATION_JSON_VALUE y APPLICATION_XML_VALUE.

Tipos de medios producidos

Puede limitar el rango de solicitudes que se muestran según el encabezado de solicitud Aceptar y la lista de tipos de contenido que produce el método del controlador. , como se muestra en el siguiente ejemplo:

Java

@GetMapping(path = "/pets/{petId}", produces = "application/json" )
@ResponseBody
public Pet getPet(@PathVariable String petId)  {
// ...
}
Kotlin

@GetMapping("/pets/{petId}", produces = ["application/json"])
@ResponseBody
fun getPet(@PathVariable String petId): Pet {
// ...
}

El tipo de medio puede determinar el juego de caracteres. Se admiten expresiones negables; por ejemplo, !text/plain significa cualquier tipo de contenido que no sea text/plain.

Puedes declarar un genérico produces el atributo a nivel de clase. Sin embargo, a diferencia de la mayoría de los otros atributos de mapeo de solicitudes, cuando se usa a nivel de clase, el atributo produce a nivel de método anula en lugar de extender la declaración a nivel de clase.

MediaType proporciona constantes para los tipos de medios utilizados habitualmente, como APPLICATION_JSON_VALUE y APPLICATION_XML_VALUE.

Parámetros y encabezados

Puede limitar la visualización de consultas según las condiciones de los parámetros de consulta. Puede verificar la presencia de un parámetro de consulta myParam, su ausencia (!myParam) o la presencia de un valor específico (myParam=myValue). Los siguientes ejemplos comprueban la presencia de un parámetro con un valor:

Java

@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") 
public void findPet (@PathVariable String petId) {
// ...
}
  1. Asegúrese de que myParam sea igual a myValue.
Kotlin

@GetMapping("/pets/{petId}", params = ["myParam=myValue"])  
fun findPet(@PathVariable petId: String) {
// ...
}
  1. Asegúrese de que myParam sea igual a myValue.

Lo mismo se puede utilizar con condiciones de encabezado de solicitud, como se muestra en el siguiente ejemplo:

Java

@GetMapping(path = "/pets", headers = "myHeader=myValue")  
public  void findPet(@PathVariable String petId) {
// ...
}
  1. Asegúrese de que myHeader sea igual a myValue.
Kotlin

@GetMapping("/pets", headers = ["myHeader=myValue"]) 
fun findPet(@PathVariable petId: String) {
// ... }
  1. Asegúrese de que myHeader sea igual a myValue.

Métodos HTTP HEAD, OPTIONS

@GetMapping (y @RequestMapping(method=HttpMethod.GET)) admiten de forma transparente el método HTTP HEAD para solicitudes de mapeo. No es necesario cambiar los métodos del controlador. La función contenedora de respuesta utilizada en HttpHandler garantiza que el encabezado Content-Length se establezca en el número de bytes escritos (sin escribir realmente en la respuesta).

Predeterminado El método OPCIONES HTTP se maneja configurando el encabezado de respuesta Allow en la lista de métodos HTTP enumerados en todos los métodos marcados con la anotación @RequestMapping.

En el caso de la anotación @RequestMapping sin declarar métodos HTTP, el encabezado Allow se establece en GET, HEAD, POST, PUT, PATCH, DELETE , OPCIONES. Los métodos del controlador siempre deben declarar métodos HTTP admitidos (por ejemplo, utilizando variantes específicas del método HTTP: @GetMapping, @PostMapping y otros).

Puede asignar explícitamente un método anotado con @RequestMapping al método HEAD HTTP y al método OPTIONS HTTP, pero en casos normales esto no es necesario.

Anotaciones personalizadas

Spring WebFlux admite el uso de anotaciones compuestas para mostrar consultas. Estas son anotaciones que son en sí mismas metanotaciones @RequestMapping y están compuestas para volver a declarar un subconjunto (o todos) de los atributos marcados con la anotación @RequestMapping para un alcance más limitado. , propósito más específico.

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping y @PatchMapping son ejemplos de anotaciones compuestas. Se proporcionan porque es probable que la mayoría de los métodos del controlador deban asignarse a un método HTTP específico en lugar de utilizar la anotación @RequestMapping, que se asigna a todos los métodos HTTP de forma predeterminada. Si necesita un ejemplo de anotaciones compuestas, observe cómo se declaran.

Spring WebFlux también admite atributos de visualización de consultas personalizados con lógica de visualización de consultas personalizada. Esta es una opción más avanzada que requiere subclasificar RequestMappingHandlerMapping y anular el método getCustomMethodCondition en el que puede verificar el atributo personalizado y devolver su propia RequestCondition.

Registro explícito

Puede registrar mediante programación métodos de controlador, que se pueden usar para registro dinámico o en casos más complejos, como si hay diferentes instancias del mismo controlador en diferentes URL. El siguiente ejemplo muestra cómo hacer esto:

Java

@Configuration
public class MyConfig {
@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) (1)
    throws NoSuchMethodException {
RequestMappingInfo info = RequestMappingInfo
        .paths("/user/{id}").methods(RequestMethod.GET).build(); 
Method method = UserHandler.class.getMethod("getUser", Long.class); 
mapping.registerMapping(info, handler, method); 
}
}
  1. Inyectar el controlador de destino y mostrar el controlador para los controladores.
  2. Preparar los metadatos de visualización de la solicitud.
  3. Obtener el método del controlador.
  4. Agregar registro.
Kotlin

@Configuration
class MyConfig {
@Autowired
fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { 
val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() 
val method = UserHandler::class.java.getMethod("getUser", Long::class.java) 
mapping.registerMapping(info, handler, method) 
}
}
  1. Inyectar el controlador de destino y la asignación del controlador para los controladores.
  2. Preparar los metadatos de asignación de la solicitud.
  3. Obtener el método del controlador.
  4. Agregar registro.

Métodos de controlador

Métodos de controlador con @RequestMapping La anotación tiene una firma flexible y se puede elegir entre varios argumentos de métodos de controlador admitidos y valores de retorno.

Argumentos de método

La siguiente tabla enumera los argumentos de métodos de controlador admitidos.

Los tipos reactivos (Reactor, RxJava u otros) se admiten en argumentos que requieren bloquear E/S para resolverse (por ejemplo, leer el cuerpo de la solicitud). Esto se indica en la columna "Descripción". No se esperan tipos reactivos para argumentos que no requieren bloqueo.

El argumento java.util.Optional de JDK 1.8 se admite como argumento de método en combinación con anotaciones que tienen un atributo required (por ejemplo, @RequestParam, @RequestHeader y otros), y es equivalente a required=false .

Método controlador argumento Descripción

ServerWebExchange

Proporciona acceso al ServerWebExchange completo: contenedor para solicitudes y respuestas HTTP, atributos de solicitud y sesión, métodos checkNotModified, etc.

ServerHttpRequest, ServerHttpResponse

Proporciona acceso a un servidor HTTP solicitud o respuesta.

WebSession

Proporciona acceso a La sesión. Esto no inicia una nueva sesión a menos que se agreguen atributos. Admite tipos reactivos.

java.security.Principal

El usuario autenticado actual - posiblemente una clase de implementación Principal concreta, si se conoce. Admite tipos reactivos.

org.springframework.http.HttpMethod

Método de solicitud HTTP.

java.util.Locale

La configuración regional actual de la solicitud, determinada por el LocaleResolver más específico disponible: esencialmente el LocaleResolver o el LocaleContextResolver configurado.

java.util.TimeZone + java.time.ZoneId

La zona horaria asociada con la solicitud actual, según lo determinado por LocaleContextResolver.

@PathVariable

Diseñado para proporcionar acceso a variables de plantilla URI.

@MatrixVariable

Diseñado para proporcionar acceso a pares nombre-valor en segmentos de ruta de URI.

@RequestParam

Proporciona acceso a los parámetros de solicitud. Los valores de los parámetros se convierten al tipo declarado de los argumentos del método.

Tenga en cuenta que usar la anotación @RequestParam es opcional; por ejemplo, para establecer sus atributos. Consulte la sección "Cualquier otro argumento" más adelante en esta tabla.

@RequestHeader

Diseñado para proporcionar acceso a los encabezados de solicitud. Los valores del encabezado se convierten al tipo declarado del argumento del método.

@CookieValue

Diseñado para proporcionar acceso a cookies. Los valores de las cookies se convierten al tipo de argumento del método declarado.

@RequestBody

Proporciona acceso al cuerpo de la solicitud HTTP. El contenido del cuerpo se convierte al tipo de argumento del método declarado utilizando instancias HttpMessageReader. Admite tipos reactivos.

HttpEntity<B>

Diseñado para proporcionar acceso a los encabezados y al cuerpo de la solicitud. El cuerpo se representa utilizando instancias HttpMessageReader. Admite tipos reactivos.

@RequestPart

Proporciona acceso al componente en la solicitud multipart/form-data. Admite tipos reactivos.

java.util.Map, org.springframework.ui.Model y org.springframework.ui.ModelMap.

Diseñado para proporcionar acceso al modelo que se utiliza en los controladores HTML y se muestra en las plantillas, como parte de la representación de la vista.

@ModelAttribute

Destinado a proporcionar acceso a un atributo existente en el modelo (del cual se crea una instancia si falta) con enlace de datos y validación.

Tenga en cuenta que usar @ModelAttribute La anotación es opcional, por ejemplo, establecer sus atributos. Consulte la sección "Cualquier otro argumento" más adelante en esta tabla.

Errores, BindingResult

Proporcionar acceso a errores de validación y enlace de datos para el objeto de comando, es decir. argumento @ModelAttribute. El argumento Errors o BindingResult debe declararse inmediatamente después del argumento del método que se está validando.

SessionStatus + nivel de clase @SessionAttributes

Destinado a marcar la finalización del procesamiento del formulario, lo que genera atributos de sesión declarado a través de la anotación de código a borrar @SessionAttributes a nivel de clase. Para obtener más información, consulte la sección sobre la anotación @SessionAttributes.

UriComponentsBuilder

Diseñado para preparar una URL asociada con el host, puerto, esquema y ruta de contexto de la solicitud actual.

@SessionAttribute

Diseñado para proporcionar acceso a cualquier atributo de sesión, a diferencia de los atributos del modelo, almacenados en la sesión como un resultado de declarar la anotación de @SessionAttributes a nivel de clase.

@RequestAttribute

Diseñado para proporcionar acceso a los atributos de la solicitud.

Cualquier otro argumento

Si el argumento de un método no coincide con ninguno de los anteriores, se resuelve de forma predeterminada en @RequestParam si es un tipo simple definido por BeanUtils#isSimplePropert, o en @ModelAttribute caso contrario.

Valores devueltos

La siguiente tabla enumera los retornos admitidos. valores de un método controlador. Tenga en cuenta que los tipos reactivos de bibliotecas como Reactor, RxJava u otras generalmente son compatibles con todos los valores de retorno.

Valor de retorno del método del controlador Descripción

@ResponseBody

El valor de retorno se codifica a través de instancias HttpMessageWriter y se escribe en la respuesta.

HttpEntity<B>, ResponseEntity<B>

El valor de retorno especifica que la respuesta completa, incluidos los encabezados HTTP y el cuerpo, se codificarán mediante instancias HttpMessageWriter y se escribirán en la respuesta.

HttpHeaders

Diseñado para devolver una respuesta con encabezados y sin cuerpo.

Cadena

Nombre de la vista a resolver por instances ViewResolver y se utiliza junto con un modelo implícito, definido a través de objetos de comando y métodos marcados con la anotación @ModelAttribute. El método controlador también puede modificar el modelo mediante programación declarando un argumento Model.

View

Una instancia de View destinada a renderizarse junto con un modelo implícito, definido a través de objetos y métodos de comando con @ModelAttribute anotación. Un método de controlador también puede modificar un modelo mediante programación declarando un argumento Model.

java.util.Map, org.springframework.ui.Model

Atributos para agregar al modelo implícito, con el nombre de la vista determinado implícitamente en función de la ruta de solicitud.

@ModelAttribute

Un atributo agregado al modelo cuando En este caso, tener vistas se determina implícitamente en función de la ruta de solicitud.

Tenga en cuenta que la anotación @ModelAttribute es opcional. Consulte "Cualquier otro valor de retorno" más adelante en esta tabla.

Rendering

API para scripts para visualizar modelos y vistas.

void

Se considera un método con un tipo de retorno de void, que probablemente sea asíncrono (por ejemplo, Mono<Void>) (o con un valor de retorno null), procesó completamente la respuesta si también tiene un argumento ServerHttpResponse, un argumento ServerWebExchange o un @ResponseStatus anotación. Lo mismo ocurre si el controlador ha realizado una verificación positiva en la ETag o en la marca de tiempo lastModified.

Si ninguno de los valores anteriores es verdadero, el tipo de retorno void también puede indicar "sin cuerpo de respuesta" para controladores REST o seleccionar un nombre de vista predeterminado para controladores HTML.

Flux<ServerSentEvent>, Observable<ServerSentEvent>, u otro tipo reactivo

Se preocupa por la generación de eventos enviados por el servidor. La función contenedora ServerSentEvent se puede omitir si solo es necesario escribir datos (sin embargo, text/event-stream se debe solicitar o declarar en la pantalla a través de produce atributo).

Cualquier otro valor de retorno

Si el valor de retorno no coincide con ninguno de los anteriores, se trata de forma predeterminada como el nombre de la vista si es String o void (se aplica la opción de nombre de vista predeterminada), o como un modelo. atributo que debe agregarse al modelo, a menos que sea un tipo simple según lo definido por BeanUtils#isSimpleProperty, en cuyo caso permanece sin resolver.

Conversión de tipo

Algunos argumentos de métodos de controlador anotados que representan entradas de solicitud basadas en cadenas (como @RequestParam, @RequestHeader, @PathVariable, @MatrixVariable y @CookieValue)) pueden requerir una conversión de tipo si el argumento no se declara como String.

En tales casos, la conversión de tipos se aplica automáticamente en función de los convertidores configurados. De forma predeterminada, se admiten tipos simples (como int, long, Date y otros). La conversión de tipos se puede configurar usando WebDataBinder o registrando Formatters con FormattingConversionService.

Un problema práctico con la conversión de tipos es el manejo valor de cadena original vacío. Dicho valor se considera faltante si se vuelve null como resultado de una conversión de tipo. Esto puede ser cierto para Long, UUID y otros tipos de destino. Si necesita permitir la inyección null, utilice el indicador required en la anotación del argumento o declare el argumento como @Nullable.

Variables de matriz

En RFC 3986 Los pares "nombre" se describen -valor" en segmentos de ruta. En Spring WebFlux las llamamos "variables de matriz", según la "publicación anterior" de Tim Berners -Lee, pero también pueden denominarse parámetros de ruta URI.

Las variables de matriz pueden aparecer en cualquier segmento de ruta, con cada variable separada por un punto y coma y varios valores separados por comas, por ejemplo, "/cars;color=red,green;year=2012". También se pueden especificar múltiples valores a través de nombres de variables repetidos, por ejemplo, color=red;color=green;color= azul) .

A diferencia de Spring MVC, en WebFlux la presencia o ausencia de variables matriciales en la URL no afecta la representación de las solicitudes. En otras palabras, no es necesario utilizar una variable URI para enmascarar el contenido de la variable. Sin embargo, si necesita acceder a variables matriciales desde un método de controlador, debe agregar una variable URI al segmento de ruta donde se esperan variables matriciales. El siguiente ejemplo muestra cómo hacer esto:

Java

// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
Kotlin

 // GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
fun findPet(@PathVariable petId: String, @MatrixVariable q: Int) {
// petId == 42
// q == 11
}

Considerando que todos los segmentos de ruta pueden contener variables matriciales , a veces es posible que necesite definir en qué variable de ruta debe estar la variable de matriz, como se muestra en el siguiente ejemplo:

Java

// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
Kotlin

@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable(name = "q", pathVar = "ownerId") q1: Int,
@MatrixVariable(name = "q", pathVar = "petId") q2: Int) {
// q1 == 11
// q2 == 22
}

Puede definir una variable de matriz como opcional y proporcionar un valor predeterminado, como se muestra en el siguiente ejemplo:

Java

// GET /pets/42
@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
Kotlin

// GET /pets/42
@GetMapping("/pets/{petId}")
fun findPet(@MatrixVariable(required = false, defaultValue = "1") q: Int) {
// q == 1
}

Para obtener todas las variables de una matriz, use MultiValueMap como se muestra en el siguiente ejemplo:

Java

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable MultiValueMap<String, String> matrixVars,
@MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
Kotlin

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable matrixVars: MultiValueMap<String, String>,
@MatrixVariable(pathVar="petId") petMatrixVars: MultiValueMap<String, String>) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}

@RequestParam

Puede utilizar la anotación @RequestParam para vincular los parámetros de solicitud a un argumento de método en el controlador. El siguiente fragmento de código demuestra el uso:

Java

@Controller
@RequestMapping("/pets")
public class EditPetForm {
// ...
@GetMapping
public String setupForm(@RequestParam("petId") int petId, Model model) { 
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
// ...
}
  1. Usando anotación @RequestParam.
Kotlin

import org.springframework.ui.set
@Controller
@RequestMapping("/pets")
class EditPetForm {
// ...
@GetMapping
fun setupForm(@RequestParam("petId") petId: Int, model: Model): String { 
val pet = clinic.loadPet(petId)
model["pet"] = pet
return "petForm"
}
// ...
}
  1. Usando la anotación @RequestParam.
El concepto de "parámetro de solicitud" en la API de Servlet combina parámetros de solicitud, datos de formulario, y datos de varias partes en uno. Sin embargo, en WebFlux, se accede a cada uno de ellos por separado a través de ServerWebExchange. Aunque la anotación @RequestParam se vincula solo a los parámetros de solicitud, puede utilizar el enlace de datos para aplicar parámetros de solicitud, datos de formulario y elementos multiparte a un objeto de comando.

Parámetros del método que utilizan la anotación @RequestParam es obligatoria de forma predeterminada, pero también puede configurar un parámetro de método para que sea opcional estableciendo el indicador apropiado para @RequestParam en false o declarando el argumento usando el contenedor java.util.Optional.

Si el tipo de parámetro del método de destino no es String, la conversión de tipo es se aplica automáticamente.

Si la anotación @RequestParam se declara para Map<String, String> o MultiValueMap<String, String>, el mapa se completa con todos los parámetros de solicitud.

Tenga en cuenta que usar la anotación @RequestParam es opcional, por ejemplo, para establecer sus atributos. De forma predeterminada, cualquier argumento que sea un tipo de valor simple (como se define en BeanUtils#isSimpleProperty) y no se resuelve mediante ningún otro solucionador de argumentos, se trata como si estuviera anotado con @RequestParam.

@RequestHeader

Puede utilizar la anotación @RequestHeader para vincular un encabezado de solicitud a un argumento de método en un controlador.

El siguiente ejemplo muestra una solicitud con encabezados:

 
Host                    localhost:8080
Accept                  text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language         fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding         gzip,deflate
Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive              300

El siguiente código de ejemplo le permite obtener el valor de Accept-headers Encoding y Keep-Alive:

Java
 
@GetMapping("/demo")
public void handle(
@RequestHeader("Accept-Encoding") String encoding, (1)
@RequestHeader("Keep-Alive") long keepAlive) { 
//...
}
  1. Obtenga Accept-Encoding valores del encabezado.
  2. Obtenga los valores del encabezado Keep-Alive.
Kotlin

@GetMapping("/demo")
fun handle(
@RequestHeader("Accept-Encoding") encoding: String, (1)
@RequestHeader("Keep-Alive") keepAlive: Long) { 
//...
}
  1. Obtener los valores del código Accept-Encoding.
  2. Obtener los valores del encabezado Keep-Alive.

Si el tipo del parámetro del método de destino no es String, la conversión de tipo se aplica automáticamente.

Si la anotación @RequestHeader se utiliza en el argumento Map<String, String>, MultiValueMap<String, String> o HttpHeaders, el mapa se completa con todos los valores de encabezado.

Tiene soporte incorporado para convertir una cadena separada por comas en una matriz o colección de cadenas u otros tipos conocidos por el sistema de conversión de tipos. Por ejemplo, un parámetro de método anotado con @RequestHeader("Accept") puede ser de tipo String, así como String[] o List<String>.

@CookieValue

Puedes usar la anotación @CookieValue para vincular un valor de cookie HTTP al argumento del método en el controlador.

El siguiente ejemplo muestra una solicitud con una cookie:

JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84

El siguiente ejemplo de código muestra cómo obtener el valor de la cookie:

Java

@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) { 
//...
}
  1. Obteniendo el valor de la cookie.
Kotlin

@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) { 
//...
}
  1. Obtener el valor de la cookie.

Si el tipo de parámetro del método de destino no es String, la conversión de tipo se aplica automáticamente.

@ModelAttribute

Puedes usar la anotación @ModelAttribute en un argumento de método para acceder al atributo desde el modelo o crear una instancia. de él si falta. El atributo del modelo también se superpone con los valores de los parámetros de consulta y los campos del formulario, cuyos nombres coinciden con los nombres de los campos. Esto se denomina enlace de datos y elimina la necesidad de analizar y transformar parámetros de consulta y campos de formulario individuales. El siguiente ejemplo muestra cómo se vincula una instancia de Pet:

Java

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { }
  1. Enlazando el instancia Pet.
Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { }
  1. Enlazando la instancia Pet.

La instancia Pet del ejemplo anterior se resuelve de la siguiente manera:

  • Del modelo, si tiene ya se ha agregado a través de Model.

  • Desde una sesión HTTP a través de la anotación @SessionAttributes.

  • Desde una llamada al constructor predeterminado.

  • Desde una llamada al "constructor principal" con argumentos correspondientes a parámetros de solicitud o campos de formulario. Los nombres de los argumentos se especifican mediante la anotación @ConstructorProperties para JavaBeans o mediante nombres de parámetros almacenados en código de bytes en tiempo de ejecución.

Después de obtener una instancia del atributo del modelo, se aplica el enlace de datos. La clase WebExchangeDataBinder asigna parámetros de consulta y nombres de campos de formulario a nombres de campos del Objeto de destino. Los campos correspondientes se completan después de la conversión de tipo cuando sea necesario.

La vinculación de datos puede provocar errores. De forma predeterminada, se genera una WebExchangeBindException, pero para verificar dichos errores en un método de controlador, puede agregar un argumento BindingResult directamente al lado de @ModelAttribute. anotación, como se muestra en el siguiente ejemplo:

Java

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { 
if (result.hasErrors()) {
return "petForm";
}
// ...
}
  1. Agregando BindingResult.
Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { 
if (result.hasErrors()) {
return "petForm"
}
// ...
}
  1. Agregando BindingResult.

Puede aplicar automáticamente la validación después del enlace de datos agregando la anotación javax.validation.Valid o @Validated código de anotación de Spring. El siguiente ejemplo utiliza la anotación @Valid:

Java

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { 
if (result.hasErrors()) {
return "petForm";
}
// ...
}
  1. Usando anotación @Válido para el argumento del atributo del modelo.
Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { 
if (result.hasErrors()) {
return "petForm"
}
// ...
}
  1. Usando la anotación @Valid para el argumento del atributo del modelo.

Spring WebFlux, a diferencia de Spring MVC, admite tipos reactivos en el modelo, por ejemplo, Mono<Account> o io.reactivex.Single<Account>. Puede declarar un argumento marcado con la anotación @ModelAttribute con o sin una función contenedora de tipo reactivo, después de lo cual se resolverá de acuerdo con el valor real si es necesario. Sin embargo, tenga en cuenta que para utilizar el argumento BindingResult, debe declarar el argumento con la anotación @ModelAttribute antes, sin una función contenedora de tipo reactivo, como se mostró anteriormente. Además, puede manejar cualquier error mediante un tipo reactivo, como se muestra en el siguiente ejemplo:

Java

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
return petMono
.flatMap(pet -> {
    // ...
})
.onErrorResume(ex -> {
    // ...
});
}
Kotlin

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") petMono: Mono<Pet>): Mono<String> {
return petMono
    .flatMap { pet ->
        // ...
    }
    .onErrorResume{ ex ->
        // ...
    }
}

Tenga en cuenta que utilice la anotación @ModelAttribute es opcional, por ejemplo, para establecer sus atributos. De forma predeterminada, cualquier argumento que no sea un tipo de valor simple (como se define en BeanUtils#isSimpleProperty) y no se resuelve mediante ningún otro solucionador de argumentos, se trata como si estuviera anotado con @ModelAttribute.

@SessionAttributes

@SessionAttributes se utiliza para almacenar atributos del modelo en WebSession entre solicitudes. Esta es una anotación de nivel de tipo que declara los atributos de sesión utilizados por un controlador en particular. Por lo general, esto enumera los nombres de atributos del modelo o los tipos de atributos del modelo que deben almacenarse de forma transparente en la sesión para solicitudes de acceso posteriores.

Considere el siguiente ejemplo:

Java
 
@Controller
@SessionAttributes("pet") 
public class EditPetForm {
// ...
}        
    
  1. Usando la anotación @SessionAttributes.
Kotlin

@Controller
@SessionAttributes("pet") 
class EditPetForm {
// ...
} 
  1. Usando la anotación @SessionAttributes.

En la primera solicitud, si se agrega un atributo de modelo llamado pet al modelo, se promociona automáticamente y se almacena en WebSession. Permanece allí hasta que otro método de controlador utiliza el argumento del método SessionStatus para borrar el almacenamiento, como se muestra en el siguiente ejemplo:

Java

@Controller
@SessionAttributes("pet") 
public class EditPetForm {
// ...
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) { 
if (errors.hasErrors()) {
    // ...
}
    status.setComplete();
    // ...
}
}
}
  1. Usando el anotación @SessionAttributes.
  2. Usando la variable SessionStatus.
Kotlin

@Controller
@SessionAttributes("pet") 
class EditPetForm {
// ...
@PostMapping("/pets/{id}")
fun handle(pet: Pet, errors: BindingResult, status: SessionStatus): String { 
if (errors.hasErrors()) {
    // ...
}
status.setComplete()
// ...
}
}
  1. Usando la anotación @SessionAttributes.
  2. Usando la variable SessionStatus.

@SessionAttribute

Si necesita acceso a atributos de sesión preexistentes que se administran globalmente (es decir, está, fuera del controlador (por ejemplo, filtro) y puede estar presente o no, puede usar la anotación @SessionAttribute en el parámetro del método, como se muestra en el siguiente ejemplo:

Java

@GetMapping("/")
public String handle(@SessionAttribute User user) { 
// ...
}
  1. Utilice la anotación @SessionAttribute.
Kotlin

@GetMapping("/")
fun handle(@SessionAttribute user: User): String { 
// ...
} 
  1. Utilice la anotación @SessionAttribute.

Para casos de uso que requieren agregar o eliminar atributos de sesión, tenga en cuenta la capacidad de inyectar WebSession en un método de controlador.

Para almacenar temporalmente atributos de modelo en una sesión como parte del controlador flujo de trabajo, considere usar SessionAttributes.

@RequestAttribute

Similar a la anotación @SessionAttribute, puede utilizar la anotación @RequestAttribute para acceder a la solicitud de atributos existentes creada anteriormente (por ejemplo, WebFilter), como se muestra en el siguiente ejemplo:

Java

@GetMapping("/")
public String handle(@RequestAttribute Client client) { 
// ...
}
  1. Usando la anotación @RequestAttribute.
Kotlin

@GetMapping("/")
fun handle(@RequestAttribute client: Client): String { 
// ...
}
  1. Usando la anotación @RequestAttribute.

Contenido de varias partes

ServerWebExchange proporciona acceso a contenido de varias partes. La mejor manera de manejar un formulario de carga de archivos (desde un navegador, por ejemplo) en un controlador es vincular los datos a un objeto de comando, como se muestra en el siguiente ejemplo:

Java

class MyForm {
private String name;
private MultipartFile file;
// ...
}
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
// ...
}
}
Kotlin

class MyForm(
val name: String,
val file: MultipartFile)
@Controller
class FileUploadController {
@PostMapping("/form")
fun handleFormUpload(form: MyForm, errors: BindingResult): String {
// ...
}
}

También puede enviar solicitudes de varias partes desde clientes que no son de navegador en scripts utilizando servicios RESTful. El siguiente ejemplo utiliza un archivo junto con JSON:

POST /someUrl
Content-Type: multipart/mixed
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit
{
"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...

Puede acceder a componentes individuales utilizando la anotación @RequestPart, como se muestra en el siguiente ejemplo:

Java

@PostMapping("/")
public String handle(@RequestPart("meta-data") Part metadata, 
@RequestPart("file-data") FilePart file) { 
// ...
}
  1. Uso Anotación @RequestPart para obtener metadatos.
  2. Utilice la anotación @RequestPart para obtener un archivo.
Kotlin

@PostMapping("/")
fun handle(@RequestPart("meta-data") Part metadata,
@RequestPart("file-data") FilePart file): String { 
// ...
}
  1. Utilice la anotación @RequestPart para recuperar metadatos.
  2. Utilice la anotación @RequestPart para recuperar un archivo.

Para deserializar el contenido sin procesar de un componente (por ejemplo, en JSON, similar a la anotación @RequestBody), puede declarar un Objeto de destino específico en lugar de una Parte, como se muestra en el siguiente ejemplo:

Java

@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata) { 
// ...
}
  1. Utilice la anotación @RequestPart para recuperar metadatos.
Kotlin

@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData): String { 
// ...
}
  1. Uso de la anotación @RequestPart para obtener metadatos.

Puede utilizar la anotación @RequestPart en combinación con la anotación javax.validation.Valid o la anotación @Validated de Spring, que hará que se aplique la validación de frijol estándar. Los errores de validación dan como resultado una WebExchangeBindException, lo que genera una respuesta 400 (BAD_REQUEST). La excepción contiene un BindingResult con detalles del error y también se puede manejar en un método de controlador declarando un argumento con una función contenedora asincrónica y luego use las declaraciones asociadas con el error:

Java

@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
// use uno de los onError*...
}
Operadores
Kotlin

@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData): String {
// ...
}

Para acceder a todos los datos de múltiples componentes como un MultiValueMap, debe Puede utilizar la anotación de @RequestBody, como se muestra en el siguiente ejemplo:

Java

@PostMapping("/")
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) { 
// ...
}
  1. Usando la anotación @RequestBody.
Kotlin

@PostMapping("/")
fun handle(@RequestBody parts: MultiValueMap<String, Part>): String { 
// ...
}
  1. Usando la anotación @RequestBody.

Para acceder en streaming secuencial a datos de varias partes, puede utilizar la anotación @RequestBody con Flux<Part> (o Flow<Part> en Kotlin), como se muestra en el siguiente ejemplo:

Java

@PostMapping("/")
public String handle(@RequestBody Flux<Part> parts) { 
// ...
}
  1. Usando la anotación @RequestBody.
Kotlin

@PostMapping("/")
fun handle(@RequestBody parts: Flow<Part>): String { 
// ...
}
  1. Usando anotación @RequestBody.

@RequestBody

Anotación @RequestBody se puede utilizar para leer el cuerpo de la solicitud y deserializarlo en un objeto object HttpMessageReader. El siguiente ejemplo utiliza un argumento anotado con @RequestBody:

Java

@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
// ...
}
Kotlin
 
@PostMapping("/accounts")
fun handle(@RequestBody account: Account) {
// ...
}

A diferencia de Spring MVC, en WebFlux el argumento de un método marcado con la anotación @RequestBody admite tipos reactivos y lectura y transmisión (cliente-servidor) totalmente sin bloqueo.

Java

@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
// ...
}
Kotlin

@PostMapping("/accounts")
fun handle(@RequestBody accounts: Flow<Account>) {
// ...
}

Puede usar la opción de códecs de mensajes HTTP en la configuración de WebFlux para configurar o personalizar lectores de mensajes.

La anotación @RequestBody se puede usar en combinación con la de anotación javax.validation.Valid o la anotación @Validated de Spring, que hace que se aplique la validación de Bean estándar. Los errores de validación provocan WebExchangeBindException, lo que genera una respuesta 400 (BAD_REQUEST). La excepción contiene un BindingResult con detalles del error y también se puede manejar en un método de controlador declarando un argumento con una función contenedora asíncrona y luego usando las declaraciones relacionadas con el error:

Java

@PostMapping("/accounts")
public void handle(@Valid @RequestBody Mono<Account> account) {
// use uno de los onError*...
}}
Kotlin

@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Mono<Account>) {
// ...
}

HttpEntity

HttpEntity es más o menos idéntico a @RequestBody anotación en términos de uso, pero se basa en la entidad: un contenedor que abre los encabezados y el cuerpo de la solicitud. El siguiente ejemplo utiliza HttpEntity:

Java

@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
// ...
}
Kotlin

@PostMapping("/accounts")
fun handle(entity: HttpEntity<Account>) {
// ...
}

@ResponseBody

La anotación @ResponseBody se puede utilizar en nuestro método para que la devolución se serialice en el cuerpo de la respuesta a través de HttpMessageWriter. El siguiente ejemplo muestra cómo hacer esto:

Java

@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
// ...
}
Kotlin

@GetMapping("/accounts/{id}")
@ResponseBody
fun handle(): Account {
// ...
}

@ResponseBody también se admite a nivel de clase, en cuyo caso lo heredan todos los métodos del controlador. Este es el efecto de la anotación @RestController, que no es más que una metaanotación marcada con las anotaciones @Controller y @ResponseBody.

@ResponseBody admite tipos reactivos, lo que significa que puede devolver tipos Reactor o RxJava y recibir los valores asincrónicos que crean en respuesta.

Puede combinar métodos marcados con la anotación @ResponseBody con vistas de serialización JSON.

Puede utilizar la opción de códecs de mensajes HTTP en la configuración de WebFlux para configurar o personalizar lectores de mensajes.

ResponseEntity

El atributo ResponseEntity es similar a la anotación @ResponseBody, pero con un estado y encabezados. Por ejemplo:

Java

@GetMapping("/something")
public ResponseEntity<String> handle() {
String body = ... ;
String etag = ... ;
return ResponseEntity.ok().eTag(etag).body(body);
}
Kotlin

@GetMapping("/something")
fun handle(): ResponseEntity<String> {
val body: String = ...
val etag: String = ...
return ResponseEntity.ok().eTag(etag).build(body)
}

WebFlux admite el uso de valor único de tipo reactivo para crear de forma asincrónica ResponseEntity y/o tipos reactivos de valor único y múltiple para el cuerpo. Esto le permite utilizar diferentes respuestas asincrónicas con ResponseEntity de la siguiente manera:

  • ResponseEntity<Mono<T>> o ResponseEntity<Flux<T>>comunica inmediatamente el estado y los encabezados de la respuesta, mientras que el cuerpo de la respuesta se proporciona de forma asíncrona en un momento posterior. Usamos Mono si el cuerpo consta de 0..1 valores, o Flux si puede crear varios valores.

  • Mono<ResponseEntity<T>> proporciona los tres parámetros (estado de respuesta, encabezados y cuerpo) de forma asíncrona en un momento posterior. Esto le permite cambiar el estado de la respuesta y los encabezados según los resultados del procesamiento de solicitudes asincrónicas.

  • Mono<ResponseEntity<Mono<T>>> o Mono<ResponseEntity<Flux<T>>> son otra alternativa posible, aunque menos común. Primero proporcionan el estado de la respuesta y los encabezados de forma asíncrona, y luego, en segundo lugar, el cuerpo de la respuesta, también de forma asíncrona.

Jackson JSON

Spring proporciona soporte para la biblioteca Jackson JSON .

Vistas JSON

Spring WebFlux proporciona soporte integrado para vistas de serialización de la biblioteca Jackson, que le permite representar exclusivamente un subconjunto de todos los campos Objeto. Para usarlo con métodos de controlador anotados con @ResponseBody, o con la clase ResponseEntity, puede aprovechar la anotación @JsonView de Jackson para activar el clase de vista de serialización, como se muestra en el siguiente ejemplo:

Java

@RestController
public class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
@JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
Kotlin

@RestController
class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView::class)
fun getUser(): User {
return User("eric", "7!jd#h23")
}
}
class User(
@JsonView(WithoutPasswordView::class) val username: String,
@JsonView(WithPasswordView::class) val password: String
) {
interface WithoutPasswordView
interface WithPasswordView : WithoutPasswordView
}
Anotación @JsonView le permite usar una variedad de clases de vista, pero solo puede especificar una por método de controlador. Utilice una interfaz compuesta si necesita activar varias vistas.

Model

Puede utilizar la anotación @ModelAttribute:

  • Como argumento de método en métodos con la anotación @RequestMapping para crear o proporcionar acceso a un objeto desde el modelo y vincularlo a una solicitud mediante WebDataBinder .

  • Como anotación a nivel de método en clases marcadas con @Controller o @ControllerAdvice anotaciones para ayudar a inicializar el modelo antes de llamar a un método con la anotación @RequestMapping.

  • En un método con @RequestMapping anotación para marcar su valor de retorno como un atributo del modelo.

Esta sección describe los métodos anotados con @ModelAttribute, o el segundo elemento de la lista anterior. Un controlador puede tener cualquier número de métodos marcados con la anotación @ModelAttribute. Todos estos métodos se llaman antes que los métodos marcados con la anotación @RequestMapping en el mismo controlador. Un método con la anotación @ModelAttribute también se puede compartir entre controladores a través de la anotación @ControllerAdvice.

Métodos con La anotación @ModelAttribute tiene firmas de métodos flexibles. Admiten muchos de los mismos argumentos que los métodos marcados con la anotación @RequestMapping, excepto la anotación @ModelAttribute en sí o cualquier cosa asociada con el cuerpo de la solicitud.

El siguiente ejemplo utiliza un método marcado con la anotación @ModelAttribute:

Java

@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountRepository.findAccount(number));
// agregar más ...
}
Kotlin
 
@ModelAttribute
fun populateModel(@RequestParam number: String, model: Model) {
model.addAttribute(accountRepository.findAccount(number))
// agregar más ...
}
        

El siguiente ejemplo agrega solo un atributo:

Java

@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountRepository.findAccount(number);
}
Kotlin

@ModelAttribute
fun addAccount(@RequestParam number: String): Account {
return accountRepository.findAccount(number);
}
Si el nombre no se especifica explícitamente, entonces el nombre predeterminado se elige según el tipo, como se describe en el javadoc en acuerdos. Siempre puedes asignar un nombre explícito usando la sobrecarga addAttribute o mediante el atributo name en la anotación @ModelAttribute (para el valor de retorno).

Spring WebFlux, a diferencia de Spring MVC, admite explícitamente tipos reactivos en el modelo (por ejemplo, Mono<Account> o io.reactivex.Single<Account>). Dichos atributos del modelo asincrónico se pueden resolver de forma transparente (y el modelo actualizado) a sus valores reales durante una llamada a @RequestMapping si se declara el argumento con la anotación @ModelAttribute. sin una función contenedora, como se muestra en el siguiente ejemplo:

Java

@ModelAttribute
public void addAccount(@RequestParam String number) {
Mono<Account> accountMono = accountRepository.findAccount(number);
model.addAttribute("account", accountMono);
}
@PostMapping("/accounts")
public String handle(@ModelAttribute Account account, BindingResult errors) {
// ...
}
Kotlin

import org.springframework.ui.set
@ModelAttribute
fun addAccount(@RequestParam number: String) {
val accountMono: Mono<Account> = accountRepository.findAccount(number)
model["account"] = accountMono
}
@PostMapping("/accounts")
fun handle(@ModelAttribute account: Account, errors: BindingResult): String {
// ...
}

Además, cualquier atributo del modelo que tenga una función contenedora reactiva se resuelve a sus valores reales (y el modelo se actualiza) inmediatamente antes. se representa la vista.

También es posible utilizar la anotación @ModelAttribute como una anotación a nivel de método para los métodos marcados con la anotación @RequestMapping. , en cuyo caso el valor de retorno del método marcado como anotación @RequestMapping, se interpreta como un atributo del modelo. Por lo general, esto no es necesario porque este es el comportamiento predeterminado en los controladores HTML a menos que el valor de retorno sea una String, que de otro modo se interpretaría como el nombre de la vista. La anotación @ModelAttribute también puede ayudarle a personalizar el nombre del atributo del modelo, como se muestra en el siguiente ejemplo:

Java

@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
public Account handle() {
// ...
return account;
}
Kotlin

@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
fun handle(): Account {
// ...
return account
}

DataBinder

Clases con anotación @Controller o @ControllerAdvice pueden tener métodos anotados con @InitBinder para inicializar instancias de WebDataBinder. Éstos, a su vez, se utilizan para:

  • Vincular parámetros de solicitud (es decir, datos de formulario o solicitud) a un objeto modelo.

  • Convierte valores de solicitud basados en Cadena (como parámetros de solicitud, variables de ruta, encabezados, cookies, etc.) al tipo de destino de argumentos del método del controlador.

  • Formatear los valores de los objetos del modelo como valores String al representar formularios HTML.

Métodos con el anotación@InitBinder puede registrar componentes java.beans.PropertyEditor o Converter y Formatter específicos del controlador de Spring. También puede utilizar la configuración Java de WebFlux para registrar los tipos Converter y Formatter en un FormattingConversionService compartido globalmente.

Métodos marcados con la anotación @InitBinder admiten muchos de los mismos argumentos que los métodos con la anotación @RequestMapping, con la excepción de los argumentos anotados con @ModelAttribute (objeto de comando). Normalmente se declaran con un argumento WebDataBinder para registrarse y un valor de retorno void. El siguiente ejemplo utiliza la anotación @InitBinder:

Java

@Controller
public class FormController {
@InitBinder 
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
// ...
}
  1. Usando anotación @InitBinder.
Kotlin
@Controller
class FormController {
@InitBinder 
fun initBinder(binder: WebDataBinder) {
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
dateFormat.isLenient = false
binder.registerCustomEditor(Date::class.java, CustomDateEditor(dateFormat, false))
}
// ...
}

Como alternativa, cuando utilice la configuración basada en Formatter a través del FormattingConversionService genérico, puede reutilizar el mismo enfoque y registrar Formatter. código> código de instancias> específico del controlador, como se muestra en el siguiente ejemplo:

Java
@Controller
public class FormController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); 
}
// ...
}
  1. Agregue un formateador personalizado (en este caso DateFormatter).
Kotlin

@Controller
class FormController {
@InitBinder
fun initBinder(binder: WebDataBinder) {
binder.addCustomFormatter(DateFormatter("yyyy-MM-dd")) 
}
// ...
}
  1. Agregar un formateador personalizado (en este caso DateFormatter).

Estructura del modelo

En el contexto de las aplicaciones web, el enlace de datos implica vincular parámetros de solicitud HTTP (es decir, datos de formulario o parámetros de solicitud) a las propiedades de un objeto modelo y sus subproductos, objetos.

Solo propiedades public correspondientes a están abiertos para el enlace de datos convenciones de nomenclatura de JavaBeans; por ejemplo, los métodos public String getFirstName() y public void setFirstName(String) para Propiedad firstName.

El objeto modelo y su gráfico de objetos anidados también se denominan a veces objeto de comando, objeto de formulario base o POJO (“buen viejo objeto Java”).

De forma predeterminada, Spring permite el enlace a todas las propiedades públicas en el gráfico de objetos del modelo. Esto significa que debe pensar detenidamente qué propiedades públicas tiene el modelo, ya que el cliente puede apuntar a cualquier propiedad pública, incluso aquellas que no están destinadas al caso de uso.

Por ejemplo, si tiene datos HTTP endpoint -forms, un cliente malintencionado puede pasar valores para propiedades que existen en el gráfico de objetos del modelo pero que no forman parte del formulario HTML presentado en el navegador. Esto puede provocar que el objeto del modelo y cualquiera de sus subobjetos se establezcan en datos que no se espera que se actualicen.

El enfoque recomendado es utilizar un objeto de modelo especializado. que abre solo aquellas propiedades que son relevantes para el envío del formulario. Por ejemplo, en un formulario para cambiar la dirección de correo electrónico de un usuario, el objeto modelo debe declarar un conjunto mínimo de propiedades, como en el siguiente ChangeEmailForm.


public class ChangeEmailForm {
private String oldEmailAddress;
private String newEmailAddress;
public void setOldEmailAddress(String oldEmailAddress) {
this.oldEmailAddress = oldEmailAddress;
}
public String getOldEmailAddress() {
return this.oldEmailAddress;
}
public void setNewEmailAddress(String newEmailAddress) {
this.newEmailAddress = newEmailAddress;
}
public String getNewEmailAddress() {
return this.newEmailAddress;
}
}

Si no puede o no quiere utilizar un objeto de modelo especializado para cada caso de uso de enlace de datos, entonces deberestringir las propiedades permitidas para el enlace de datos. Idealmente, esto se puede lograr registrando patrones de campos permitidos usando el método setAllowedFields() en WebDataBinder.

Por ejemplo, Para registrar patrones de campo válidos en su aplicación, puede implementar un método marcado con la anotación @InitBinder en un componente con @Controller o @ControllerAdvice anotación, como se muestra a continuación:

 
@Controller
public class ChangeEmailController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setAllowedFields("oldEmailAddress", "newEmailAddress");
}
// métodos @RequestMapping, etc.
}

Además de registrar plantillas de campos válidas, también puede registrar patrones de campos no permitidos utilizando el método setDisallowedFields() en DataBinder y sus subclases. Sin embargo, tenga en cuenta que la "lista de permitidos" es más segura que la "lista de denegados". Por lo tanto, se debe preferir el uso de setAllowedFields() a setDisallowedFields().

Tenga en cuenta que la comparación con patrones de campos permitidos distingue entre mayúsculas y minúsculas; mientras que la coincidencia de patrones de campos no válidos no lo es. Además, no se aceptará un campo que coincida con un patrón de campos no válidos, incluso si también coincide con un patrón en la lista de campos válidos.

Es muy importante configurar correctamente las plantillas de campos válidas e inválidas al abrir directamente el modelo de dominio para el enlace de datos. De lo contrario, supondrá un gran riesgo para la seguridad.

Además, se recomienda encarecidamente no utilizar tipos de su modelo de dominio, como entidades JPA o Hibernate, como objetos de modelo en escenarios de enlace de datos.

Gestión de excepciones

Las clases con las anotaciones @Controller y @ControllerAdvice pueden tener métodos marcados con la anotación @ExceptionHandler, para manejar excepciones de los métodos del controlador. El siguiente ejemplo contiene este método de controlador:

Java

@Controller
public class SimpleController {
// ...
@ExceptionHandler 
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
  1. Declaración de la anotación @ExceptionHandler.
Kotlin

@Controller
class SimpleController {
// ...
@ExceptionHandler 
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
}
  1. Declaración de anotación @ExceptionHandler.

Una excepción puede ser una excepción de alto nivel propagada (por ejemplo, una IOException lanzada directamente) o una causa anidada dentro de una excepción contenedora (por ejemplo, IOException incluida dentro de IllegalStateException).

Para tipos de excepción coincidentes, es preferible declare la excepción de destino como un argumento de método, como se muestra en el ejemplo anterior. Además, la declaración de anotación puede limitar los tipos de excepciones que deben coincidir, Generalmente recomendamos ser lo más específico posible en la firma del argumento y declarar las asignaciones de excepción raíz principal a la anotación @ControllerAdvice en el orden apropiado.

Un método con la anotación @ExceptionHandler en WebFlux admite los mismos argumentos de método y valores de retorno que un método con la anotación @RequestMapping, excepto para los argumentos del método asociados con el cuerpo de la solicitud y con la anotación @ModelAttribute.

El soporte para métodos anotados con @ExceptionHandler en Spring WebFlux lo proporciona el HandlerAdapter para métodos con la anotación @RequestMapping.

Manejo de excepciones en la API REST

Un requisito común para REST- servicios basados es incluir información de error en el cuerpo de la respuesta. Spring Framework no hace esto automáticamente porque la inclusión de información de error en el cuerpo de la respuesta es específica de la aplicación. Sin embargo, la anotación @RestController puede usar métodos marcados con la anotación @ExceptionHandler con un valor de retorno ResponseEntity para establecer el estado y el cuerpo de la respuesta. Dichos métodos también se pueden declarar en clases con la anotación @ControllerAdvice para aplicarlos globalmente.

Tenga en cuenta que Spring WebFlux no tiene un equivalente para ResponseEntityExceptionHandler de Spring MVC, ya que WebFlux solo arroja ResponseStatusException (o sus subclases) y no es necesario convertirlos a código de estado HTTP.

ControllerAdvice

Normalmente, los métodos marcados con las anotaciones @ExceptionHandler, @InitBinder y @ModelAttribute son se utilizan dentro de la clase @Controller (o jerarquía de clases) en la que se declaran. Si desea que dichos métodos se apliquen de manera más global (en todos los controladores), puede declararlos en una clase anotada con @ControllerAdvice o @RestControllerAdvice.

La anotación @ControllerAdvice está anotada con @Component, lo que significa que dichas clases se pueden registrar como Spring beans mediante el escaneo de componentes. La anotación @RestControllerAdvice es una anotación compuesta que está marcada tanto con la anotación @ControllerAdvice como con la anotación @ResponseBody, lo que significa que los métodos con la anotación @ExceptionHandler se representa en el cuerpo de la respuesta a través del mapeo de mensajes (a diferencia de la resolución de vista o la representación de plantilla).

Al inicio, las clases de marco para los métodos anotados con @RequestMapping y @ExceptionHandler detectan Spring beans anotados con @ControllerAdvice y luego aplican sus métodos en tiempo de ejecución. Los métodos globales anotados con @ExceptionHandler (de la anotación @ControllerAdvice) se aplican después de los locales (de @Controller anotación). Por el contrario, los métodos globales con las anotaciones @ModelAttribute y @InitBinder se aplican antes de los locales.

De forma predeterminada, Los métodos marcados con de anotación @ControllerAdvice se aplican a cada solicitud (es decir, a todos los controladores), pero puede limitar los controladores utilizando atributos de anotación, como se muestra en el siguiente ejemplo:

Java

// Apunte a todos los controladores anotados con @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// Apunte a todos los controladores en ciertos paquetes
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Apunte a todos los controladores asignados a clases específicas
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}}
Kotlin

// Apunte a todos los controladores anotados con @RestController
@ControllerAdvice(annotations = [RestController::class])
public class ExampleAdvice1 {}
// Seleccione todos los controladores en el apuntar a ciertos paquetes
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Apunta a todos los controladores asignados a determinadas clases
@ControllerAdvice(assignableTypes = [ControllerInterface::class, AbstractController::class])
public class ExampleAdvice3 {}

Los selectores del ejemplo anterior son se evalúa durante la ejecución y puede afectar negativamente al rendimiento cuando se utiliza de forma extensiva. Para obtener más información, consulte la anotación de javadoc @ControllerAdvice.