26/08/2013
En el presente tip vamos a comprobar cómo funcionan dos de los result que por defecto tenemos disponibles y que nos permiten navegar o pasar de un Action a otro sin la interacción del usuario. Estos result son dos:
- redirectAction: es una versión específica del result «redirect» que permite redireccionar al usuario hacia otro action de nuestra aplicación. La redirección implica la creación de un nuevo request por lo que perderemos los datos asociados al mismo. Si queremos pasar parámetros al siguiente action podemos hacerlo con la etiqueta «param» tal y como veremos.
La url que se mostrará el usuario es la del action hacia el cual se hace la redirección y esto tiene una implicación práctica muy importante: si el usuario refresca el navegador será esta url la que se invoque y no la primera que inició la ejecución sucesiva de los actions. - chain: ejecuta el action indicado «dentro» del servidor, por lo que no hay redirección alguna y el request es el mismo en todos los actions que ejecutemos en cadena. Asimismo, el ValueStack se conserva en todos los actions y tras ejecutarse la cadena el action que aparecerá en la url es el primero que se invocó.
Vamos a comprobar este funcionamiento mediante un ejemplo muy sencillo basado en los ejemplos que vimos en el primer y segundo tip. Este ejemplo constará de un action que invocará a otro según los dos tipos de result que se han comentado. En primer lugar, tendremos un form.jsp con dos botones para indicar al Action si tras el submit queremos ejecutar el segundo Action mediante un chain o una redirección:
<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <%@include file="/jsp/head.jsp" %> </head> <body> <s:form action="primerAction" namespace="/"> <s:textfield name="campo1" key="label1" /> <s:textfield name="campo2" key="label2" /> <s:submit key="redirectAction" name="boton" value="redirectAction"/> <s:submit key="chain" name="chain" value="chain"/> </s:form> <%@include file="/jsp/footer.jsp" %> </body> </html>
Estos serán los dos action, obsérvese que contienen los mismos atributos para poder comprobar cómo se pasan de uno a otro.
package com.danielme.tips.struts2.actions; import com.opensymphony.xwork2.ActionSupport; /** * * @author danielme.com * */ public class PrimerAction extends ActionSupport { private static final long serialVersionUID = 1L; private String campo1; private String campo2; private String boton; public String execute() { return SUCCESS; } public String redirectAction() { return "redirect"; } public String chain() { return "chain"; } public String getCampo1() { return campo1; } public void setCampo1(String campo1) { this.campo1 = campo1; } public String getCampo2() { return campo2; } public void setCampo2(String campo2) { this.campo2 = campo2; } public String getBoton() { return boton; } public void setBoton(String boton) { this.boton = boton; } }
package com.danielme.tips.struts2.actions; import com.opensymphony.xwork2.ActionSupport; /** * * @author danielme.com * */ public class SegundoAction extends ActionSupport { private static final long serialVersionUID = 1L; private String campo1; private String campo2; public String execute() { return SUCCESS; } public String getCampo1() { return campo1; } public void setCampo1(String campo1) { this.campo1 = campo1; } public String getCampo2() { return campo2; } public void setCampo2(String campo2) { this.campo2 = campo2; } }
Lo más interesante es la configuración de los action. Tal y como se comentado, para pasar parámetros al nuevo action usando un redirectAction usaremos la etiqueta param. Este el contenido de struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.devMode" value="true" /> <constant name="struts.custom.i18n.resources" value="global" /> <package name="default" namespace="/" extends="struts-default"> <action name="inicio" class="com.danielme.tips.struts2.actions.PrimerAction"> <result name="success">/jsp/form.jsp</result> </action> <action name="primerAction" class="com.danielme.tips.struts2.actions.PrimerAction"> <result name="redirect" type="redirectAction"> <param name="actionName">segundoAction</param> <param name="campo1">${campo1}</param> <param name="campo2">${campo2}</param> </result> <result name="chain" type="chain">segundoAction</result> </action> <action name="segundoAction" class="com.danielme.tips.struts2.actions.SegundoAction"> <result name="success">/jsp/resultado.jsp</result> </action> </package> </struts>
Por último, la jsp que nos mostrará que el paso de parámetros es correcto
<%@ page contentType="text/html; charset=UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <%@include file="/jsp/head.jsp"%> </head> <body> <br> <s:text name="label1"/>: <s:property value="campo1" /> <br> <s:text name="label2"/>: <s:property value="campo2"/> <%@include file="/jsp/footer.jsp"%> </body> </html>
El aspecto del formulario es el siguiente:
Si rellenamos alguno de los campos y enviamos el formulario como resultado veremos los valores introducidos en el mismo independientemente del result que se haya empleado. La principal diferencia es que al hacer un redirectAction los parámetros son enviados mediante GET y los veremos en la url del navegador, por lo que si queremos ocultarlos no nos quedará más remedio que guardarlos en la sesión y tener cuidado con esta chapucilla.
Aunque es un mecanismo proporcionado por Struts 2, hacer cadenas entre Actions responde en la mayoría de los casos a un mal diseño ya que probablemente el código «encadenado» pueda abstraerse en una superclase, en la capa de negocio o incluso dentro del mismo Action si la cadena se realiza a un Action que no es más que un método de la misma clase. De hecho, desde Struts 2 desaconsejan directamente el uso del chain por poder dar lugar a «spaghetti code» (ver la documentación). El caso del redirectAction es distinto, y lo necesitaremos por ejemplo cuando ejecutemos una action «eliminarRegistro» y tras la operación de eliminación queremos redireccionar al usuario a otra pantalla y que la url mostrada no sea «eliminarRegistro» para evitar problemas al refrescar la página.
El proyecto completo para Maven se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.
En struts.xml, en la declaración del action «inicio» no está sobrando [class=»com.danielme.tips.struts2.actions.PrimerAction»] ?
Agradecería su respuesta, gracias
Efectivamente, si no se define el class se utiliza ActionSupport como action por defecto y su execute simplemente ejecuta el result SUCCESS. En todos los ejemplos del blog defino y creo todos los action para que ejemplo quede más claro.
Excelente post, me ayudó a poder seguir con mi proyecto. Muchas gracias!!!
Me aparece un error en el web.xml al copiar el código de struts. Alguna solución??