struts2中ActionSupport类具有验证表单的功能,可以将一些错误信息显示在jsp页面上。
ActionSupport的父类是Object,实现了六个接口,分别是:Action,LocalProvider,TextProvider,Validateble,ValidateAware,Serializable。
由于ActionSupport类实现了Action接口,可以确定ActionSupport拥有Action的5个常量值,在此基础上ActionSupport对功能进行了许多拓展,如具有验证功能。
我们先找一下validateble接口,点开Web App libraries 下的 xwork-core jar包
双击之后把源代码添加进去:
代码如下:
/* * Copyright 2002-2006,2009 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.opensymphony.xwork2; import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.util.ValueStack; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import java.io.Serializable; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; /** * Provides a default implementation for the most common actions. * See the documentation for all the interfaces this class implements for more detailed information. */ public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable { protected static Logger LOG = LoggerFactory.getLogger(ActionSupport.class); private final ValidationAwareSupport validationAware = new ValidationAwareSupport(); private transient TextProvider textProvider; private Container container; public void setActionErrors(Collection<String> errorMessages) { validationAware.setActionErrors(errorMessages); } public Collection<String> getActionErrors() { return validationAware.getActionErrors(); } public void setActionMessages(Collection<String> messages) { validationAware.setActionMessages(messages); } public Collection<String> getActionMessages() { return validationAware.getActionMessages(); } /** * @deprecated Use {@link #getActionErrors()}. */ @Deprecated public Collection<String> getErrorMessages() { return getActionErrors(); } /** * @deprecated Use {@link #getFieldErrors()}. */ @Deprecated public Map<String, List<String>> getErrors() { return getFieldErrors(); } public void setFieldErrors(Map<String, List<String>> errorMap) { validationAware.setFieldErrors(errorMap); } public Map<String, List<String>> getFieldErrors() { return validationAware.getFieldErrors(); } public Locale getLocale() { ActionContext ctx = ActionContext.getContext(); if (ctx != null) { return ctx.getLocale(); } else { if (LOG.isDebugEnabled()) { LOG.debug("Action context not initialized"); } return null; } } public boolean hasKey(String key) { return getTextProvider().hasKey(key); } public String getText(String aTextName) { return getTextProvider().getText(aTextName); } public String getText(String aTextName, String defaultValue) { return getTextProvider().getText(aTextName, defaultValue); } public String getText(String aTextName, String defaultValue, String obj) { return getTextProvider().getText(aTextName, defaultValue, obj); } public String getText(String aTextName, List<?> args) { return getTextProvider().getText(aTextName, args); } public String getText(String key, String[] args) { return getTextProvider().getText(key, args); } public String getText(String aTextName, String defaultValue, List<?> args) { return getTextProvider().getText(aTextName, defaultValue, args); } public String getText(String key, String defaultValue, String[] args) { return getTextProvider().getText(key, defaultValue, args); } public String getText(String key, String defaultValue, List<?> args, ValueStack stack) { return getTextProvider().getText(key, defaultValue, args, stack); } public String getText(String key, String defaultValue, String[] args, ValueStack stack) { return getTextProvider().getText(key, defaultValue, args, stack); } /** * Dedicated method to support I10N and conversion errors * * @param key message which contains formatting string * @param expr that should be formatted * @return formatted expr with format specified by key */ public String getFormatted(String key, String expr) { Map<String, Object> conversionErrors = ActionContext.getContext().getConversionErrors(); if (conversionErrors.containsKey(expr)) { String[] vals = (String[]) conversionErrors.get(expr); return vals[0]; } else { final ValueStack valueStack = ActionContext.getContext().getValueStack(); final Object val = valueStack.findValue(expr); return getText(key, Arrays.asList(val)); } } public ResourceBundle getTexts() { return getTextProvider().getTexts(); } public ResourceBundle getTexts(String aBundleName) { return getTextProvider().getTexts(aBundleName); } public void addActionError(String anErrorMessage) { validationAware.addActionError(anErrorMessage); } public void addActionMessage(String aMessage) { validationAware.addActionMessage(aMessage); } public void addFieldError(String fieldName, String errorMessage) { validationAware.addFieldError(fieldName, errorMessage); } public String input() throws Exception { return INPUT; } public String doDefault() throws Exception { return SUCCESS; } /** * A default implementation that does nothing an returns "success". * <p/> * Subclasses should override this method to provide their business logic. * <p/> * See also {@link com.opensymphony.xwork2.Action#execute()}. * * @return returns {@link #SUCCESS} * @throws Exception can be thrown by subclasses. */ public String execute() throws Exception { return SUCCESS; } public boolean hasActionErrors() { return validationAware.hasActionErrors(); } public boolean hasActionMessages() { return validationAware.hasActionMessages(); } public boolean hasErrors() { return validationAware.hasErrors(); } public boolean hasFieldErrors() { return validationAware.hasFieldErrors(); } /** * Clears field errors. Useful for Continuations and other situations * where you might want to clear parts of the state on the same action. */ public void clearFieldErrors() { validationAware.clearFieldErrors(); } /** * Clears action errors. Useful for Continuations and other situations * where you might want to clear parts of the state on the same action. */ public void clearActionErrors() { validationAware.clearActionErrors(); } /** * Clears messages. Useful for Continuations and other situations * where you might want to clear parts of the state on the same action. */ public void clearMessages() { validationAware.clearMessages(); } /** * Clears all errors. Useful for Continuations and other situations * where you might want to clear parts of the state on the same action. */ public void clearErrors() { validationAware.clearErrors(); } /** * Clears all errors and messages. Useful for Continuations and other situations * where you might want to clear parts of the state on the same action. */ public void clearErrorsAndMessages() { validationAware.clearErrorsAndMessages(); } /** * A default implementation that validates nothing. * Subclasses should override this method to provide validations. */ public void validate() { } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * <!-- START SNIPPET: pause-method --> * Stops the action invocation immediately (by throwing a PauseException) and causes the action invocation to return * the specified result, such as {@link #SUCCESS}, {@link #INPUT}, etc. * <p/> * <p/> * The next time this action is invoked (and using the same continuation ID), the method will resume immediately * after where this method was called, with the entire call stack in the execute method restored. * <p/> * <p/> * Note: this method can <b>only</b> be called within the {@link #execute()} method. * <!-- END SNIPPET: pause-method --> * * @param result the result to return - the same type of return value in the {@link #execute()} method. */ public void pause(String result) { } /** * If called first time it will create {@link com.opensymphony.xwork2.TextProviderFactory}, * inject dependency (if {@link com.opensymphony.xwork2.inject.Container} is accesible) into in, * then will create new {@link com.opensymphony.xwork2.TextProvider} and store it in a field * for future references and at the returns reference to that field * * @return reference to field with TextProvider */ private TextProvider getTextProvider() { if (textProvider == null) { TextProviderFactory tpf = new TextProviderFactory(); if (container != null) { container.inject(tpf); } textProvider = tpf.createInstance(getClass(), this); } return textProvider; } @Inject public void setContainer(Container container) { this.container = container; } }
Validateable接口只有一个方法,即validate(),必须覆盖该方法来实现自定义的验证功能。
下面将实现继承自ActionSupport类的实例,是action控制层具有验证功能。
将上一节中的Login.java添加validate方法,具体代码如下:
package controller; import com.opensymphony.xwork2.ActionSupport; public class Login extends ActionSupport{ private String username; public String getUsername() { return username; } public void setUsername(String username) { System.out.println("username setting"); this.username = username; } public void setPassword(String password) { System.out.println("password setting"); this.password = password; } public String getPassword() { return password; } private String password; @Override public void validate() { // TODO Auto-generated method stub super.validate(); if("".equals(this.getUsername())) { this.addFieldError("usernname", "用户名不能为空"); } if("".equals(this.getPassword())) { this.addFieldError("password", "密码不能为空"); } } public String execute() { if(username.equals("hello")&&password.equals("struts2")) { return "success"; } else { return "false"; } } }
validate方法是在execute方法之前执行的。
如果表单的内容为空,则执行this.addFieldError()方法:
this.addFieldError("usernname", "用户名不能为空"); this.addFieldError("password", "密码不能为空");
将出错的信息显示在jsp页面名称为username和password的s:textfield标签中,然后就可以查看login.jsp页面的变化:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <body> <form action="login.action" method="post" name="login"> <s:textfield name="username" label="账号"></s:textfield> <s:textfield name=password“" label="密码"></s:textfield> <br> <s:submit value="提交"></s:submit> </form> </body> </html>
在新的jsp页面中使用了struts2表单标签库,s:textfield是在浏览器显示单行文本域,而s:textfield name的属性值一定要和Login.java类中的属性名相同,这样才可以上实现自动填充,label属性是标签内的文本提示内容。
最后还需要配置struts.xml文件,并显示错误提示,等待重新输入:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtd/struts-2.0.dtd"> <struts> <package name="Maybe" extends="struts-default"> <action name="login" class="controller.Login"> <result name="success">/true.jsp</result> <result name="false">/false.jsp</result> <result name="input">/login.jsp</result> </action> </package> </struts>
其中result name=“inout”是当出现错误自动转到重新输入的状态。
我们来查看一下Action这个类:
/* * Copyright 2002-2007,2009 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.opensymphony.xwork2; /** * All actions <b>may</b> implement this interface, which exposes the <code>execute()</code> method. * <p/> * However, as of XWork 1.1, this is <b>not</b> required and is only here to assist users. You are free to create POJOs * that honor the same contract defined by this interface without actually implementing the interface. */ public interface Action { /** * The action execution was successful. Show result * view to the end user. */ public static final String SUCCESS = "success"; /** * The action execution was successful but do not * show a view. This is useful for actions that are * handling the view in another fashion like redirect. */ public static final String NONE = "none"; /** * The action execution was a failure. * Show an error view, possibly asking the * user to retry entering data. */ public static final String ERROR = "error"; /** * The action execution require more input * in order to succeed. * This result is typically used if a form * handling action has been executed so as * to provide defaults for a form. The * form associated with the handler should be * shown to the end user. * <p/> * This result is also used if the given input * params are invalid, meaning the user * should try providing input again. */ public static final String INPUT = "input"; /** * The action could not execute, since the * user most was not logged in. The login view * should be shown. */ public static final String LOGIN = "login"; /** * Where the logic of the action is executed. * * @return a string representing the logical result of the execution. * See constants in this interface for a list of standard result values. * @throws Exception thrown if a system level exception occurs. * <b>Note:</b> Application level exceptions should be handled by returning * an error value, such as <code>Action.ERROR</code>. */ public String execute() throws Exception; }
可以看到在Action中定义了许多常量字符串INPUT就是其中之一,如果运行了Login.java中的validate方法,addFieldError方法就会返回到login.jsp页面,显示出错误信息。
运行一下,若不输入用户名和密码,运行结果如图所示: