|
Recipe 9.5. Reporting Errors and Messages from an ActionProblemYou want to display error messages to the user when things go wrong and success messages when things go right. SolutionFor Struts 1.1, use ActionErrors for reporting errors and ActionMessages for informational messages. For Struts 1.2, use ActionMessages for informational messages and errors. DiscussionStruts gives you a least four classes and three custom tags for creating, storing, and displaying errors and messages. In Struts 1.1, the ActionErrors class is used to hold a collection of errors, represented by ActionError instances. Each ActionError consists of a key to be looked up from the default MessageResources bundle and an optional set of Objects (up to four) to be used as parameters for message substitution. The Action class in Example 9-9 shows typical usage of ActionErrors. Example 9-9. Creating ActionErrors from an Actionpackage com.oreilly.strutsckbk.ch09; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.PropertyUtils; import org.apache.struts.Globals; import org.apache.struts.action.Action; import org.apache.struts.action.ActionError; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class RegistrationAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ActionErrors errors = new ActionErrors( ); String username = (String) PropertyUtils.getSimpleProperty( form, "username"); String password = (String) PropertyUtils.getSimpleProperty( form, "password"); if (username.equals(password)) { ActionError error = new ActionError( "error.register.sameAsPassword", username); // Use Action Message with Struts 1.2 //ActionMessage error = new ActionMessage( // "error.register.sameAsPassword", username); errors.add("username", error); } if (!errors.isEmpty( )) { saveErrors(request, errors); return mapping.getInputForward( ); } User user = new User( ); user.setUsername(username); user.setPassword(password); SecurityService service = new SecurityService( ); service.add(user); return mapping.findForward("success"); } } If you need to create informational messages, use the ActionMessages and ActionMessage classes in a similar fashion, as shown in Example 9-10. Example 9-10. Creating ActionMessages from an Actionpackage com.oreilly.strutsckbk.ch09; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.PropertyUtils; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; public class LoginAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ActionMessages messages = new ActionMessages( ); String username = (String) PropertyUtils.getSimpleProperty( form, "username"); String password = (String) PropertyUtils.getSimpleProperty( form, "password"); SecurityService service = new SecurityService( ); service.authenticate( username, password); User user = new User( ); user.setUsername(username); request.getSession( ).setAttribute("user", user); ActionMessage message = new ActionMessage( "message.login.success", username); messages.add(ActionMessages.GLOBAL_MESSAGE, message); if (!messages.isEmpty( )) { saveMessages( request, messages ); } return mapping.findForward("success"); } } Though you'll find little difference in the APIs you use to create errors or messages, the approaches for rendering errors or messages can differ. You can display all errors on a JSP page this way: <html:errors/> You would think you could do the same thing with the html:messages tag, but the html:messages tag behaves differently than the html:errors tag. By default, the html:messages tag retrieves the values stored as errorsnot messagesfrom the HttpServletRequest. You must explicitly set the message attribute to true to retrieve values stored as messages.
Unlike the html:errors tag, the html:messages tag is a looping tag, like logic:iterate. The tag iterates through ActionErrors, ActionMessages, or an array of strings. The required id attribute defines a page-scoped variable that contains the formatted message. You can use the bean:write or the JSTL c:out tag to display each message: <html:messages message="true" > <c:out value="${msg}"/><br /> </html:messages> Unlike the html:errors tag, the html:messages tag doesn't use a predefined header and footer for display. The presentation details are left to you. Recipe 9.6 shows you some ways to create custom presentation for your errors and messages. What if you are using Struts 1.2? Well, the ActionError class has been deprecated in favor of ActionMessage in Struts 1.2. On the other hand, ActionErrors has not been deprecated; however, in most places you can use an ActionMessages object where you used to use ActionErrors.
So, how do you identify errors versus messages? It depends on the attribute name that they are stored under in the request. The saveErrors( ) method now accepts ActionMessages instead of ActionErrors; yet it stores the object in the request as errors using the key identified by the constant org.apache.struts.Globals.ERROR_KEY. The saveMessages( ) method accepts ActionMessages. It stores the object in the request using the key identified by the org.apache.struts.Globals.MESSAGE_KEY constant. The html:errors and html:messages tags are used in Struts 1.2 just like in Struts 1.1. If you have ever needed to store messages HTTP session, you can use a new method, added in Struts 1.2, of the base Action class: saveMessages(HttpSession session, ActionMessages messages); This method was added to provide a workaround for message display when the rendering page is the result of a redirect instead of a forward. When the messages are saved in the servlet request, the JSP page that displays those messages must be the result of a forward; if the JSP were redirected to the messages would be lost. With the new saveMessages( ) method, messages are saved in the session and are therefore available whether the page is the result of a forward or a redirect.
Now that the messages are saved in the session, what's to prevent them from showing up on a page where they shouldn't? The Struts developers anticipated this problem. The ActionMessages class provides the isAccessed( ) method to indicate if the messages have been displayed (accessed) or not. The RequestProcessor, in the new processCachedMessages( ) method, checks this value and removes any messages stored in the session that have been accessed. So, though you store the messages in the session, they will be cleaned on the next request after they have been displayed. Here's how you would use the new saveMessages method in the Action shown in Example 9-10: if (!messages.isEmpty( )) { saveMessages( request.getSession(true), messages ); } You can change the forward for the success page to a redirect: <forward name="success" path="/login_success.jsp" redirect="true"/> Your messages will still be available on the success page, and you don't have to worry about double postings. See AlsoStruts 1.2 introduced the ability to have session-scoped action messages. The Struts 1.2 release notes at http://struts.apache.org/userGuide/release-notes.html describe this feature. You can use the html:messages tag to render errors or messages; see Recipe 9.6 for additional details on this and other related tags. |
|