strust2运行流程分析
1.发送一个HttpServletRequest给StrutsPrepareAndExecuteFilter
2.StrutsPrepareAndExecuteFilter询问ActionMapper:该请求是否是一个Struts2请求(即是否返回一个非空的ActionMapping对象)
3.若ActionMapper认为该请求是一个Struts2请求,则StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy
4.ActionProxy通过ConfigurationManager询问框架的配置文件,确定需要调用的Action类及Action方法。
5.ActionProxy创建一个ActionInvocation的实例,并进行初始化。
6.ActionInvocation实例在调用Action的过程前后,涉及到相关拦截器(Interceptor)的调用。
7.Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。调用结果的execute方法,渲染结果。在渲染过程中可以使用Struts2框架中的标签。
8.执行各个拦截器invocation.invoke()之后的代码。
9.把结果发送到客户端。
Struts声明式验证
概述
一个健壮的web应用程序必须确保用户输入是合法,有效的,
Struts2的输入验证
--基于XWork Validation Framework的声明式验证:Struts2提供了一个基于XWork Validation Framework的内建验证程序,使用这些验证程序不需要编程,只要在一个XML文件里对验证程序应该如何工作做出声明就可以了,需要声明的内容包括:
.哪些字段需要进行验证。
.使用什么验证规则。
.在验证失败是应该把什么样的出错消息发送到浏览器端
--编写验证:通过编写代码来验证用户输入
struts2的验证
1)验证分为两种
--声明式验证
>>对哪个Action或Model的哪个字段进行验证
>>使用什么验证规则
>>如果验证失败,转向哪一个页面,显示什么错误消息
---编程式验证
2)声明式验证的示例
I.先明确对哪一个Action的哪一个字段进行验证:age
II.编写配置文件
>把struts-2.3.28.1-all\struts-2.3.28.1\apps\struts2-blank\struts2-blank\WEB-INF\src\java\example下的Login-validation.xml复制到当前Action所在的包下。
>把该配置文件改为:把Login改为当前Action的名字
>编写验证规则:参见struts-2.3.28.1-all\struts-2.3.28.1\docs\docs.home.html
>在配置文件中可以定义错误消息。
III.若验证失败则转向input的那个result,所以需要配置name=input的result
<result name="input">/validateion.jsp</result>
IV.如何显示错误消息?
1.若使用的是非simple主题,则自动显示在配置文件中可以定义错误消息的配置
2.若使用的是simple主题,需自己打印消息
<s:fielderror name="age"></s:fielderror>或 ${fieldErrors.age[0] }
3.该错误消息可以国际化吗?
可以, 在验证配置文件中写 <message key="error.int"></message>,
再在i18n.properties中写error.int=xxxxxxx
V.1.若一个Action的类多个action使用同样的验证规则:
ActionClassName-validation.xml
2.若一个Action类的多个action使用不同的验证规则:
ActionClass-allas-validation.xml ,例如UserAction-User.create(action的name属性值)-validation.xml
不带别名的配置文件ActionClassName-validation.xml中的验证规则依旧在起作用,可以把各个action公有的验证规则配置在其中,但需要注意的是。只适用某一个action的请求的验证规则就不要在这里配置了。
声明式验证框架的原理:
>struts2的默认的拦截器栈中提供了一个validation拦截器
>每个具体的验证规则都会对应具体的一个验证器,有一个配置文件把验证规则名和验证器关联起来了,而实际上验证的是那个验证器。
该文件位于com.opensymphony.xwork2.validator.validators.default.xml
Struts2内建的验证程序
.required:确保某个给定字段的值不是空值null
.requiredstring:确保某给定字段的值既不是空值null,也不是空白。
trim参数:默认为true,表示struts在验证字段值之前先剔除前后空格
.stringlength:验证一个非空的字段值是不是有足够的长度。
minLengthL:相关字段的最小长度,若没有给出这个参数,该字段将没有最小长度限制。
maxLengthL:相关字段的最大长度,若没有给出这个参数,该字段将没有最大长度限制。
trim参数:验证字段值之前是否剔除前后空格
.date:确保某给定日期字段的值落在一个给定的范围内
max:相关字段的最大值:若没有给出这个参数,该字段将没有最大限制
min:相关字段的最小值:若没有给出这个参数,该字段将没有最小限制
.email:检查给定String值是否是一个合法的email
.url:检查给定String值是否是一个合法的url
.regex:检查某给定字段的值是否与一个给定的正则表达式模式相匹配
expression*:用来匹配的正则表达式
caseSensitive:是否区分字母的大小写,默认为true
trim参数:默认为true,表示struts在验证字段值之前先剔除前后空格
.int:检查给定整数字段值是否在某一个范围内
max:相关字段的最大值:若没有给出这个参数,该字段将没有最大限制
min:相关字段的最小值:若没有给出这个参数,该字段将没有最小限制
.conversion:检查对给定Action属性进行的类型转换是否会导致一个转换错误,该验证程序还可以在默认的类型转换消息的基础上添加一条自定义的消息
.expression和fieldexpression:用来验证给定字段是否满足一个OGNL表达式
前者是一个非字段验证程序,后者是一个字段验证程序。
前者在验证失败时将生成一个action错误,而后者在验证失败时将会生成一个字段错误
expression*:用来进行验证的OGNL表达式
短路验证器
短路验证:若对一个字段使用多个验证器,默认情况下会执行所有的验证。若希望前面的验证器没有通过,后面的就不再验证,可以使用短路验证。
<validator.../>元素和<field-validator.../>元素可以指定一个可选的short-circuit属性,该属性指定该验证器是否是短路验证器,默认值为false。
对同一个字段内的多个验证器,如果一个短路验证器验证失败,其他验证器不会继续校验。
若类型转换失败,默认情况下,还会执行后面的拦截器,还会进行验证,可以通过修改ConversionErrorInterceptor源代码的方式使当类型转换失败时,不再执行后续的验证拦截器,而直接返回input的result。
Object action = invocation.getAction(); if (action instanceof ValidationAware) { ValidationAware va = (ValidationAware) action; if(va.hasFieldErrors()|| va.hasActionErrors()){ return "input"; } }
字段验证和非字段验证
关于非字段验证:不是针对某一字段的验证 <!-- 测试非字段验证 --> <validator type="expression"> <param name="expression"><![CDATA[password = password2]]></param> <message>Password id not equals to password2 </message> </validator>、 显示非字段验证的错误消息,使用s:actionerror标签:<s:actionerror/>
字段验证 VS 非字段验证
字段验证:字段优先,可以为一个字段配置多个验证规则
非字段验证:规则优先
大部分验证规则支持两种验证器,但个别的验证规则只能使用非字段验证,例如表达式验证
相同的验证规则使用同一条消息
错误消息的重用性
多个字段使用同样的验证规则,可否使用同一条验证消息?
可以,写法如下
error.int=${getText(fieldName)} needs to be between ${min} and ${max}
age=\u5E74\u9F84
count=\u6570\u91CF
范例:
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.custom.i18n.resources" value="i18n"></constant> <package name="default" namespace="/" extends="struts-default"> <action name="testValidateion" class="com.wul.app.TestValidationAction"> <result>/success.jsp</result> <!-- 若验证失败转向的input --> <result name="input">/validation.jsp</result> </action> <action name="testValidateion2" class="com.wul.app.TestValidationAction"> <result>/success.jsp</result> <!-- 若验证失败转向的input --> <result name="input">/validation2.jsp</result> </action> </package> </struts>
i18n.properties
#error.int=wul\u56FD\u9645\u5316:Age needs to be between ${min} and ${max} #error.countint=count needs to be between ${min} and ${max} #error.int=${fieldName} needs to be between ${min} and ${max} error.int=${getText(fieldName)} needs to be between ${min} and ${max} age=\u5E74\u9F84 count=\u6570\u91CF
ConversionErrorInterceptor.java
/* * 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.interceptor; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.ValidationAware; import com.opensymphony.xwork2.conversion.impl.XWorkConverter; import com.opensymphony.xwork2.util.ValueStack; import org.apache.commons.lang3.StringEscapeUtils; import java.util.HashMap; import java.util.Map; /** * <!-- START SNIPPET: description --> * ConversionErrorInterceptor adds conversion errors from the ActionContext to the Action's field errors. * * <p/> * This interceptor adds any error found in the {@link ActionContext}'s conversionErrors map as a field error (provided * that the action implements {@link ValidationAware}). In addition, any field that contains a validation error has its * original value saved such that any subsequent requests for that value return the original value rather than the value * in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to * display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to * the user). * * * <!-- END SNIPPET: description --> * * <p/> <u>Interceptor parameters:</u> * * <!-- START SNIPPET: parameters --> * * <ul> * * <li>None</li> * * </ul> * * <!-- END SNIPPET: parameters --> * * <p/> <u>Extending the interceptor:</u> * * <p/> * * <!-- START SNIPPET: extending --> * * Because this interceptor is not web-specific, it abstracts the logic for whether an error should be added. This * allows for web-specific interceptors to use more complex logic in the {@link #shouldAddError} method for when a value * has a conversion error but is null or empty or otherwise indicates that the value was never actually entered by the * user. * * <!-- END SNIPPET: extending --> * * <p/> <u>Example code:</u> * * <pre> * <!-- START SNIPPET: example --> * <action name="someAction" class="com.examples.SomeAction"> * <interceptor-ref name="params"/> * <interceptor-ref name="conversionError"/> * <result name="success">good_result.ftl</result> * </action> * <!-- END SNIPPET: example --> * </pre> * * @author Jason Carreira */ public class ConversionErrorInterceptor extends AbstractInterceptor { public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override"; protected Object getOverrideExpr(ActionInvocation invocation, Object value) { return escape(value); } protected String escape(Object value) { return "\"" + StringEscapeUtils.escapeJava(String.valueOf(value)) + "\""; } @Override public String intercept(ActionInvocation invocation) throws Exception { ActionContext invocationContext = invocation.getInvocationContext(); Map<String, Object> conversionErrors = invocationContext.getConversionErrors(); ValueStack stack = invocationContext.getValueStack(); HashMap<Object, Object> fakie = null; for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) { String propertyName = entry.getKey(); Object value = entry.getValue(); if (shouldAddError(propertyName, value)) { String message = XWorkConverter.getConversionErrorMessage(propertyName, stack); Object action = invocation.getAction(); if (action instanceof ValidationAware) { ValidationAware va = (ValidationAware) action; va.addFieldError(propertyName, message); } if (fakie == null) { fakie = new HashMap<Object, Object>(); } fakie.put(propertyName, getOverrideExpr(invocation, value)); } } if (fakie != null) { // if there were some errors, put the original (fake) values in place right before the result stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie); invocation.addPreResultListener(new PreResultListener() { public void beforeResult(ActionInvocation invocation, String resultCode) { Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE); if (fakie != null) { invocation.getStack().setExprOverrides(fakie); } } }); } ////修改添加////////// Object action = invocation.getAction(); if (action instanceof ValidationAware) { ValidationAware va = (ValidationAware) action; if(va.hasFieldErrors()|| va.hasActionErrors()){ return "input"; } } //////////////// return invocation.invoke(); } protected boolean shouldAddError(String propertyName, Object value) { return true; } }
TestValidationAction.java
package com.wul.app; import com.opensymphony.xwork2.ActionSupport; public class TestValidationAction extends ActionSupport { /** * */ private static final long serialVersionUID = 1L; private int age; private String password; private String password2; //private int count;//若想不回显 //改为 private Integer count; public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getPassword2() { return password2; } public void setPassword2(String password2) { this.password2 = password2; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String execute() throws Exception { System.out.println("age:"+age); return SUCCESS; } }
TestValidationAction-testValidateion-validation.xml
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd"> <validators> <!-- 针对age属性进行验证,基于字段的验证 --> <field name="age"> <field-validator type="int"> <param name="min">20</param> <param name="max">50</param> <!--<message>wul:Age needs to be between ${min} and ${max}</message> --> <message key="error.int"></message> </field-validator> </field> </validators>
TestValidationAction-testValidateion2-validation.xml
<!DOCTYPE validators PUBLIC "-//Apache Struts//XWork Validator 1.0.2//EN" "http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd"> <validators> <!-- 针对age属性进行验证,基于字段的验证 --> <field name="age"> <!-- 设置短路验证:若当前验证没有通过,则不再进行下面的验证 --> <field-validator type="conversion" short-circuit="true"> <message>Conversion Error Occurred</message> </field-validator> <field-validator type="int"> <param name="min">1</param> <param name="max">130</param> <!--<message>wul:Age needs to be between ${min} and ${max}</message> --> <message key="error.int"></message> </field-validator> </field> <field name="count"> <field-validator type="int"> <param name="min">1</param> <param name="max">10</param> <!-- <message key="error.countint"></message>--> <message key="error.int"></message> </field-validator> </field> <!-- 测试非字段验证 --> <validator type="expression"> <param name="expression"><![CDATA[password = password2]]></param> <message>Password id not equals to password2 </message> </validator> </validators>
validation.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <body> <s:debug></s:debug> <!-- 要求年龄必须在20-50之间 --> <s:form action="testValidateion" theme="simple"> <s:textfield name="age" label="Age"></s:textfield> <s:fielderror name="age"></s:fielderror> ${fieldErrors.age[0] } <s:submit></s:submit> </s:form> </body> </html>
validation2.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <body> <s:debug></s:debug> <s:actionerror/> <!-- 要求年龄必须在1-130之间 --> <s:form action="testValidateion2" theme="simple"> <s:textfield name="age" label="Age"></s:textfield> <s:fielderror fieldName="age"></s:fielderror> <s:password name="password" label="password"></s:password> <s:password name="password2" label="password2"></s:password> <s:textfield name="count" label="Count"></s:textfield> <s:fielderror fieldName="count"></s:fielderror> <s:submit></s:submit> </s:form> </body> </html>
success.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <body> success </body> </html>