Spring Framework tiene integración incorporada para usar Spring MVC con JSP y JSTL.
View Resolvers
Al
desarrollar JSP, es común declarar un InternalResourceViewResolver
bean.
InternalResourceViewResolver
se puede utilizar para enviar a cualquier recurso de servlet, pero especialmente a JSP. Como práctica recomendada,
recomendamos encarecidamente colocar los archivos JSP en un directorio bajo el directorio "WEB-INF"
para que los clientes no puedan acceder a ellos directamente.
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
JSP frente a JSTL
Cuando utilice la biblioteca de etiquetas estándar JSP (JSTL), debe utilizar
un clase de vista especial JstlView
, ya que JSTL requiere cierta preparación para que funcionen cosas
como las funciones I18N.
Biblioteca de etiquetas JSP para Spring
Spring proporciona enlace del parámetro de solicitud comandos de datos a objetos como se describe en capítulos anteriores. Para facilitar el desarrollo de páginas JSP junto con estas funciones de enlace de datos, Spring proporciona varias etiquetas que facilitan aún más las cosas. Todas las etiquetas Spring tienen funciones de escape HTML que se pueden habilitar o deshabilitar para escapar de caracteres.
El descriptor de la biblioteca de etiquetas (TLD)
spring.tld
se incluye en spring-webmvc.jar
. Para obtener información completa sobre
etiquetas individuales, consulte Referencia de API o consulte la descripción de la biblioteca de etiquetas.
Biblioteca de etiquetas de formulario Spring
Desde la versión 2.0, Spring proporciona un conjunto completo de etiquetas que admiten el enlace de datos para trabajar con elementos de formulario cuando se utiliza JSP y Spring Web MVC. Cada etiqueta proporciona soporte para un conjunto de atributos de la etiqueta HTML correspondiente, lo que hace que el uso de las etiquetas sea familiar e intuitivo. El HTML generado por las etiquetas cumple con los estándares HTML 4.01/XHTML 1.0.
A diferencia de otras bibliotecas de etiquetas de formulario/entrada, la biblioteca de etiquetas de formulario para Spring está integrada con Spring Web MVC, lo que permite que las etiquetas tengan acceso a comandos. datos de objeto y de referencia con los que trabaja su responsable del tratamiento. Como se muestra en los siguientes ejemplos, las etiquetas de formulario hacen que los JSP sean más fáciles de desarrollar, leer y mantener.
Recorreremos las etiquetas de formulario y veremos un ejemplo de cómo usar cada etiqueta. Hemos incluido fragmentos HTML generados donde ciertas etiquetas requieren comentarios adicionales.
Configuración
Biblioteca de etiquetas de
formulario incluida desde spring-webmvc.jar
. El descriptor de la biblioteca se llama spring-form.tld
.
Para usar etiquetas de esta biblioteca, agregue la siguiente directiva en la parte superior de su página JSP:
<%@ taglib prefix="form" uri= "http://www.springframework.org/tags/form" %>
donde form
es el prefijo de el nombre de la etiqueta que desea utilizar para las etiquetas de
esta biblioteca.
Etiqueta de formulario
Esta etiqueta
representa el "formulario" HTML "elemento y expone la ruta de enlace a etiquetas internas para el anclaje. Coloca el
objeto de comando en un PageContext
para que se pueda acceder al objeto de comando mediante etiquetas
internas. Todas las demás etiquetas de esta biblioteca son subetiquetas de la etiqueta form
.
Supongamos que tenemos un objeto de dominio llamado User
. Es un JavaBean con propiedades como firstName
y lastName
. Podemos usarlo como objeto de formulario base de nuestro controlador de formulario, que
devuelve un archivo form.jsp
. El siguiente ejemplo muestra cómo podría verse form.jsp
:
<form:form>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
Los valores firstName
y lastName
se recuperan de el objeto de comando colocado en
PageContext
por el controlador de página. Continúe leyendo para ver ejemplos más avanzados del uso de
etiquetas internas con la etiqueta form
.
La siguiente lista muestra el HTML generado, que parece un formulario estándar:
<form method="POST">
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value="Harry"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value="Potter"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
El JSP anterior supone que el nombre de la variable del objeto de formulario subyacente es
command
.
Si colocó el objeto de formulario subyacente en el modelo con un nombre diferente (definitivamente la mejor opción),
puede vincular el formulario a una variable con nombre, como se muestra en el siguiente ejemplo:
<form:form modelAttribute="user">
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
La etiqueta input
Esta etiqueta representa el elemento HTML input
con un
valor enlazado y type='text'
de forma predeterminada. También puede utilizar tipos específicos de HTML5
como email
, tel
, date
y otros.
La etiqueta checkbox
Esta etiqueta representa una etiqueta HTML input
con type
establecido en
checkbox
.
Supongamos que nuestro User
tiene parámetros de usuario personales,
como una suscripción al boletín y una lista de pasatiempos. El siguiente ejemplo muestra la clase
Preferences
:
public class Preferences {
private boolean receiveNewsletter;
private String[] interests;
private String favouriteWord;
public boolean isReceiveNewsletter() {
return receiveNewsletter;
}
public void setReceiveNewsletter(boolean receiveNewsletter) {
this.receiveNewsletter = receiveNewsletter;
}
public String[] getInterests() {
return interests;
}
public void setInterests(String[] interests) {
this.interests = interests;
}
public String getFavouriteWord() {
return favouriteWord;
}
public void setFavouriteWord(String favouriteWord) {
this.favouriteWord = favouriteWord;
}
}
class Preferences(
var receiveNewsletter: Boolean,
var interests: StringArray,
var favouriteWord: String
)
El archivo form.jsp
correspondiente podría verse así:
<form:form>
<table>
<tr>
<td>Subscribe to newsletter?:</td>
<%-- Approach 1: The property is of type java.lang.Boolean --%>
<td><form:checkbox path="preferences.receiveNewsletter"/></td>
</tr>
<tr>
<td>Interests:</td>
<%-- Approach 2: The property is an array or has type java.util.Collection --%>
<td>
Quidditch: <form:checkbox path="preferences.interests" value="Quidditch"/>
Herbology: <form:checkbox path="preferences.interests" value="Herbology"/>
Defence Against the Dark Arts: <form:checkbox path="preferences.interests" value="Defence Against the Dark Arts"/>
</td>
</tr>
<tr>
<td>Favourite Word:</td>
<%-- Approach 3: The property is of type java.lang.Object --%>
<td>
Magic: <form:checkbox path="preferences.favouriteWord" value="Magic"/>
</td>
</tr>
</table>
</form:form>
Existen tres enfoques para la etiqueta checkbox
que deberían satisfacer todas sus necesidades de
casillas de verificación.
Enfoque uno: si el valor asociado es de tipo
java.lang.Boolean
,input(checkbox)
se marca comochecked
si el valor asociado estrue
. El atributovalue
corresponde al valor permitido de la propiedad de valorsetValue(Object)
.Enfoque dos: si el valor asociado es de tipo
array
ojava.util.Collection
,input(checkbox)
se marca comochecked
si el valor configurado essetValue( Object)
y está presente en laCollection
asociada.Enfoque tres: en caso de cualquier otro tipo de valor asociado
input(checkbox)
se marca comochecked
si elsetValue(Object)
configurado es igual al valor asociado.
Tenga en cuenta que, independientemente del enfoque, se genera la misma estructura HTML. El siguiente fragmento de HTML define varias casillas de verificación:
<tr>
<td>Interests:</td>
<td>
Quidditch: <input name="preferences.interests" type="checkbox" value="Quidditch"/>
<input type="hidden" value="1" name="_preferences.interests"/>
Herbology: <input name="preferences.interests" type="checkbox" value="Herbology"/>
<input type="hidden" value="1" name="_preferences.interests"/>
Defence Against the Dark Arts: <input name="preferences.interests" type="checkbox" value="Defence Against the Dark Arts"/>
<input type="hidden" value="1" name="_preferences.interests"/>
</td>
</tr>
Es posible que no esperes ver un campo oculto adicional después de cada casilla de verificación. Si una
casilla de verificación en una página HTML no está marcada, su valor no se envía al servidor como parte de los
parámetros de solicitud HTTP después de enviar el formulario, por lo que se necesita una solución alternativa para
esta característica HTML para que funcione el enlace de datos del formulario Spring. . La etiqueta
checkbox
sigue la convención existente de Spring de incluir un parámetro oculto con el prefijo de guión
bajo (_
) para cada casilla de verificación. Al hacer esto, efectivamente le estás diciendo a Spring que
"la casilla de verificación estaba visible en el formulario y quiero que el objeto al que están vinculados los datos
del formulario refleje el estado de la casilla de verificación, sin importar qué".
La etiqueta
casillas de verificación
Esta etiqueta representa múltiples etiquetas HTML input
con
tipo
establecido en casilla de checkbox
.
Esta sección se basa en el ejemplo de
la sección anterior sobre la etiqueta checkbox
. A veces es preferible no enumerar todos los posibles
pasatiempos en una página JSP. Es mejor especificar una lista de opciones disponibles en tiempo de ejecución y
pasarla a la etiqueta. Este es el propósito de la etiqueta checkboxes
. Puede pasar un
Array
, List
o Map
que contenga las opciones disponibles en la propiedad
items
. Normalmente, la propiedad vinculada es una colección, por lo que puede contener varios valores
seleccionados por el usuario. El siguiente ejemplo muestra una página JSP que utiliza esta etiqueta:
<form:form>
<table>
<tr>
<td>Interests:</td>
<td>
<%-- La propiedad es una matriz o es de tipo java.util.Collection --%>
<form:checkboxes path="preferences.interests" items="${interestList}"/>
</td>
</tr>
</table>
</form:form>
Este ejemplo supone que interestList
es una List
, disponible como un atributo de
modelo que contiene cadenas de valores para seleccionar. Si usa Map
, la clave de la entrada del Mapa se
usa como valor y el valor de la entrada del Mapa se usa como etiqueta para mostrar. También puede usar un objeto
personalizado en el que puede especificar nombres de propiedad para el valor usando itemValue
y la
etiqueta usando itemLabel
.
El etiqueta botón de radio
Esta etiqueta
representa un elemento HTML input
con type
establecido en radio
.
Un uso típico implica múltiples instancias de etiquetas vinculadas a la misma propiedad pero con diferentes valores, como se muestra en el siguiente ejemplo:
<tr>
<td>Sex:</td>
<td>
Male: <form:radiobutton path="sex" value="M"/> <br/>
Female: <form:radiobutton path="sex" value="F"/>
</td>
</tr>
La etiqueta radiobuttons
Esta etiqueta representa varios elementos HTML
input
con type
establecido en radio
.
Al igual que con la etiqueta checkboxes
,
puedes pasar la etiqueta disponible. opciones como una variable de tiempo de ejecución. Para hacer esto, puede usar
la etiqueta radiobuttons
. Pasa un Array
, List
o Map
que contiene
las opciones disponibles en la propiedad items
. Si usa Mapa, la clave de la entrada del Mapa se usa
como valor y el valor de la entrada del Mapa se usa como etiqueta para mostrar. También puede usar un objeto
personalizado donde puede especificar nombres de propiedad para un valor usando itemValue
y una
etiqueta usando itemLabel
, como se muestra en el siguiente ejemplo:
<tr>
<td>Sex:</td>
<td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
</tr>
La etiqueta contraseña
Esta etiqueta representa la entrada HTML input
con
tipo establecido en password
, con valor vinculado.
<tr>
<td>Password:</td>
<td>
<form:password path="password"/>
</td>
</tr>
Tenga en cuenta que, de forma predeterminada, no se muestra el valor de la contraseña. Si desea que se muestre
el valor de la contraseña, puede establecer el valor del atributo showPassword
en true
,
como se muestra en el siguiente ejemplo:
<tr>
<td>Password:</td>
<td>
<form:password path="password" value="^76525bvHGq" showPassword="true"/>
</td>
</tr>
La etiqueta select
Esta etiqueta representa el elemento HTML "select" . Admite el enlace
de datos a la opción seleccionada, así como el uso de etiquetas anidadas option
y options
.
Supongamos que el User
hay una lista de habilidades. El HTML correspondiente podría verse
así:
<tr>
<td>Skills:</td>
<td><form:select path="skills" items="${skills}"/></td>
</tr>
Si User
tiene habilidades en herbología, entonces la fuente HTML para la cadena "Habilidades"
podría verse así esto:
<tr>
<td>Skills:</td>
<td>
<select name="skills" multiple="true">
<option value="Potions">Potions</option>
<option value="Herbology" selected="selected">Herbology</option>
<option value="Quidditch">Quidditch</option>
</select>
</td>
</tr>
La etiqueta de
opción
Esta etiqueta representa la HTML elementooption
.
Establece selected
según el valor asociado. El siguiente HTML muestra un resultado típico:
<tr>
<td>House:</td>
<td>
<form:select path="house">
<form:option value="Gryffindor"/>
<form:option value="Hufflepuff"/>
<form:option value="Ravenclaw"/>
<form:option value="Slytherin"/>
</form:select>
</td>
</tr>
Si la casa de User
está en Gryffindor, entonces la fuente HTML para la cadena "Casa" se verá
así:
<tr>
<td>House:</td>
<td>
<select name="house">
<option value="Gryffindor" selected="selected">Gryffindor</option>
<option value="Hufflepuff">Hufflepuff</option>
<option value="Ravenclaw">Ravenclaw</option>
<option value="Slytherin">Slytherin</option>
</select>
</td>
</tr>
- Tenga en cuenta la adición del atributo
selected
La etiqueta options
Esta etiqueta muestra una lista de elementos HTML option
.
Establece el atributo selected
en función del valor asociado. El siguiente HTML muestra un resultado
típico:
<tr>
<td>Country:</td>
<td>
<form:select path="country">
<form:option value="-" label="--Please Select"/>
<form:options items="${countryList}" itemValue="code" itemLabel="name"/>
</form:select>
</td>
</tr>
Si User
viviera en el Reino Unido, la fuente HTML para la cadena "País" se vería así esto:
<tr>
<td>Country:</td>
<td>
<select name="country">
<option value="-">--Please Select</option>
<option value="AT">Austria</option>
<option value="UK" selected="selected">United Kingdom</option>
<option value="US">United States</option>
</select>
</td>
</tr>
- Tenga en cuenta la adición del atributo
selected
.
Como se muestra en el ejemplo anterior, combinar la etiqueta option
con la etiqueta
options
produce el mismo HTML estándar, pero le permite para especificar explícitamente el valor en
JSP, que está destinado únicamente a mostrarse (donde pertenece), como en el caso de la cadena predeterminada en el
ejemplo: "-- Seleccione".
El
items
generalmente se completan con una colección o matriz de objetos de elementos de datos. itemValue
y itemLabel
se refieren a las propiedades de bean de estos objetos de elemento, si se especifican.
De lo contrario, los propios objetos del elemento se convierten en cadenas. Alternativamente, puede especificar
un Map
de elementos, en cuyo caso las claves del Mapa se interpretarán como valores de opción y los
valores del Mapa corresponderán a etiquetas de opción. Si también se especifican itemValue
o itemLabel
(o ambos), entonces la propiedad de valor del elemento de datos se aplicará a la clave Mapa y la propiedad de
etiqueta del elemento de datos se aplicará a el valor del Mapa.
La etiqueta textarea
Esta etiqueta representa el elemento HTML textarea
. El
siguiente HTML muestra el resultado típico:
<tr>
<td>Notes:</td>
<td><form:textarea path="notes" rows="3" cols="20"/></td>
<td><form:errors path="notes"/></td>
</tr>
La etiqueta hidden
Esta etiqueta representa la entrada HTML input
con
type
establecida en hidden
, con un valor vinculado. Para enviar un valor oculto
independiente, utilice la etiqueta HTML input
con type
establecido en hidden
.
El siguiente HTML muestra el resultado típico:
<form:hidden path="house"/>
Si decide pasar el valor de house
como oculto, entonces el HTML se verá así:
<input nombre="casa" tipo="hidden" valor="Gryffindor"/>
errores
tag
Esta etiqueta muestra errores de campo en el elemento HTML
span
. Proporciona acceso a los errores generados en su controlador o a los errores generados por
cualquier validador asociado con su controlador.
Supongamos que queremos mostrar todos los mensajes de error
para los campos firstName
y lastName
después de enviar el formulario. Tenemos un
validador para instancias de la clase User
llamado UserValidator
, como se muestra en el
siguiente ejemplo:
public class UserValidator implements Validator {
public boolean supports(Class candidate) {
return User.class.isAssignableFrom(candidate);
}
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.");
}
}
class UserValidator : Validator {
override fun supports(candidate: Class<*>): Boolean {
return User::class.java.isAssignableFrom(candidate)
}
override fun validate(obj: Any, errors: Errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.")
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.")
}
}
form.jsp
podría verse así:
<form:form>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
<%-- Show errors for firstName field --%>
<td><form:errors path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
<%-- Show errors for lastName field --%>
<td><form:errors path="lastName"/></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
Si enviamos un formulario con valores vacíos en firstName
y lastName
código de
campos>, entonces el HTML se verá así:
<form method="POST">
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value=""/></td>
<%-- Associated errors to firstName field displayed --%>
<td><span name="firstName.errors">Field is required.</span></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value=""/></td>
<%-- Associated errors to lastName field displayed --%>
<td><span name="lastName.errors">Field is required.</span></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
¿Qué sucede si queremos mostrar la lista completa de errores de una página determinada? El siguiente ejemplo
muestra que la etiqueta errors
también admite algunas funciones de sustitución básicas.
path="*"
: Salidas a mostrar todos los errores.path="lastName"
: muestra todos los errores asociados con el campolastName
.Si se omite
path
, solo se imprimen los errores de objeto.
En el siguiente ejemplo, en La parte superior de la página muestra una lista de errores y junto a los campos hay errores específicos de cada campo:
<form:form>
<form:errors path="*" cssClass="errorBox"/>
<table>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
<td><form:errors path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
<td><form:errors path="lastName"/></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form:form>
HTML se verá así:
<form method="POST">
<span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span>
<table>
<tr>
<td>First Name:</td>
<td><input name="firstName" type="text" value=""/></td>
<td><span name="firstName.errors">Field is required.</span></td>
</tr>
<tr>
<td>Last Name:</td>
<td><input name="lastName" type="text" value=""/></td>
<td><span name="lastName.errors">Field is required.</span></td>
</tr>
<tr>
<td colspan="3">
<input type="submit" value="Save Changes"/>
</td>
</tr>
</table>
</form>
El descriptor de biblioteca de etiquetas (TLD) spring-form.tld
está incluido en spring-webmvc.jar
.
Para obtener información completa sobre etiquetas individuales, consulte Referencia de API o consulte la descripción de la biblioteca de etiquetas.
Traducción de métodos HTTP
El principio clave REST es el uso de una "interfaz unificada". Esto significa que
todos los recursos (URL) se pueden manipular utilizando los mismos cuatro métodos HTTP: GET, PUT, POST y DELETE.
Para cada método, la especificación HTTP define la semántica exacta. Por ejemplo, GET siempre debe ser una operación
segura, lo que significa que no tiene efectos secundarios, y PUT o DELETE deben ser idempotentes, lo que significa
que puedes repetir estas operaciones una y otra vez, pero el resultado final debe ser el mismo. Aunque HTTP define
estos cuatro métodos, HTML sólo admite dos de ellos: GET y POST. Afortunadamente, existen dos posibles soluciones:
puede usar JavaScript para realizar PUT o DELETE, o puede realizar un POST con un método "real" como parámetro
adicional (muy parecido a un campo de entrada oculto en un formulario HTML). El HiddenHttpMethodFilter
de Spring aprovecha este último truco. Este filtro es un filtro de servlet normal, por lo que se puede utilizar
junto con cualquier marco web (no solo Spring MVC). Agregue este filtro a su web.xml y una POST con un parámetro
method
oculto se convertirá en una solicitud para el método HTTP correspondiente.
Para convertir métodos HTTP, el la etiqueta de formulario Spring MVC se ha actualizado para brindar soporte para configurar el método HTTP. Por ejemplo, el siguiente fragmento está tomado del ejemplo de Pet Clinic:
<form:form method="delete">
<p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>
El ejemplo anterior ejecuta el método HTTP POST, mientras que el método DELETE "real" está oculto detrás de un
parámetro de solicitud. Lo recoge HiddenHttpMethodFilter
, que se define en web.xml, como se muestra en
el siguiente ejemplo:
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<servlet-name>petclinic</servlet-name>
</filter-mapping>
El siguiente ejemplo muestra el método correspondiente con la anotación @Controller
:
@RequestMapping(method =
RequestMethod.DELETE) public String eliminarPet(@PathVariable intownerId, @PathVariable int petId) {
this.clinic.deletePet(petId); devolver "redireccionamiento:/propietarios/" + ID de propietario; }
@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
this.clinic.deletePet(petId);
return "redirect:/owners/" + ownerId;
}
Etiquetas HTML5
La biblioteca de etiquetas de formulario Spring permite la entrada de atributos dinámicos, lo que significa que puede ingresar cualquier atributo específico de HTML5.
La etiqueta de formulario input
admite la entrada de atributos de tipo. diferente de
text
. Esto le permite visualizar nuevos tipos de entrada específicos de HTML5, como
email
, date
, range
y otros. Tenga en cuenta que ingresar
type='text'
no es obligatorio porque text
es el tipo predeterminado.