zoukankan      html  css  js  c++  java
  • struts2

    jsp

       JSP要经过两次的“编码”,第一阶段会用pageEncoding,第二阶段会用utf-8的.Java文件至utf-8的.class文件,第三阶段就是由Tomcat传回浏览器的网页, 用的是contentType。

      可以简单认为是,pageEncoding是jsp文件本身的编码;contentType的charset是指服务器发送给客户端时的内容编码。例如:pageEncoding="GBK"。这句话的意思是,告诉JVM 这个jsp本身采用的"GBK"编码,在JSP编译成Servlet传给JVM的时候,就用“GBK”的编码方式将Jsp网页源文件翻译成统一的UTF-8形式的Java字节码。如果不加设定,则JVM默认的用ISO-8859-1这种编码方式。contentType里的charset=gbk,指的是此网页文件输出到浏览器的输出方式为gbk。在这个过程中,一个JSP的源文件需要经过三个阶段,两次编码,才能完成一次完整的输出。

      第一阶段:将jsp编译成Servlet(.java)文件。用到的指令是pageEncoding,根据pageEncoding=“XXX”的指示,找到编码的规则为“XXX”,服务器在将JSP文件编译成.java文件时会根据pageEncoding的设定读取jsp,结果是由指定的编码方案翻译成统一的UTF-8编码的JAVA源码(即.java)。
      第二阶段:从Servlet文件(.java)到Java字节码文件(.class),从UTF-8到UTF-8。在这一阶段中,不论JSP编写时候用的是什么编码方案,经过这个阶段的结果全部是UTF-8的encoding的java源码。JAVAC用UTF-8的encoding读取java源码,编译成UTF-8编码的二进制码(即.class),这是JVM对常数字串在二进制码(Java encoding)内表达的规范。这一过程是由JVM的内在规范决定的,不受外界控制。
      第三阶段:从服务器到浏览器,这在一过程中用到的指令是contentType。服务器载入和执行由第二阶段生成出来JAVA二进制码,输出的结果,也就是在客户端可见到的结果,在这次输出过程中,由contentType属性中的charset来指定,将UTF8形式的二进制码以charset的编码形式来输出。如果没有人为设定,则默认的是ISO-8859-1的形式。

      可见,pageEncoding和contentType都可以设置JSP源文件和响应正文中的字符集编码。但也有区别:
      设置JSP源文件字符集时,优先级为pageEncoding>contentType。如果都没有设置,默认ISO-8859-1。
      设置响应输出的字符集时,优先级为contentType>pageEncoding。如果都没有设置,默认ISO-8859-1。

    struts2

    一,自定义类型转化器,只有在表单提交到对应字段和对应字段在回显时才会起作用。

      转换器中报异常的话并不会返回到input的result,而只有出现error时才会

    二,验证器,只要Action使用到了对应的字段,就回去验证,包活在表单提交时。

      1.modelDriver拦截器的拦截过程(若栈顶对象为null,那么栈顶对象依旧是Action类对象)

        @Override
        public String intercept(ActionInvocation invocation) throws Exception {
            Object action = invocation.getAction();
    
            if (action instanceof ModelDriven) {
                ModelDriven modelDriven = (ModelDriven) action;
                ValueStack stack = invocation.getStack();
                Object model = modelDriven.getModel();
                if (model !=  null) {
                    stack.push(model);
                }
                if (refreshModelBeforeResult) {
                    invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
                }
            }
            return invocation.invoke();
        }

       2.ExceptionMappingInterceptor

        声明式异常处理。

        在struts.xml中<action>节点下<exception-mapping result="input" exception="java.lang.RuntimeException"></exception-mapping>

        或者在<package>节点下声明整个包的异常映射

          <global-exception-mappings>
            <exception-mapping result="" exception=""></exception-mapping>
          </global-exception-mappings>

        那么若在exception拦截器之后的执行过程中出现<exception-mapping>中配置的异常,那么直接将结果转到对应配置的result。并将ExceptionHolder对象放到栈顶

        拦截器的拦截方法为下

        @Override
        public String intercept(ActionInvocation invocation) throws Exception {
            String result;
    
            try {
                result = invocation.invoke();
            } catch (Exception e) {
                if (isLogEnabled()) {
                    handleLogging(e);
                }
                List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
                ExceptionMappingConfig mappingConfig = this.findMappingFromExceptions(exceptionMappings, e);
                if (mappingConfig != null && mappingConfig.getResult()!=null) {
                    Map parameterMap = mappingConfig.getParams();
                    // create a mutable HashMap since some interceptors will remove parameters, and parameterMap is immutable
                    invocation.getInvocationContext().setParameters(new HashMap<String, Object>(parameterMap));
                    result = mappingConfig.getResult();
                    publishException(invocation, new ExceptionHolder(e));
                } else {
                    throw e;
                }
            }
    
            return result;
        }
    CRUD    
        defaultStack(即默认拦截顺序)(自上而下的依次拦截)
            <interceptor-stack name="defaultStack">
                    <interceptor-ref name="exception"/>
                    <interceptor-ref name="alias"/>
                    <interceptor-ref name="servletConfig"/>
                    <interceptor-ref name="i18n"/>
                    <interceptor-ref name="prepare"/>
                    <interceptor-ref name="chain"/>
                    <interceptor-ref name="scopedModelDriven"/>
                    <interceptor-ref name="modelDriven"/>
                    <interceptor-ref name="fileUpload"/>
                    <interceptor-ref name="checkbox"/>
                    <interceptor-ref name="multiselect"/>
                    <interceptor-ref name="staticParams"/>
                    <interceptor-ref name="actionMappingParams"/>
                    <interceptor-ref name="params">
                        <param name="excludeParams">dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,parameters...*</param>
                    </interceptor-ref>
                    <interceptor-ref name="conversionError"/>
                    <interceptor-ref name="validation">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                    <interceptor-ref name="workflow">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                    <interceptor-ref name="debugging"/>
                </interceptor-stack>
        paramsPrepareParamsStack的拦截顺序
            <interceptor-stack name="paramsPrepareParamsStack">
                    <interceptor-ref name="exception"/>
                    <interceptor-ref name="alias"/>
                    <interceptor-ref name="i18n"/>
                    <interceptor-ref name="checkbox"/>
                    <interceptor-ref name="multiselect"/>
                    <interceptor-ref name="params">
                        <param name="excludeParams">dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,parameters...*</param>
                    </interceptor-ref>
                    <interceptor-ref name="servletConfig"/>
                    <interceptor-ref name="prepare"/>
                    <interceptor-ref name="chain"/>
                    <interceptor-ref name="modelDriven"/>
                    <interceptor-ref name="fileUpload"/>
                    <interceptor-ref name="staticParams"/>
                    <interceptor-ref name="actionMappingParams"/>
                    <interceptor-ref name="params">
                        <param name="excludeParams">dojo..*,^struts..*,^session..*,^request..*,^application..*,^servlet(Request|Response)..*,parameters...*</param>
                    </interceptor-ref>
                    <interceptor-ref name="conversionError"/>
                    <interceptor-ref name="validation">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                    <interceptor-ref name="workflow">
                        <param name="excludeMethods">input,back,cancel,browse</param>
                    </interceptor-ref>
                </interceptor-stack>
    
    类型转换错误的显示及自定义
        什么是类型转换?
        jsp页面中提交的字段的类型都是String或者String[]的,当Action类或者model类中接受对应字段的属性不为String时就要进行类型转换
        如何显示错误消息?
        1). Action类要实现ValidationAware接口(因为ActionSupport实现了接口,所以继承ActionSupport即可)
        2). jsp页面中的表单的主体不为simple即可
        问题1: 如何覆盖默认的错误消息?
        1). 在对应的 Action 类所在的包中新建  
            ActionClassName.properties 文件, ActionClassName 即为包含着输入字段的 Action 类的类名
        2). 在属性文件中添加如下键值对: invalid.fieldvalue.fieldName=xxx(表单项的名字)
        
        
        问题2: 如果是 simple 主题, 还会自动显示错误消息吗? 如果不会显示, 怎么办 ?
        1). 通过 debug 标签, 可知若转换出错, 则在值栈的 Action(实现了 ValidationAware 接口) 对象中有一个  fieldErrors 属性.
        该属性的类型为 Map<String, List<String>> 键: 字段(属性名), 值: 错误消息组成的 List. 所以可以使用 LE 或 OGNL 的方式
        来显示错误消息: ${fieldErrors.age[0]}
        
        2). 还可以使用 s:fielderror 标签来显示. 可以通过 fieldName 属性显示指定字段的错误.
        
        问题3. 若是 simple 主题, 且使用  <s:fielderror fieldName="age"></s:fielderror> 来显示错误消息, 则该消息在一个 
        ul, li, span 中. 如何去除 ul, li, span 呢 ?
        在 template.simple 下面的 fielderror.ftl 定义了 simple 主题下, s:fielderror 标签显示错误消息的样式. 所以修改该
        配置文件即可. 在 src 下新建  template.simple 包, 新建 fielderror.ftl 文件, 把原生的 fielderror.ftl 中的内容
        复制到新建的 fielderror.ftl 中, 然后剔除 ul, li, span 部分即可. 
        
    这里定义的类型转换器,仅是在表单的提交和回显时才会进过转换器。
    若想使页面上的Date类型按指定格式显示在页面上,则可以
    
        问题4. 如何自定义类型转换器 ?  
        1). 为什么需要自定义的类型转换器 ? 因为 Struts 不能自动完成 字符串 到 引用类型 的 转换.
        2). 如何定义类型转换器:
        I.  开发类型转换器的类: 扩展 StrutsTypeConverter 类.
        II. 配置类型转换器: 
        有两种方式
        ①. 基于字段的配置: 
            > 在字段所在的 Model(可能是 Action, 可能是一个 JavaBean) 的包下, 新建一个 ModelClassName-conversion.properties 文件
            > 在该文件中输入键值对: fieldName=类型转换器的全类名. 
            > //第一次使用该转换器时创建实例. 
            > 类型转换器是单实例的!    
        
        ②. 基于类型的配置:
            > 在 src 下新建 xwork-conversion.properties
            > 键入: 待转换的类型=类型转换器的全类名.(java.util.Date=conversion.DateConversion 类型须为全称)
            > //在当前 Struts2 应用被加载时创建实例.
                //(所以在它的构造函数中不能用ServletActionContext.getServletContext().getInitParameter("dateForm");)
            
            //切记:当字符串切割“.”时血的教训(正则,要写成“\.”)
            
            form 标签的 name 属性可以被映射到一个属性的属性.如Manger.birth
            Struts 还允许填充 Collection 里的对象, 这常见于需要快速录入批量数据的场合
                例:表单中标签名字为mgrs[0,1,2,3...].birth        Action中属性为private Collection<Manager> mgrs = null;即可
            
            配置整个App的初始化参数?
                        在Web.xml中
                        <context-param>
                                <param-name>dateForm</param-name>
                                <param-value>yyyy年MM月dd日 hh:mm:ss</param-value>
                        </context-param>
            
    国际化的目标
    
        1). 如何配置国际化资源文件
        
        I.   Action 范围资源文件: 在Action类文件所在的路径建立名为 ActionName_language_country.properties 的文件
        II.  包范围资源文件: 在包的根路径下建立文件名为 package_language_country.properties 的属性文件,
        一旦建立,处于该包下的所有 Action 都可以访问该资源文件。注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。
        III. 全局资源文件
            > 命名方式: basename_language_country.properties
            > struts.xml <constant name="struts.custom.i18n.resources" value="baseName"/>
        
        IV.  国际化资源文件加载的顺序如何呢 ? 离当前 Action 较近的将被优先加载. 
        
        假设我们在某个 ChildAction 中调用了getText("username"):
        
        (1) 加载和 ChildAction 的类文件在同一个包下的系列资源文件 ChildAction.properties
        (2) 加载  ChildAction 实现的接口 IChild,且和 IChildn 在同一个包下 IChild.properties 系列资源文件。
        (3) 加载 ChildAction 父类 Parent,且和 Parent 在同一个包下的 baseName 为 Parent.properties 系列资源文件。
        (4) 若 ChildAction 实现 ModelDriven 接口,则对于getModel()方法返回的model 对象,重新执行第(1)步操作。
        (5) 查找当前包下 package.properties 系列资源文件。
        (6) 沿着当前包上溯,直到最顶层包来查找 package.properties 的系列资源文件。
        (7) 查找 struts.custom.i18n.resources 常量指定 baseName 的系列资源文件。
        (8) 直接输出该key的字符串值。
        
        
        2). 如何在页面上 和 Action 类中访问国际化资源文件的  value 值
        
        I. 在 Action 类中. 若 Action 实现了 TextProvider 接口, 则可以调用其 getText() 方法获取 value 值
            > 通过继承 ActionSupport 的方式。 
            
        II. 页面上可以使用 s:text 标签; 对于表单标签可以使用表单标签的 key 属性值
            > 若有占位符, 则可以使用 s:text 标签的 s:param 子标签来填充占位符
            > 可以利用标签和 OGNL 表达式直接访问值栈中的属性值(对象栈 和 Map 栈)
            
            time=Time:{0}
            
            <s:text name="time">
                <s:param value="date"></s:param>
            </s:text>
        
            ------------------------------------
            
            time2=Time:${date}
            
            <s:text name="time2"></s:text>
            
        
        3). 实现通过超链接切换语言. 
        
        I.  关键之处在于知道 Struts2 框架是如何确定 Local 对象的 !
        II. 可以通过阅读 I18N 拦截器知道. 
        III. 具体确定 Locale 对象的过程:
        
            > Struts2 使用 i18n 拦截器 处理国际化,并且将其注册在默认的拦截器栈中
            > i18n拦截器在执行Action方法前,自动查找请求中一个名为 request_locale 的参数。
                  如果该参数存在,拦截器就将其作为参数,转换成Locale对象,并将其设为用户默认的Locale(代表国家/语言环境)。
                  并把其设置为 session 的 WW_TRANS_I18N_LOCALE 属性
            > 若 request 没有名为request_locale 的参数,则 i18n 拦截器会从 Session 中获取 WW_TRANS_I18N_LOCALE 的属性值,
                 若该值不为空,则将该属性值设置为浏览者的默认Locale 
            > 若 session 中的 WW_TRANS_I18N_LOCALE 的属性值为空,则从 ActionContext 中获取 Locale 对象。
            
        IV.  具体实现: 只需要在超连接的后面附着  request_locale 的请求参数, 值是 语言国家 代码.
            <a href="testI18n.action?request_locale=en_US">English</a>
            <a href="testI18n.action?request_locale=zh_CN">中文</a>
            
            > 注意: 超链接必须是一个 Struts2 的请求, 即使 i18n 拦截器工作!
        //注意<a href="index.jsp?request_locale=en_US">English</a>此请求是不经过struts拦截器的
        
    Struts2 的验证
        1). 验证分为两种:
        
            > 声明式验证*
            
                >> 对哪个 Action 或 Model 的那个字段进行验证
                >> 使用什么验证规则
                >> 如果验证失败, 转向哪一个页面, 显示是什么错误消息
            
            > 编程式验证
            
        2). 声明式验证的 helloworld
        
        I.  先明确对哪一个 Action 的哪一个字段进行验证: age
        II. 编写配置文件: 
            > 把 struts-2.3.15.3appsstruts2-blankWEB-INFclassesexample 下的 Login-validation.xml 文件复制到
            当前 Action 所在的包下. 
            > 把该配置文件改为: 把  Login 改为当前 Action 的名字. 
            > 编写验证规则: 参见 struts-2.3.15.3/docs/WW/docs/validation.html 文档即可.
            > 在配置文件中可以定义错误消息: 
            
                <field name="age">
                 <field-validator type="int">
                     <param name="min">20</param>
                     <param name="max">50</param>
                     <message>^^Age needs to be between ${min} and ${max}</message>
                 </field-validator>
             </field>
             
             > 该错误消息可以国际化吗. 可以
             
                     <message key="error.int"></message>. 
                     
                     再在 国际化资源文件 中加入一个键值对: error.int=^^^Age needs to be between ${min} and ${max}
            
        III. 若验证失败, 则转向 input 的那个 result. 所以需要配置 name=input 的 result
             <result name="input">/validation.jsp</result>
             
        IV. 如何显示错误消息呢 ?      
        
            > 若使用的是非 simple, 则自动显示错误消息.
            > 若使用的是 simple 主题, 则需要 s:fielderror 标签或直接使用 EL 表达式(使用 OGNL)
            
            ${fieldErrors.age[0] } 
            OR
            <s:fielderror fieldName="age"></s:fielderror>*
        
        3). 注意: 若一个 Action 类可以应答多个 action 请求, 多个 action 请求使用不同的验证规则, 怎么办 ?
        
            > 为每一个不同的 action 请求定义其对应的验证文件: ActionClassName-AliasName-validation.xml
            
            > 不带别名的配置文件: ActionClassName-validation.xml 中的验证规则依然会发生作用. 可以把各个 action 公有的验证规则
            配置在其中. 但需要注意的是, 只适用于某一个 action 的请求的验证规则就不要这里再配置了. 
            
        4). 声明式验证框架的原理:
        
            > Struts2 默认的拦截器栈中提供了一个 validation 拦截器
            
            > 每个具体的验证规则都会对应具体的一个验证器. 有一个配置文件把验证规则名称和验证器关联起来了. 而实际上验证的是那个验证器. 
            该文件位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
            
            <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/>
        
        5). 短路验证: 若对一个字段使用多个验证器, 默认情况下会执行所有的验证. 若希望前面的验证器验证没有通过, 后面的就不再验证, 可以使用短路验证
        
                <!-- 设置短路验证: 若当前验证没有通过, 则不再进行下面的验证 -->
                <field-validator type="conversion" short-circuit="true">
                    <message>^Conversion Error Occurred</message>
                </field-validator>
        
                <field-validator type="int">
                    <param name="min">20</param>
                    <param name="max">60</param>
                    <message key="error.int"></message>
                </field-validator>    
                
        6). 若类型转换失败, 默认情况下还会执行后面的拦截器, 还会进行 验证. 可以通过修改 ConversionErrorInterceptor 源代码的方式使
        当类型转换失败时, 不再执行后续的验证拦截器, 而直接返回 input 的 result
        
                        Object action = invocation.getAction();
                if (action instanceof ValidationAware) {
                    ValidationAware va = (ValidationAware) action;
        
                    if(va.hasFieldErrors() || va.hasActionErrors()){
                        return "input";
                    }
                }    
                
        7). 关于非字段验证: 不是针对于某一个字段的验证. 
        
            <validator type="expression">
                <param name="expression"><![CDATA[password==password2]]></param>
                <message>Password is not equals to password2</message>
            </validator>
             
                  显示非字段验证的错误消息, 使用 s:actionerror 标签:  <s:actionerror/>
                  
        8). 不同的字段使用同样的验证规则, 而且使用同样的响应消息 ?
        
        error.int=${getText(fieldName)} needs to be between ${min} and ${max}
        
        age=u5E74u9F84
        count=u6570u91CF       
        
        详细分析参见  PPT 159.  
        
        9). 自定义验证器:
        
        I.   定义一个验证器的类
        
            > 自定义的验证器都需要实现 Validator. 
            > 可以选择继承 ValidatorSupport 或 FieldValidatorSupport 类
            > 若希望实现一个一般的验证器, 则可以继承 ValidatorSupport
            > 若希望实现一个字段验证器, 则可以继承 FieldValidatorSupport
            
            > 具体实现可以参考目前已经有的验证器. 
            
            > 若验证程序需要接受一个输入参数, 需要为这个参数增加一个相应的属性
        
        II.  在配置文件中配置验证器
        
            > 默认情况下, Struts2 会在 类路径的根目录下(src中)加载 validators.xml 文件. 在该文件中加载验证器.
                 该文件的定义方式同默认的验证器的那个配置文件: 位于 com.opensymphony.xwork2.validator.validators 下的 default.xml
                 
            > 若类路径下没有指定的验证器, 则从 com.opensymphony.xwork2.validator.validators 下的 default.xml 中的验证器加载     
        
        III. 使用: 和目前的验证器一样. 
        
        IV. 示例代码: 自定义一个 18 位身份证验证器
        
    1. 文件的上传:
    
        1). 表单需要注意的 32). Struts2 的文件上传实际上使用的是 Commons FileUpload 组件, 所以需要导入
        
        commons-fileupload-1.3.jar
        commons-io-2.0.1.jar
        
        3). Struts2 进行文件上传需要使用 FileUpload 拦截器
        
        4). 基本的文件的上传: 直接在 Action 中定义如下 3 个属性, 并提供对应的 getter 和 setter
        
        //文件对应的 File 对象
        private File [fileFieldName];
        //文件类型
        private String [fileFieldName]ContentType;
        //文件名
        private String [fileFieldName]FileName;
        
        5). 使用 IO 流进行文件的上传即可. 
        
        6). 一次传多个文件怎么办 ?
        
        若传递多个文件, 则上述的 3 个属性, 可以改为 List 类型! //多个文件域的 name 属性值需要一致. 
        
        7). 可以对上传的文件进行限制吗 ? 例如扩展名, 内容类型, 上传文件的大小 ? 若可以, 则若出错, 显示什么错误消息呢 ? 消息可以定制吗 ? 
        
        可以的!
        
        可以通过配置 FileUploadInterceptor 拦截器的参数的方式来进行限制
        
        maximumSize (optional) - 默认的最大值为 2M. 上传的单个文件的最大值
        
        allowedTypes (optional) - 允许的上传文件的类型. 多个使用 , 分割
        
        allowedExtensions (optional) - 允许的上传文件的扩展名. 多个使用 , 分割.
        
        注意: 在 org.apache.struts2 下的 default.properties 中有对上传的文件总的大小的限制. 可以使用常量的方式来修改该限制
        
        struts.multipart.maxSize=2097152
        
        定制错误消息. 可以在国际化资源文件中定义如下的消息:
        
        struts.messages.error.uploading - 文件上传出错的消息
        
        struts.messages.error.file.too.large - 文件超过最大值的消息
        
        struts.messages.error.content.type.not.allowed - 文件内容类型不合法的消息
        
        struts.messages.error.file.extension.not.allowed - 文件扩展名不合法的消息
        
        问题: 此种方式定制的消息并不完善. 可以参考 org.apache.struts2 下的 struts-messages.properties, 可以提供更多的定制信息.
    
    2. 文件的下载:
    
        1). Struts2 中使用 type="stream" 的 result 进行下载即可
        
        2). 具体使用细节参看 struts-2.3.15.3-all/struts-2.3.15.3/docs/WW/docs/stream-result.html
        
        3). 可以为 stream 的 result 设定如下参数
        
        contentType: 结果类型
        contentLength: 下载的文件的长度
        contentDisposition: 设定 Content-Dispositoin 响应头. 该响应头指定接应是一个文件下载类型, 一般取值为  attachment;filename="document.pdf".
        
        inputName: 指定文件输入流的 getter 定义的那个属性的名字. 默认为 inputStream
        
        bufferSize: 缓存的大小. 默认为 1024
        allowCaching: 是否允许使用缓存 
        contentCharSet: 指定下载的字符集 
        
        4). 以上参数可以在 Action 中以 getter 方法的方式提供!
    
    3. 表单的重复提交问题
    
        1). 什么是表单的重复提交
        
            > 在不刷新表单页面的前提下:?
                >> 多次点击提交按钮
                >> 已经提交成功, 按 "回退" 之后, 再点击 "提交按钮".
                >> 在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)"
                
            > 注意:
                >> 若刷新表单页面, 再提交表单不算重复提交
                >> 若使用的是 redirect 的响应类型, 已经提交成功后, 再点击 "刷新", 不是表单的重复提交
                
        2). 表单重复提交的危害:              
        
        3). Struts2 解决表单的重复提交问题:
        
        I. 在 s:form 中添加 s:token 子标签
        
            > 生成一个隐藏域
            > 在 session 添加一个属性值
            > 隐藏域的值和 session 的属性值是一致的. 
            
        II. 使用 Token 或 TokenSession 拦截器. 
        
            > 这两个拦截器均不在默认的拦截器栈中, 所以需要手工配置一下
            > 若使用 Token 拦截器, 则需要配置一个 invalid.token 的 result ; 且Action类要实现 TextProvider 接口,否则会报空指针异常,简单的方法是继承ActionSupport
            > 若使用 TokenSession 拦截器, 则不需要配置任何其它的 result
            
        III. Token VS TokenSession
        
            > 都是解决表单重复提交问题的
            > 使用 token 拦截器会转到 token.valid 这个 result
            > 使用 tokenSession 拦截器则还会响应那个目标页面, 但不会执行 tokenSession 的后续拦截器. 就像什么都没发生过一样!
            
        IV. 可以使用 s:actionerror 标签来显示重复提交的错误消息. 
        该错误消息可以在国际化资源文件中覆盖. 该消息可以在 struts-messages.properties 文件中找到
        
        struts.messages.invalid.token=^^The form has already been processed or no token was supplied, please try again.
    
    4. 自定义拦截器
    
        1). 具体步骤
        
        I. 定义一个拦截器的类
        
            > 可以实现 Interceptor 接口
            > 继承 AbstractInterceptor 抽象类
        
        II. 在 struts.xml 文件配置.    
        
            <interceptors>
                    
                <interceptor name="hello" class="com.atguigu.struts2.interceptors.MyInterceptor"></interceptor>
                
            </interceptors>
            
            <action name="testToken" class="com.atguigu.struts2.token.app.TokenAction">
                <interceptor-ref name="hello"></interceptor-ref>
                <interceptor-ref name="defaultStack"></interceptor-ref>
                <result>/success.jsp</result>
                <result name="invalid.token">/token-error.jsp</result>
            </action>
            
        III. 注意: 在自定义的拦截器中可以选择不调用 ActionInvocation 的 invoke() 方法. 那么后续的拦截器和 Action 方法将不会被调用.
        Struts 会渲染自定义拦截器 intercept 方法返回值对应的 result
  • 相关阅读:
    Appium Desktop使用
    mumu模拟器使用
    adb
    测试准出
    缺陷管理
    测试准入检查
    测试工作流程
    需求测试分析
    异常字符测试
    今日总结
  • 原文地址:https://www.cnblogs.com/feifeiyun/p/6415881.html
Copyright © 2011-2022 走看看