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:
@RestController
public classHelloController {
@GetMapping("/hello") public String handle (){
return "Hello WebFlux";
}
}
@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:
@Configuration
@ComponentScan("org.example.web")
public class WebConfig {
// ...
}
@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:
@RestController
@RequestMapping("/persons") class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostMapping
@ResponseStatus( HttpStatus.CREATED) public void add(@RequestBody Person person) {
// ...
}
}
@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 |
|
|
Coincide con cero o más caracteres en el segmento de ruta |
|
|
Coincide con cero o más rutas segmentos hasta el final de la ruta |
|
|
Coincide con un segmento de ruta y lo escribe en una variable llamada "nombre". |
|
|
Coincide con la expresión |
|
|
Coincide con cero o más segmentos de ruta con el final de la ruta y los escribe en una variable denominada "ruta". |
|
Se puede acceder a las variables URI capturadas mediante una anotación @PathVariable
, como se muestra en el siguiente ejemplo:
@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
@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:
@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {
@GetMapping("/pets/{petId}") public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
}
- Mostrar un URI a nivel de clase.
- Mostrar URI a nivel de método.
@Controller
@RequestMapping("/owners/{ownerId}")
class OwnerController {
@GetMapping("/pets/{petId}") fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
// ...
}
}
- Pantalla URI a nivel de clase.
- 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:
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\ \.[a-z]+}") public void handle(@PathVariable String version, @PathVariable String ext) {
// ...
}
@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.
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:
@PostMapping(path = "/pets", consumes = "application /json") public void addPet(@RequestBody Pet pet) {
// ...
}
@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:
@GetMapping(path = "/pets/{petId}", produces = "application/json" )
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
@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:
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue")
public void findPet (@PathVariable String petId) {
// ...
}
- Asegúrese de que
myParam
sea igual amyValue
.
@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) fun findPet(@PathVariable petId: String) {
// ...
}
- Asegúrese de que
myParam
sea igual amyValue
.
Lo mismo se puede utilizar con condiciones de encabezado de solicitud, como se muestra en el siguiente ejemplo:
@GetMapping(path = "/pets", headers = "myHeader=myValue") public void findPet( String petId){
// ...
}
- Asegúrese de que
myHeader
sea igual amyValue
.
@GetMapping("/pets", headers = ["myHeader=myValue"]) fun findPet(@PathVariable petId: String) {
// ... }
- Asegúrese de que
myHeader
sea igual amyValue
.
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:
@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);
}
}
- Inyectar el controlador de destino y mostrar el controlador para los controladores.
- Preparar los metadatos de visualización de la solicitud.
- Obtener el método del controlador.
- Agregar registro.
@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)
}
}
- Inyectar el controlador de destino y la asignación del controlador para los controladores.
- Preparar los metadatos de asignación de la solicitud.
- Obtener el método del controlador.
- 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 |
---|---|
|
Proporciona acceso al |
|
Proporciona acceso a un servidor HTTP solicitud o respuesta. |
|
Proporciona acceso a La sesión. Esto no inicia una nueva sesión a menos que se agreguen atributos. Admite tipos reactivos. |
|
El usuario autenticado actual - posiblemente una clase de implementación |
|
Método de solicitud HTTP. |
|
La configuración regional actual de la solicitud, determinada por el |
|
La zona horaria asociada con la solicitud actual, según lo determinado por |
|
Diseñado para proporcionar acceso a variables de plantilla URI. |
|
Diseñado para proporcionar acceso a pares nombre-valor en segmentos de ruta de URI. |
|
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 |
|
Diseñado para proporcionar acceso a los encabezados de solicitud. Los valores del encabezado se convierten al tipo declarado del argumento del método. |
|
Diseñado para proporcionar acceso a cookies. Los valores de las cookies se convierten al tipo de argumento del método declarado. |
|
Proporciona acceso al cuerpo de la solicitud HTTP. El contenido del cuerpo se convierte al tipo de argumento del método declarado utilizando instancias |
|
Diseñado para proporcionar acceso a los encabezados y al cuerpo de la solicitud. El cuerpo se representa utilizando instancias |
|
Proporciona acceso al componente en la solicitud |
|
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. |
|
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 |
|
Proporcionar acceso a errores de validación y enlace de datos para el objeto de comando, es decir. argumento |
|
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 |
|
Diseñado para preparar una URL asociada con el host, puerto, esquema y ruta de contexto de la solicitud actual. |
|
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 |
|
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 |
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 |
---|---|
|
El valor de retorno se codifica a través de instancias |
|
El valor de retorno especifica que la respuesta completa, incluidos los encabezados HTTP y el cuerpo, se codificarán mediante instancias |
|
Diseñado para devolver una respuesta con encabezados y sin cuerpo. |
|
Nombre de la vista a resolver por instances |
|
Una instancia de |
|
Atributos para agregar al modelo implícito, con el nombre de la vista determinado implícitamente en función de la ruta de solicitud. |
|
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 |
|
API para scripts para visualizar modelos y vistas. |
|
Se considera un método con un tipo de retorno de Si ninguno de los valores anteriores es verdadero, el tipo de retorno |
|
Se preocupa por la generación de eventos enviados por el servidor. La función contenedora |
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 |
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:
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
// 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:
// 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
}
@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:
// GET /pets/42
@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
// 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:
// 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]
}
// 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:
@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";
}
// ...
}
- Usando anotación
@RequestParam
.
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"
}
// ...
}
- Usando la anotación
@RequestParam
.
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
:
@GetMapping("/demo")
public void handle(
@RequestHeader("Accept-Encoding") String encoding, (1)
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
- Obtenga
Accept-Encoding
valores del encabezado. - Obtenga los valores del encabezado
Keep-Alive
.
@GetMapping("/demo")
fun handle(
@RequestHeader("Accept-Encoding") encoding: String, (1)
@RequestHeader("Keep-Alive") keepAlive: Long) {
//...
}
- Obtener los valores del código
Accept-Encoding
. - 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.
@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:
@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) {
//...
}
- Obteniendo el valor de la cookie.
@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) {
//...
}
- 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
:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) { }
- Enlazando el instancia
Pet
.
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String { }
- 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:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
- Agregando
BindingResult
.
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String {
if (result.hasErrors()) {
return "petForm"
}
// ...
}
- 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
:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
- Usando anotación
@Válido
para el argumento del atributo del modelo.
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String {
if (result.hasErrors()) {
return "petForm"
}
// ...
}
- 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:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
return petMono
.flatMap(pet -> {
// ...
})
.onErrorResume(ex -> {
// ...
});
}
@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:
@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
}
- Usando la anotación
@SessionAttributes
.
@Controller
@SessionAttributes("pet")
class EditPetForm {
// ...
}
- 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:
@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors()) {
// ...
}
status.setComplete();
// ...
}
}
}
- Usando el anotación
@SessionAttributes
. - Usando la variable
SessionStatus
.
@Controller
@SessionAttributes("pet")
class EditPetForm {
// ...
@PostMapping("/pets/{id}")
fun handle(pet: Pet, errors: BindingResult, status: SessionStatus): String {
if (errors.hasErrors()) {
// ...
}
status.setComplete()
// ...
}
}
- Usando la anotación
@SessionAttributes
. - 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:
@GetMapping("/")
public String handle(@SessionAttribute User user) {
// ...
}
- Utilice la anotación
@SessionAttribute
.
@GetMapping("/")
fun handle(@SessionAttribute user: User): String {
// ...
}
- 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:
@GetMapping("/")
public String handle(@RequestAttribute Client client) {
// ...
}
- Usando la anotación
@RequestAttribute
.
@GetMapping("/")
fun handle(@RequestAttribute client: Client): String {
// ...
}
- 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:
class MyForm {
private String name;
private MultipartFile file;
// ...
}
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
// ...
}
}
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:
@PostMapping("/")
public String handle(@RequestPart("meta-data") Part metadata,
@RequestPart("file-data") FilePart file) {
// ...
}
- Uso Anotación
@RequestPart
para obtener metadatos. - Utilice la anotación
@RequestPart
para obtener un archivo.
@PostMapping("/")
fun handle(@RequestPart("meta-data") Part metadata,
@RequestPart("file-data") FilePart file): String {
// ...
}
- Utilice la anotación
@RequestPart
para recuperar metadatos. - 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:
@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata) {
// ...
}
- Utilice la anotación
@RequestPart
para recuperar metadatos.
@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData): String {
// ...
}
- 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:
@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
// use uno de los onError*...
}
@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:
@PostMapping("/")
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) {
// ...
}
- Usando la anotación
@RequestBody
.
@PostMapping("/")
fun handle(@RequestBody parts: MultiValueMap<String, Part>): String {
// ...
}
- 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:
@PostMapping("/")
public String handle(@RequestBody Flux<Part> parts) {
// ...
}
- Usando la anotación
@RequestBody
.
@PostMapping("/")
fun handle(@RequestBody parts: Flow<Part>): String {
// ...
}
- 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
:
@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
// ...
}
@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.
@PostMapping("/accounts")
public void handle(@RequestBody Mono<Account> account) {
// ...
}
@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:
@PostMapping("/accounts")
public void handle(@Valid @RequestBody Mono<Account> account) {
// use uno de los onError*...
}}
@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
:
@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
// ...
}
@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:
@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
// ...
}
@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:
@GetMapping("/something")
public ResponseEntity<String> handle() {
String body = ... ;
String etag = ... ;
return ResponseEntity.ok().eTag(etag).body(body);
}
@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>>
oResponseEntity<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. UsamosMono
si el cuerpo consta de 0..1 valores, oFlux
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>>>
oMono<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:
@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;
}
}
@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
}
@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 medianteWebDataBinder
.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
:
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountRepository.findAccount(number));
// agregar más ...
}
@ModelAttribute
fun populateModel(@RequestParam number: String, model: Model) {
model.addAttribute(accountRepository.findAccount(number))
// agregar más ...
}
El siguiente ejemplo agrega solo un atributo:
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountRepository.findAccount(number);
}
@ModelAttribute
fun addAccount(@RequestParam number: String): Account {
return accountRepository.findAccount(number);
}
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:
@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) {
// ...
}
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:
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
public Account handle() {
// ...
return account;
}
@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
:
@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));
}
// ...
}
- Usando anotación
@InitBinder
.
@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:
@Controller
public class FormController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
// ...
}
- Agregue un formateador personalizado (en este caso
DateFormatter
).
@Controller
class FormController {
@InitBinder
fun initBinder(binder: WebDataBinder) {
binder.addCustomFormatter(DateFormatter("yyyy-MM-dd"))
}
// ...
}
- 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
.
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:
@Controller
public class SimpleController {
// ...
@ExceptionHandler
public ResponseEntity<String> handle(IOException ex) {
// ...
}
}
- Declaración de la anotación
@ExceptionHandler
.
@Controller
class SimpleController {
// ...
@ExceptionHandler
fun handle(ex: IOException): ResponseEntity<String> {
// ...
}
}
- 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.
@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.
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:
// 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 {}}
// 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
.
GO TO FULL VERSION