Struts2
> 拦截器作用和Filter类似。
> 它可以在action方法调用之前被调用,可以拦截请求,也可以放行请求,还可以在action方法执行完毕之后做一些处理。
> Struts中已经给我们定义好了多个拦截器,可以满足我们的大部分需求。
> 但是有时我们会需要一个个性化的需求,比如 权限验证。
> 实现自定义拦截的步骤:
1) 创建一个类并实现com.opensymphony.xwork2.interceptor.Interceptor接口
> 拦截器每次执行时都会调用intercept方法
> intercept方法中有一个ActionInvocation对象,通过调用invocation.invoke()可以将请求放行
2) 在struts.xml配置文件中注册拦截器
在package标签中注册拦截器
<interceptors>
<interceptor name="拦截器名" class="拦截器的全类名"></interceptor>
</interceptors>
3) 在action中使用拦截器
注意:如果在action中引用了自定义拦截器,那么还需要在引入默认的拦截器栈,
如果不引用则整个Action只会经过自定义拦截器,而不会在调用其他的拦截器,
通过interceptor-ref来决定拦截器的执行位置,越靠前则越先执行
<action name="hello" class="com.atguigu.struts2.action.HelloAction">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result>/success.jsp</result>
<result name="input">/input.jsp</result>
</action>
3.上传下载
- 上传
> 将本地文件上传到服务器中
- 回忆Servlet里是如何做的
1.表单的要求
1) method是post
2) enctype是multipart/form-data
2.Servlet中解析上传的内容
通过FileUpload来解析请求内容的
- Struts2中
- 表单的要求和Servlet中一致
1) method是post
2) enctype是multipart/form-data
- Struts2中已经整合了commons-fileupload-1.3.jar和commons-io-2.0.1.jar
同时Struts2中还具有一个fileUpload拦截器,他会自动将表单中的信息(包括普通表单项和文件表单项)
自动封装到Action的属性中
- Action的要求
File fileName --> fileName要求和文件表单项的name一致
String [fileName]FileName --> 用来接收文件的名字
String [fileName]ContentType --> 用来接收文件类型(MIME值)
- 例子:
比如表单的file元素的name属性为photo,则Action中名字应该如下:
private File photo;
private String photoFileName;
private String photoContentType;
- 小问题:
1.文件名重名,一般可以在文件名的前面生成一个UUID作为前缀。
2.限制文件的大小
- 我们可以通过对拦截器设置参数,来设置单个文件的大小
maximumSize --> 单个文件的大小,默认大小为2m,单位是字节
当文件超过限制的大小时,会将页面转到input结果指向的页面
可以在国际化资源文件中通过struts.messages.error.file.too.large
来指定错误消息。
- 默认Struts2对文件总大小的限制为2M
-超过大小以后会转到input的结果,会报一个action的error
3.限制文件的类型
allowedTypes
- 允许的MIME类型
可以通过struts.messages.error.content.type.not.allowed来设置错误消息
allowedExtensions
- 允许的扩展名
可以通过struts.messages.error.file.extension.not.allowed来设置错误消息
文件的总大小需要在struts配置文件中来修改一个常量:
<constant name="struts.multipart.maxSize" value="5097152"></constant>
通过struts.messages.upload.error.SizeLimitExceededException来设置错误消息
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">57,408</param>
<param name="fileUpload.allowedTypes">image/pjpeg,image/gif</param>
<param name="fileUpload.allowedExtensions">jpg,gif</param>
</interceptor-ref>
</interceptor-stack>
- 下载
> 将服务器中的资源下载到本地。
> 关键点:
1.流(要下载的文件的流)
2.设置两个相应头(ContentType文件的类型, contentDisposition告诉浏览器如何处理流)
> 在Struts2中,如果想让用户下载文件,则返回的result需要指定为stream,在该类型中有如下几个参数需要配置:
contentType 文件的类型,该属性我们在Action中动态的获取
contentLength 文件的大小,用来在浏览器下载时显示一个进度条,告诉浏览器文件的大小
contentDisposition 告诉浏览器如何处理,设置为attachment;filename=文件名
inputName 被下载文件的流的名字,默认是inputStream,一般不修改
bufferSize 指定输出是缓存的大小,默认值1024
allowCaching 是否允许使用缓存 (default = true)
contentCharSet 指定字符编码,一般也不设置
> Action中的属性
// 被下载文件的流
private InputStream inputStream;
// 被下载文件的类型MIME值
private String contentType;
// 被下载文件的大小
private Long contentLength;
// contentDisposition响应头的信息
private String contentDisposition;
> execute方法
public String execute() throws Exception {
//获取ServletContext
ServletContext servletContext = ServletActionContext.getServletContext();
//获取文件的路径
String realPath = servletContext.getRealPath("/WEB-INF/file/有多少爱可以重来.mp3");
//获取文件的流
inputStream = new FileInputStream(realPath);
//设置文件的类型
contentType = servletContext.getMimeType(realPath);
//获取文件的长度
contentLength = new File(realPath).length();
//设置文件名
String fileName = "有多少爱可以重来.mp3";
fileName = new String(fileName.getBytes("gbk"),"iso8859-1");
contentDisposition = "attachment;filename="+fileName;
return SUCCESS;
}
> result配置
<result type="stream">
<param name="bufferSize">2048</param>
</result>
4.表单重复提交
> 同一个表单在不刷新的情况下多次向服务器提交请求。
> 危害:
1.会向数据库中添加大量的垃圾数据
2.服务器处理多次相同的请求,会造成服务器的压力较大
> 表单重复提交的三种情况:
1.提交请求后,重复刷新成功页面
2.提交请求后,回退不刷新页面,再次提交
3.多次点击提交按钮
> token(令牌)
1.用户在访问页面时,我们要生成一个随机的token值
2.将该值放入到session域中,同时放在表单隐藏域中
3.用户提交请求时,隐藏域中token将会一起提交
4.服务器在处理请求时先要检查请求参数中的token和session中的token是否一致
如果一致,则处理请求
否则,返回错误页面
5.注意,token使用过一次以后必须要从session域中移除
> Struts2中的使用:
1.我们直接在表单中使用<s:token></s:token>标签,
它可以自动在表单创建一个表单隐藏域,并生成一个随机的token
同事它也会将该token放入到session域中
2.我们希望有个拦截器可以在请求到达action之前来替我们验证token值
Struts2给我们提供了两个用来检查token的拦截器:
这两个拦截器检查token的机制相同,检查请求参数中的token和Session中token是否一致
检查完毕之后都会从session中移除token
不同的是token 会在没通过验证时,将页面转到一个错误页面
而tokenSession会继续转到action执行完毕以后的页面,但是action并不会执行
token
-token拦截器,会在检查token错误时,将请求转到result的name为invalid.token的结果。
- 使用token拦截器之前,需要先在action中引入token拦截器
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="invalid.token">/token-error.jsp</result>
tokenSession
-tokenSession拦截器在发现表单重复提交以后,并不会转到错误页面,
而是继续返回执行成功页面,但是它不会执行action的方法
<interceptor-ref name="tokenSession"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result>/success.jsp</result>
<result name="invalid.token">/token-error.jsp</result>
5.输入验证
> 我们所谓的输入验证,就是指检查用户输入的内容是否符合规范。
> 之前输入验证我们都是通过JS代码来检查,JS在检查之前请求还没有到达服务器。
> 如果仅仅使用JS来验证,JS的验证时可以跳过的。
> 所以大部分情况下,我们还需要在后台的程序中对输入进行验证。
> Struts2为我们提供了两种验证方式:声明式验证 还有 编程式验证
> 声明式验证又分为两种验证:字段验证,非字段验证
- 在Struts2中进行声明式验证,只需要向action所在的包导入一个配置文件,并在配置文件中来配置规则。
- 在struts-2.3.15.3\apps\struts2-blank\WEB-INF\classes\example有一个Login-validation.xml
这是一个验证配置文件的实例。
- 配置文件的命名规则:ActionClassName-validation.xml
<field name="username">
<field-validator type="requiredstring">
<message>用户名必须填写!</message>
</field-validator>
</field>
field 标签创建一个字段验证:
name属性指向的是要验证的字段的名字
field-validator 标签指定一个验证的规则
type属性执行的一个验证器
message标签指定出现错误以后显示的错误信息
> 如果以这种方式为配置文件命名:
ActionClassName-validation.xml
则该配置文件的内容会对整个action中的方法都起作用。
所以一般在ActionName-validation.xml配置都是一些通用的规则。
> 我们希望的是一个请求对应一个验证规则,那这时候文件名可以这样写:
ActionClassName-ActionName-validation.xml
比如说 UserAction中user_add请求可以这样写
UserAction-user_add-validation.xml
那这样该配置文件只会对user_add请求起作用。
> 像确认密码这种不需要验证本身的规则,而只是验证是否与密码一致这种验证我们成为非字段验证。
我们使用validator标签来创建一个非字段验证,非字段验证主要是检查字段之间逻辑关系
<validator type="expression">
<param name="expression">password==rePwd</param>
<message>两次输入的密码不一致!</message>
</validator>
如果没有通过非字段验证,则会在值栈中放入一个actionError,我们需要使用:
<s:actionerror/>标签来显示错误信息
> 短路验证:
当上一个验证规则没有通过时,则以后的验证也不在进行验证。
要设置短路验证只需要在field-validator设置如下属性
short-circuit="true"
> 原理
我们所使用的这些验证规则,在框架中都有对应的类来支持,所以使用声明式验证时实际上是在调用那个类来检查规则。
> 有些情况自带的验证器并不能满足我们的需求,这时我们还可以自定义验证器(了解)
> 验证身份证号码是否为18位
1.创建一个类并继承FieldValidatorSupport
@Override
public void validate(Object object) throws ValidationException {
//1.获取要验证的字段
String fieldName = getFieldName();
String val = (String) getFieldValue(fieldName, object);
//2.验证
//检查身份证号码,是否为18位
//3.返回一个结果
if(val == null || val.trim().length()!=18){
//没通过验证
addFieldError(fieldName, object);
}
}
2.编写配置文件
在类路径下创建一个validators.xml配置文件,并在该文件中注册自定义验证器
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Definition 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<validators>
<validator name="idCardValidator" class="com.atguigu.struts2.validator.IDValidator"/>
</validators>
3.在项目中使用
<field name="id">
<field-validator type="idCardValidator">
<message>身份证号不足18位!</message>
</field-validator>
</field>
补充:
像s:textfield标签中label属性,它默认不会被OGNL解析,但是有些情况,我们希望它可以被OGNL解析
如果想让某个内容被强制OGNL解析,则可以这么写 %{OGNL表达式}
1.运行流程
> 拦截器作用和Filter类似。
> 它可以在action方法调用之前被调用,可以拦截请求,也可以放行请求,还可以在action方法执行完毕之后做一些处理。
> Struts中已经给我们定义好了多个拦截器,可以满足我们的大部分需求。
> 但是有时我们会需要一个个性化的需求,比如 权限验证。
> 实现自定义拦截的步骤:
1) 创建一个类并实现com.opensymphony.xwork2.interceptor.Interceptor接口
> 拦截器每次执行时都会调用intercept方法
> intercept方法中有一个ActionInvocation对象,通过调用invocation.invoke()可以将请求放行
2) 在struts.xml配置文件中注册拦截器
在package标签中注册拦截器
<interceptors>
<interceptor name="拦截器名" class="拦截器的全类名"></interceptor>
</interceptors>
3) 在action中使用拦截器
注意:如果在action中引用了自定义拦截器,那么还需要在引入默认的拦截器栈,
如果不引用则整个Action只会经过自定义拦截器,而不会在调用其他的拦截器,
通过interceptor-ref来决定拦截器的执行位置,越靠前则越先执行
<action name="hello" class="com.atguigu.struts2.action.HelloAction">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result>/success.jsp</result>
<result name="input">/input.jsp</result>
</action>
3.上传下载
- 上传
> 将本地文件上传到服务器中
- 回忆Servlet里是如何做的
1.表单的要求
1) method是post
2) enctype是multipart/form-data
2.Servlet中解析上传的内容
通过FileUpload来解析请求内容的
- Struts2中
- 表单的要求和Servlet中一致
1) method是post
2) enctype是multipart/form-data
- Struts2中已经整合了commons-fileupload-1.3.jar和commons-io-2.0.1.jar
同时Struts2中还具有一个fileUpload拦截器,他会自动将表单中的信息(包括普通表单项和文件表单项)
自动封装到Action的属性中
- Action的要求
File fileName --> fileName要求和文件表单项的name一致
String [fileName]FileName --> 用来接收文件的名字
String [fileName]ContentType --> 用来接收文件类型(MIME值)
- 例子:
比如表单的file元素的name属性为photo,则Action中名字应该如下:
private File photo;
private String photoFileName;
private String photoContentType;
- 小问题:
1.文件名重名,一般可以在文件名的前面生成一个UUID作为前缀。
2.限制文件的大小
- 我们可以通过对拦截器设置参数,来设置单个文件的大小
maximumSize --> 单个文件的大小,默认大小为2m,单位是字节
当文件超过限制的大小时,会将页面转到input结果指向的页面
可以在国际化资源文件中通过struts.messages.error.file.too.large
来指定错误消息。
- 默认Struts2对文件总大小的限制为2M
-超过大小以后会转到input的结果,会报一个action的error
3.限制文件的类型
allowedTypes
- 允许的MIME类型
可以通过struts.messages.error.content.type.not.allowed来设置错误消息
allowedExtensions
- 允许的扩展名
可以通过struts.messages.error.file.extension.not.allowed来设置错误消息
文件的总大小需要在struts配置文件中来修改一个常量:
<constant name="struts.multipart.maxSize" value="5097152"></constant>
通过struts.messages.upload.error.SizeLimitExceededException来设置错误消息
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">57,408</param>
<param name="fileUpload.allowedTypes">image/pjpeg,image/gif</param>
<param name="fileUpload.allowedExtensions">jpg,gif</param>
</interceptor-ref>
</interceptor-stack>
- 下载
> 将服务器中的资源下载到本地。
> 关键点:
1.流(要下载的文件的流)
2.设置两个相应头(ContentType文件的类型, contentDisposition告诉浏览器如何处理流)
> 在Struts2中,如果想让用户下载文件,则返回的result需要指定为stream,在该类型中有如下几个参数需要配置:
contentType 文件的类型,该属性我们在Action中动态的获取
contentLength 文件的大小,用来在浏览器下载时显示一个进度条,告诉浏览器文件的大小
contentDisposition 告诉浏览器如何处理,设置为attachment;filename=文件名
inputName 被下载文件的流的名字,默认是inputStream,一般不修改
bufferSize 指定输出是缓存的大小,默认值1024
allowCaching 是否允许使用缓存 (default = true)
contentCharSet 指定字符编码,一般也不设置
> Action中的属性
// 被下载文件的流
private InputStream inputStream;
// 被下载文件的类型MIME值
private String contentType;
// 被下载文件的大小
private Long contentLength;
// contentDisposition响应头的信息
private String contentDisposition;
> execute方法
public String execute() throws Exception {
//获取ServletContext
ServletContext servletContext = ServletActionContext.getServletContext();
//获取文件的路径
String realPath = servletContext.getRealPath("/WEB-INF/file/有多少爱可以重来.mp3");
//获取文件的流
inputStream = new FileInputStream(realPath);
//设置文件的类型
contentType = servletContext.getMimeType(realPath);
//获取文件的长度
contentLength = new File(realPath).length();
//设置文件名
String fileName = "有多少爱可以重来.mp3";
fileName = new String(fileName.getBytes("gbk"),"iso8859-1");
contentDisposition = "attachment;filename="+fileName;
return SUCCESS;
}
> result配置
<result type="stream">
<param name="bufferSize">2048</param>
</result>
4.表单重复提交
> 同一个表单在不刷新的情况下多次向服务器提交请求。
> 危害:
1.会向数据库中添加大量的垃圾数据
2.服务器处理多次相同的请求,会造成服务器的压力较大
> 表单重复提交的三种情况:
1.提交请求后,重复刷新成功页面
2.提交请求后,回退不刷新页面,再次提交
3.多次点击提交按钮
> token(令牌)
1.用户在访问页面时,我们要生成一个随机的token值
2.将该值放入到session域中,同时放在表单隐藏域中
3.用户提交请求时,隐藏域中token将会一起提交
4.服务器在处理请求时先要检查请求参数中的token和session中的token是否一致
如果一致,则处理请求
否则,返回错误页面
5.注意,token使用过一次以后必须要从session域中移除
> Struts2中的使用:
1.我们直接在表单中使用<s:token></s:token>标签,
它可以自动在表单创建一个表单隐藏域,并生成一个随机的token
同事它也会将该token放入到session域中
2.我们希望有个拦截器可以在请求到达action之前来替我们验证token值
Struts2给我们提供了两个用来检查token的拦截器:
这两个拦截器检查token的机制相同,检查请求参数中的token和Session中token是否一致
检查完毕之后都会从session中移除token
不同的是token 会在没通过验证时,将页面转到一个错误页面
而tokenSession会继续转到action执行完毕以后的页面,但是action并不会执行
token
-token拦截器,会在检查token错误时,将请求转到result的name为invalid.token的结果。
- 使用token拦截器之前,需要先在action中引入token拦截器
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="invalid.token">/token-error.jsp</result>
tokenSession
-tokenSession拦截器在发现表单重复提交以后,并不会转到错误页面,
而是继续返回执行成功页面,但是它不会执行action的方法
<interceptor-ref name="tokenSession"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result>/success.jsp</result>
<result name="invalid.token">/token-error.jsp</result>
5.输入验证
> 我们所谓的输入验证,就是指检查用户输入的内容是否符合规范。
> 之前输入验证我们都是通过JS代码来检查,JS在检查之前请求还没有到达服务器。
> 如果仅仅使用JS来验证,JS的验证时可以跳过的。
> 所以大部分情况下,我们还需要在后台的程序中对输入进行验证。
> Struts2为我们提供了两种验证方式:声明式验证 还有 编程式验证
> 声明式验证又分为两种验证:字段验证,非字段验证
- 在Struts2中进行声明式验证,只需要向action所在的包导入一个配置文件,并在配置文件中来配置规则。
- 在struts-2.3.15.3\apps\struts2-blank\WEB-INF\classes\example有一个Login-validation.xml
这是一个验证配置文件的实例。
- 配置文件的命名规则:ActionClassName-validation.xml
<field name="username">
<field-validator type="requiredstring">
<message>用户名必须填写!</message>
</field-validator>
</field>
field 标签创建一个字段验证:
name属性指向的是要验证的字段的名字
field-validator 标签指定一个验证的规则
type属性执行的一个验证器
message标签指定出现错误以后显示的错误信息
> 如果以这种方式为配置文件命名:
ActionClassName-validation.xml
则该配置文件的内容会对整个action中的方法都起作用。
所以一般在ActionName-validation.xml配置都是一些通用的规则。
> 我们希望的是一个请求对应一个验证规则,那这时候文件名可以这样写:
ActionClassName-ActionName-validation.xml
比如说 UserAction中user_add请求可以这样写
UserAction-user_add-validation.xml
那这样该配置文件只会对user_add请求起作用。
> 像确认密码这种不需要验证本身的规则,而只是验证是否与密码一致这种验证我们成为非字段验证。
我们使用validator标签来创建一个非字段验证,非字段验证主要是检查字段之间逻辑关系
<validator type="expression">
<param name="expression">password==rePwd</param>
<message>两次输入的密码不一致!</message>
</validator>
如果没有通过非字段验证,则会在值栈中放入一个actionError,我们需要使用:
<s:actionerror/>标签来显示错误信息
> 短路验证:
当上一个验证规则没有通过时,则以后的验证也不在进行验证。
要设置短路验证只需要在field-validator设置如下属性
short-circuit="true"
> 原理
我们所使用的这些验证规则,在框架中都有对应的类来支持,所以使用声明式验证时实际上是在调用那个类来检查规则。
> 有些情况自带的验证器并不能满足我们的需求,这时我们还可以自定义验证器(了解)
> 验证身份证号码是否为18位
1.创建一个类并继承FieldValidatorSupport
@Override
public void validate(Object object) throws ValidationException {
//1.获取要验证的字段
String fieldName = getFieldName();
String val = (String) getFieldValue(fieldName, object);
//2.验证
//检查身份证号码,是否为18位
//3.返回一个结果
if(val == null || val.trim().length()!=18){
//没通过验证
addFieldError(fieldName, object);
}
}
2.编写配置文件
在类路径下创建一个validators.xml配置文件,并在该文件中注册自定义验证器
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator Definition 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-definition-1.0.dtd">
<validators>
<validator name="idCardValidator" class="com.atguigu.struts2.validator.IDValidator"/>
</validators>
3.在项目中使用
<field name="id">
<field-validator type="idCardValidator">
<message>身份证号不足18位!</message>
</field-validator>
</field>
补充:
像s:textfield标签中label属性,它默认不会被OGNL解析,但是有些情况,我们希望它可以被OGNL解析
如果想让某个内容被强制OGNL解析,则可以这么写 %{OGNL表达式}