1.消息处理与国际化
- 概述:
- 什么是国际化:在程序设计领域,无需改写源代码即可让开发出来的应用程序能够支持多国语言和数据格式的技术被称为国际化
- 本地化:与国际化对应,指让一个具备国际化支持的应用程序支持某个地区
- Struts国际化:
- Struts国际化是建立在Java的基础之上
- 为不同的国家/语言提供对应的消息资源文件
- Struts框架会根据请求中的Locale加载对应的资源文件
- 通过程序代码取得该资源文件中指定key对应的消息
- Struts2国际化目标:
- 如何配置Struts2国际化
- Action范围资源文件:在Action类文件所在路径下新建ActionName_language_country.properties文件
- 包范围资源文件:
- 在包的根目录下建立一个package_language_country.properties文件,一旦建立,处于该包下的所有Action都可以访问该资源文件
- 注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。
- 全局资源文件:
- 命名方式:basename_language_country.properties
- struts.xml文件配置:
- <constant name="struts.custom.i18n.resources" value="baseName"/>
- struts.properties
- struts.custom.i18n.resources=baseName
- 临时指定资源文件:
- 使用<s:i18n>标签的name属性临时指定资源文件
- 国际化资源文件的加载顺序:
- 距离当前Action越近的将被优先加载
- 假设我们在某个 ChildAction 中调用了getText("username"):
- 加载和 ChildAction 的类文件在同一个包下的系列资源文件 ChildAction.properties
- 加载 ChildAction 实现的接口 IChild,且和 IChildn 在同一个包下 IChild.properties 系列资源文件。
- 加载 ChildAction 父类 Parent,且和 Parent 在同一个包下的 baseName 为 Parent.properties 系列资源文件。
- 若 ChildAction 实现 ModelDriven 接口,则对于getModel()方法返回的model 对象,重新执行第(1)步操作。
- 查找当前包下 package.properties 系列资源文件。
- 着当前包上溯,直到最顶层包来查找 package.properties 的系列资源文件。
- 查找 struts.custom.i18n.resources 常量指定 baseName 的系列资源文件。
- 直接输出该key的字符串值。
- 如何在页面和Action类中访问国家化资源文件的value值
- 在Action类中:
- 若实现了TextProvider接口,则可以调用其getText()方法获取value值
- 通过继承ActionSupport的方式
- 在页面上:
- 页面上可以使用s:text标签,对于表单可以使用表单标签的key属性值
- 若有占位符,可以使用使用s:text标签的s:parm子标签来填充占位符
- 利用标签和 OGNL表达式直接访问值栈中的属性值(对象栈和Map栈)
- 实现通过超链接切换语言:
- 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 对象。
- 具体实现:
- 注意:超链接必须是一个Struts2的请求,否则无法使i18n拦截器工作
- Struts2运行流程分析
-
-
- 流程简述:
- 请求发送给 StrutsPrepareAndExecuteFilter
- StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 Struts2 请求(即是否返回一个非空的 ActionMapping 对象)
- 若 ActionMapper 认为该请求是一个 Struts2 请求,则 StrutsPrepareAndExecuteFilter 把请求的处理交给 ActionProxy
- ActionProxy 通过 Configuration Manager 询问框架的配置文件,确定需要调用的 Action 类及 Action 方法
- ActionProxy 创建一个 ActionInvocation 的实例,并进行初始化
- ActionInvocation 实例在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
- Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置找到对应的返回结果。调用结果的 execute 方法,渲染结果。在渲染的过程中可以使用Struts2 框架中的标签。
- 执行各个拦截器 invocation.invoke() 之后的代码
- 把结果发送到客户端
-
2.输入验证
- 概述:
- 一个健壮的 web 应用程序必须确保用户输入是合法、有效的.
- Struts2 的输入验证(推荐)
- 基于 XWork Validation Framework 的声明式验证:Struts2 提供了一些基于 XWork Validation Framework 的内建验证程序. 使用这些验证程序不需要编程, 只要在一个 XML 文件里对验证程序应该如何工作作出声明就可以了. 需要声明的内容包括:
- 哪些字段需要进行验证
- 使用什么验证规则
- 在验证失败时应该把什么样的出错消息发送到浏览器端
- 编程验证:通过编写代码来验证用户输入
- 声明式验证:
- 分类:
- 字段验证: 判断某个字段属性的输入是否有效
- 非字段验证: 不只针对某个字段,而是针对多个字段的输入值之间的逻辑关系进行校验。例如:对再次输入密码的判断。
- 步骤:
- 找到struts-2.5.14.1\apps\struts2-showcase\WEB-INF\classes\org\apache\struts2\showcase\validation目录下的User-userContext-validation.xml
- 修改User-validation.xml为ActionName-validation.xml
- 验证规则:参见struts-2.5.14.1/docs/docs/core-developers/validation.html
- 在配置文件中定义错误消息:
- 编写一个验证程序配置文件. 它的文件名必须是以下两种格式之一:
- 若一个 Action 类的多个 action 使用同样的验证规则: ActionClassName-validation.xml
- 若一个 Action 类的多个 action 使用不同的验证规则: ActionClass-alias-validation.xml, 例UserAction-User_create-validation.xml
- 注意:
- 为每一个action请求定义对应的验证文件:ActionClass-alias-validation.xml
- 不带别名的配置文件:ActionClassName-validation.xml中的验证规则依然会发挥作用,可以把各个action请求公有的验证规则配置在其中,但是,只适用于某一个action请求的验证规则不要在这里配置
- 确定验证失败时的响应页面: 在 struts.xml 文件中定义一个 <result name=“input”> 的元素.
- 消息验证国际化,在message标签中有一个key属性,key属性的属性值=国际化文件中的属性值
- 错误消息显示:
- 若没有simple主题:Struts2会自动显示
- 若有simple主题:使用s:fieldError标签或者EL标签显示
- 验证程序配置:
- Struts2内建验证规则:
- conversion validator:转换验证器
- date validator:日期验证器
- double validator:浮点验证器
- email validator:email 验证器
- expression validator:表达式验证器
- fieldexpression validator:字段表达式验证器
- int validator:整型验证器
- regex validator:正则表达式验证器
- required validator:非空验证器
- requiredstring validator:非空字符串验证器
- stringlength validator:字符串长度验证器
- url validator:url 格式验证器
- visitor validator:复合属性验证器
- Struts2 内建的验证程序
- required: 确保某给定字段的值不是空值 null
- requiredstring: 确保某给定字段的值既不是空值 null, 也不是空白.
- trim 参数. 默认为 true, 表示 struts 在验证该字段值之前先剔除前后空格.
- stringlength: 验证一个非空的字段值是不是有足够的长度.
- minLength: 相关字段的最小长度. 若没有给出这个参数, 该字段将没有最小长度限制
- maxLength:相关字段的最大长度. 若没有给出这个参数, 该字段将没有最大长度限制
- trim: 在验证之前是否去除前后空格
- date: 确保某给定日期字段的值落在一个给定的范围内
- max:相关字段的最大值. 若没给出这个参数, 该字段将没有最大值限制
- min:相关字段的最小值. 若没给出这个参数, 该字段将没有最小值限制
- email: 检查给定 String 值是否是一个合法的 email
- url: 检查给定 String 值是否是一个合法的 url
- regex: 检查某给定字段的值是否与一个给定的正则表达式模式相匹配.
- expresssion*: 用来匹配的正则表达式
- caseSensitive: 是否区分字母的大小写. 默认为 true
- trim: 是否去除前后空格. 默认为 true
- int: 检查给定整数字段值是否在某一个范围内
- min: 相关字段的最小值. 若没给出这个参数, 该字段将没有最小值限制
- max: 相关字段的最大值. 若没给出这个参数, 该字段将没有最大值限制
- conversion: 检查对给定 Action 属性进行的类型转换是否会导致一个转换错误. 该验证程序还可以在默认的类型转换消息的基础上添加一条自定义的消息
- expression 和 fieldexpression: 用来验证给定字段是否满足一个 OGNL 表达式.
- 前者是一个非字段验证程序, 后者是一个字段验证程序.
- 前者在验证失败时将生成一个 action 错误, 而后者在验证失败时会生成一个字段错误
- expression*: 用来进行验证的 OGNL 表达式
- 声明式验证原理分析:
- struts2默认的拦截器栈中配置了一个validation拦截器
- 每个具体的验证规则都会对应一个具体的验证器,有一个配置文件把验证规则和验证器关联起来,而实际上验证的是哪个验证器,该文件位于com.opensymphony.xwork2.validator.validators下的default.xml
- 短路验证:
- 对同一个字段内的多个验证器,如果一个短路验证器验证失败,其他验证器不会继续校验
- <validator …/> 元素和 <field-validator …/>元素可以指定一个可选的 short-circuit 属性,该属性指定该验证器是否是短验证器,默认值为 false。
- 若类型转换失败,默认会继续执行后面的拦截器,还会进行验证,可以通过重写com.opensymphony.xwork2.interceptor包下ConversionErrorInterceptor类的doIntercept方法
- 非字段验证
- 使用s:actionerror标签打印错误
- 字段验证和非字段验证的区别:
- 字段验证字段优先,可以为一个字段配置多个验证规则
- 非字段验证验证规则优先
- 大部分验证规则支持两种验证器,但个别的验证规则只能使用非字段验证,例如表达式验证。
- 不同字段使用同一条验证规则:
- 详细分析:
- 自定义验证器:
- 定义一个验证器类:
- 自定义验证器必须实现 Validator 接口.
- ValidatorSupport 和 FieldValidatorSupport 实现了 Validator 接口
- 若需要普通的验证程序, 可以继承 ValidatorSupport 类
- 若需要字段验证程序, 可以继承 FieldValidatorSupport 类
- 若验证程序需要接受一个输入参数, 需要为这个参数增加一个相应的属性
- 在配置文件中配置验证器:
- 注册验证程序: 自定义验证器需要在类路径里的某个 validators.xml 文件里注册: 验证框架首先在根目录下找validators.xml文件,没找到validators.xml文件, 验证框架将调用默认的验证设置,即default.xml里面的配置信息.
- 使用和系统内置一样
- 示例代码:验证18位身份证号
- 编程验证(仅做了解即可):
- Struts2 提供了一个 Validateable 接口, 可以使 Action 类实现这个接口以提供编程验证功能.
- ActionSupport 类已经实现了 Validateable 接口
3.文件上传与下载
- 文件上传
- 表单准备:
- 须把 HTML 表单的 enctype 属性设置为 multipart/form-data
- 须把 HTML 表单的method 属性设置为 post
- 需添加 <input type=“file”> 字段.
- Struts2对文件上传的支持:
- 在 Struts 应用程序里, FileUpload 拦截器和 Jakarta Commons FileUpload 组件完成文件的上传.
- 步骤:
- 在 Jsp 页面的文件上传表单里使用 file 标签. 如果需要一次上传多个文件, 就必须使用多个 file 标签, 但它们的名字必须是相同的
- 在 Action 中新添加 3 个和文件上传相关的属性. 这 3 个属性的名字必须是以下格式
- [File Name] : File -被上传的文件。例如:data
- [File Name]ContentType : String -上传文件的文件类型。例如:dataContentType
- [File Name]FileName : String -上传文件的文件名。例如:dataFileName
- 如果上上传多个文件, 可以使用 List
- 文件上传实现:
- 最基本的文件上传需要定义以下属性,并提供get和set方法
- private File fileFieldName;
- private String fileContentType;
- private String fileFileName;
- 使用io流进行文件上传
- 一次上传多个文件
- 若传递多个文件,则上述三个属性可以改为list类型,且多个文本域的name属性值需要一致
- 回显时描述信息会全部打印,需要解决该问题,name属性值[0]从零开始
- 文件上传限制
- 配置 FileUpload 拦截器:
- maximumSize: 上传单个文件的最大长度(以字节为单位), 默认值为 2 MB
- allowedTypes: 允许上传文件的类型, 各类型之间以逗号分隔
- allowedExtensions: 允许上传文件扩展名, 各扩展名之间以逗号分隔
- 可以在 struts.xml 文件中覆盖这 3 个属性
- Commons FileUpload 组件默认接受上传文件总的最大值为 2M, 可以通过在 struts 配置文件中配置常量的方式修改struts.multipart.maxSize=2097152
- 修改保存位置也可以使用常量来定制:struts.multipart.saveDir=
- 定制错误消息,可以在国际化文件中定义以下错误:
- struts.message.error.uploading - 文件上传出错的消息
- struts.message.error.file.too.large - 文件超过最大值的消息
- struts.message.error.content.type.not.allowed - 文件内容类型不合法的消息
- struts.message.error.file.extension.not.allowed - 文件扩展名不合法的消息
- 与文件上传有关的出错消息在 struts-messages.properties 文件里预定义. 可以在文件上传 Action 相对应的资源文件中重新定义错误消息
- 问题:消息定制并不完善,可以参考org.apache.struts2包下的struts-messages.properties文件
- 文件下载
- 概述:在某些应用程序里, 可能需要动态地把一个文件发送到用户的浏览器中, 而这个文件的名字和存放位置在编程时是无法预知的
- 步骤:
- struts2中使用type="stream"的方式下载
- Stream 结果类型可以设置如下参数:
- contentType:被下载的文件的 MIME 类型。默认值为 text/plain
- contentLength:被下载的文件的大小,以字节为单位
- contentDisposition: 可以设置下载文件名的ContentDispositon 响应头,默认值为 inline,通常设置为如下格式: attachment;filename="document.pdf".
- inputName:Action 中提供的文件的输入流。默认值为 inputStream
- bufferSize:文件下载时缓冲区的大小。默认值为 1024
- allowCaching :文件下载时是否允许使用缓存。默认值为 true
- contentCharSet:文件下载时的字符编码。
- 注意:Stream 结果类型的参数可以在 Action 以属性的方式覆盖
- 表单重复提交:
- 概述:
- 在不刷新表单页面的前提下:
- 多次点击提交按钮
- 已经提交成功, 按 "回退" 之后, 再点击 "提交按钮".
- 在控制器响应页面的形式为转发情况下,若已经提交成功, 然后点击 "刷新(F5)“
- 缺点:
- 加重了服务器的负担
- 可能导致错误操作.
- 注意:
- 若刷新表单页面, 再提交表单不算重复提交.
- 若使用的是redreict响应类型,已经提交成功后再点击刷新,不算重复提交
- 解决方案:
- 在s:form表单中添加s:token标签
- 生成一个隐藏域
- 在session中添加一个属性值
- 隐藏域的值和session的值是一致的
- 使用Token 或 TokenSession拦截器
- 这两个拦截器均不在默认的拦截器栈中,需要手工配置
- 若使用token拦截器则需要配置一个token.valid的result
- 若使用tokensession则不需要配置其他的任何result
- token VS tokenSession
- 都是解决表单重复提交问题
- 使用token拦截器会跳转到token.valid这个result
- 使用tokensession拦截器还会响应目标页面,但不会执行后续的拦截器,像什么都没发生
- 注意;
- 一般建议token拦截器写在默认拦截器栈之前
- 可以使用s:actionerror标签来显示重复提交问题,该错误消息可以在国际化资源文件中覆盖
4.自定义拦截器
- 拦截器(Interceptor)是 Struts 2 的核心组成部分。
- Struts2 很多功能都是构建在拦截器基础之上的,例如文件的上传和下载、国际化、数据类型转换和数据校验等等。
- Struts2 拦截器在访问某个 Action 方法之前或之后实施拦截
- Struts2 拦截器是可插拔的, 拦截器是 AOP(面向切面编程) 的一种实现.
- 拦截器栈(Interceptor Stack): 将拦截器按一定的顺序联结成一条链. 在访问被拦截的方法时, Struts2 拦截器链中的拦截器就会按其之前定义的顺序被依次调用
- 步骤:
- 先创建一个拦截器类,并实现interceptor接口,或继承其实现类AbstractInterceptor类
- 在struts.xml文件中配置<interceptor-ref name=""></interceptor-ref>
- 注意:
- 在自定义的拦截器中可以选择不调用 ActionInvocation 的 invoke() 方法. 那么后续的拦截器和 Action 方法将不会被调用.
- Struts 会渲染自定义拦截器 intercept 方法返回值对应的 result