Struts2学习-4 表单校验
什么时候使用的是OGNL标签:
1.通用的标签
2.UI的标签
3.ajax的标签:Ajax Tags
以上3中标签有可能会使用的是OGNL标签
为什么学习Struts2呢?
a、企业中用的多
b、灵活--->可配置(越灵活的东东越散,感觉就越乱)
框架就是使程序员按部就班的开发程序(重复性的编码工作弄成了框架),省下的时间去研究业务(需求)。
表单效验
普通式的开发,用声明式的,细致的控制,用编程式的验证
1、编程式验证:
用代码,表达式的方式
前提:动作类一般要求继承ActionSupport
struts.xml配置文件中,对要验证的动作,需要提供name="input"的结果视图(回显)
<action name="RegUser" class="com.itheima.action.UserAction" method="RegUser">
<result type="dispatcher" name="success">/WEB-INF/pages/main.jsp</result>
<result type="dispatcher" name="error">/WEB-INF/pages/commons/error.jsp</result>
<!-- 出现错误时转向的页面:回显 -->
<result name="input">/WEB-INF/pages/regist.jsp</result>
</action>
|
针对所有动作进行验证:需要覆盖public void validate()方法,方法内部如果不满足要求,调用addFieldError填充信息.
对于这种写法,不需要验证的动作方法,请使用@SkipValidation
不能验证先RegUserUI,是先进行拦截,但是这个类还没有实例化,是不能进行拦截的。
使用org.apache.commons.lang3.StringUtils工具包简化代码。针对某些动作的验证是没有简化的代码.
@SkipValidation//用在不需要验证的动作方法上
public String RegUserUI() {
return SUCCESS;
}
//对所有的动作方法进行校验
public void validate(){
//写你的校验代码ActionSupport里面有addFieldError()方法,把错误信息存起来.
if(StringUtils.isEmpty(user.getUsername())){
addFieldError("username", "请输入用户名");//向一个Map中存储错误消息。何时返回input视图,是由该Map中有无信息决定的。
}
}
|
针对某些动作进行验证: public String regUser(){}
验证方法:public Void validateRegUser(){};注意方法中的首字母要大写 RegUser();
//针对某个动作方法进行校验
public void validateRegUser() {
// 写你的校验代码
if (user.getUsername() == null || user.getUsername().equals("")) {
addFieldError("username", "请输入用户名");
}
}
|
2、声明式验证:
(开发的时候,可以不写验证,后续增加声明式验证,写配置文件即可,编码式验证需要开始就要写)
2.1struts2中已经内置一些验证器:(com.opensymphony.xwork2.validator.validators.default.xml)
注解验证配置文件的设置
2.1.1,打开xwork-core-2.*.jar包中的xwork-validator-1.*.dtd文件,复制表头
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
|
2.1.2,进行文件配置。具体参考如下。
2.2如何使用内置验证器:
对所有的动作方法都进行验证: 在动作类相同的包中:动作类名-validation.xml使用。
针对某些动作进行验证:动作类名-动作别名-validation.xml(动作别名指Struts.xml中的action的name)
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.2.dtd">
<validators>
<!-- 针对字段的验证:方式一(建议使用).在一个字段上加上多个验证规则-->
<field name="username" >
<field-validator type="requiredstring">
<message>用户名是必须的</message>
</field-validator>
</field>
<!-- 针对字段的验证:方式二.
<validator type="requiredstring">
<!-- 校验器中有trim方法,当把这个方法设置为false,那么用户名前后可以有空格 -->
<param name="trim">false</param>
<param name="fieldName">username</param>
<message>必须的用户名</message>
</validator>-->
<!-- 在使用配置文件的時候,只使用一种配置方式 -->
<field name="password">
<field-validator type="strongpassword">
<message>密码不够强壮</message>
</field-validator>
</field>
</validators>
|
3、自定义声明式验证器:(具体看案例)
a、编写一个类,继承FieldValidatorSupport,复写public void validate(Object object) throws ValidationException
b、定义自定义的验证器:在WEB-INF/classes目录下建立一个validators.xml的配置文件。
案例:表单验证
先写struts配置页面
<package name="p1" namespace="/user" extends="struts-default">
<action name="RegUserUI" class="com.itheima.action.UserAction" method="RegUserUI">
<result type="dispatcher" name="success">/WEB-INF/pages/regist.jsp</result>
</action>
<action name="RegUser" class="com.itheima.action.UserAction" method="RegUser">
<result type="dispatcher" name="success">/WEB-INF/pages/main.jsp</result>
<result type="dispatcher" name="error">/WEB-INF/pages/commons/error.jsp</result>
<!-- 出现错误时转向的页面:回显 -->
<result name="input">/WEB-INF/pages/regist.jsp</result>
</action>
</package>
|
写一个regist.jsp页面
<s:fielderror fieldName="password" />标签标示在此显示错误信息,
里面的属性标示显示该属性的错误信息,如果不写就显示所有错误信息
requiredLabel="true"表示在用户名旁边加 *(单纯加*,没有其他作用); requiredPosition="left"表示 * 在左边
<head>
<!-- 这个标签对是错误提示的样式模板 -->
<s:head/>
<title>新用户注册</title>
<!-- 以下样式出现在s:head标签之下,覆盖了此标签的样式,所以这个为准 -->
<style type="text/css">
.errorMessage{
color: red;
}
</style>
|
<s:fielderror fieldName="password" />
<s:form action="RegUser" namespace="/user">
<s:textfield key="hello" label="用户名" name="username" requiredLabel="true" requiredPosition="left"></s:textfield>
<s:textfield label="昵称" name="nick"></s:textfield>
<s:password label="密码" name="password"></s:password>
</s:form>
|
写一个UserAction类
public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
private Map<String, String> hobbies = new HashMap<String, String>();
public Map<String, String> getHobbies() {
hobbies.put("eat", "吃饭");
hobbies.put("sleep", "睡觉");
hobbies.put("study", "学java");
return hobbies;
}
public void setHobbies(Map<String, String> hobbies) {
this.hobbies = hobbies;
}
public String RegUser() {
try {
System.out.println(user);
// 调用service,保存数据
System.out.println("调用后台service,保存数据到数据库中");
return SUCCESS;
} catch (Exception e) {
e.printStackTrace();
return ERROR;
}
}
@SkipValidation//用在不需要验证的动作方法上
public String RegUserUI() {
return SUCCESS;
}
//对所有的动作方法进行校验
// public void validate(){
// //写你的校验代码
// if(StringUtils.isEmpty(user.getUsername())){
// addFieldError("username", "请输入用户名");//向一个Map中存储错误消息。何时返回input视图,是由该Map中有无信息决定的。
// }
// }
//针对某个动作方法进行校验
// public void validateRegUser() {
// // 写你的校验代码
// if (user.getUsername() == null || user.getUsername().equals("")) {
// addFieldError("username", "请输入用户名");
// }
// }
public User getModel() {
return user;
}
}
|
使用声明式校验器
<validators>
<!-- 针对字段的验证:方式一(建议使用).在一个字段上加上多个验证规则-->
<field name="username" >
<field-validator type="requiredstring">
<message>用户名是必须的</message>
</field-validator>
</field>
<!-- 针对字段的验证:方式二.
<validator type="requiredstring">
<!-- 校验器中有trim方法,当把这个方法设置为false,那么用户名前后可以有空格 -->
<param name="trim">false</param>
<param name="fieldName">username</param>
<message>必须的用户名</message>
</validator>-->
<!-- 在使用配置文件的時候,只使用一种配置方式 -->
</validators>
|
建立StrongpasswordFieldValidate(编写一个自定义校验器)
public class StrongpasswordFieldValidate extends FieldValidatorSupport {
//object就是当前执行的动作类的实例
public void validate(Object object) throws ValidationException {
String fieldName = getFieldName();
Object value = getFieldValue(fieldName, object);
//验证
if(!(value instanceof String)){
addFieldError(fieldName, object);
}else{
String s = (String)value;
if(!isStrong(s)){
addFieldError(fieldName, object);
}
}
}
//判断s是否强大
private static final String GROUP1 = "abcdefghijklmnopqrstuvwxyz";
private static final String GROUP2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String GROUP3 = "0123456789";
private boolean isStrong(String s) {
boolean ok1 = false;
boolean ok2 = false;
boolean ok3 = false;
int length = s.length();
for(int i=0;i<length;i++){
if(ok1&&ok2&&ok3)
break;
String character = s.substring(i,i+1);
if(GROUP1.contains(character)){
ok1 = true;
continue;
}
if(GROUP2.contains(character)){
ok2 = true;
continue;
}
if(GROUP3.contains(character)){
ok3 = true;
continue;
}
}
return ok1&&ok2&&ok3;
}
}
|
对自定义的校验器进行配置
<?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="strongpassword" class="com.itheima.validators.StrongpasswordFieldValidate"/>
</validators>
|
使用自定义的校验器(struts.xml中配置)
<field name="password">
<field-validator type="strongpassword">
<message>密码不够强壮</message>
</field-validator>
</field>
|
类型转换器
先写配置文件struts.xml
<result name="input">regist.jsp</result>这个input验证也需要,转换也需要.这里的目的是回显数据
<package name="p1" namespace="/user" extends="struts-default">
<action name="RegUserUI" class="com.itheima.action.UserAction" method="RegUserUI">
<result type="dispatcher" name="success">/WEB-INF/pages/regist.jsp</result>
</action>
<action name="RegUser" class="com.itheima.action.UserAction" method="RegUser">
<result type="dispatcher" name="success">/WEB-INF/pages/main.jsp</result>
<result type="dispatcher" name="error">/WEB-INF/pages/commons/error.jsp</result>
<result name="input">/WEB-INF/pages/regist.jsp</result>
</action>
</package>
|
写一个JavaBean,定义四种类型的变量,演示.
public class User implements Serializable {
private String username;//字符串类型
private Integer age;//不要写int型,因为页面会默认为0
private Date birthday;//Date型,转换器会自动转换String
private Point luckpoint = new Point(438,520);//自定义类型
|
写一个动作类UserAction
public class UserAction extends ActionSupport implements ModelDriven<User> {
private User user = new User();
public String RegUserUI() {
return SUCCESS;
}
public String RegUser() {
try {
System.out.println(user);
// 调用service,保存数据
System.out.println("调用后台service,保存数据到数据库中");
return SUCCESS;
} catch (Exception e) {
e.printStackTrace();
return ERROR;
}
}
public User getModel() {
return user;
}
}
|
类型转换错误消息设置
1,在动作类所在的包中创建一个文件,命名规则ClassName.properties
2,在该文件中增加以下内容:
invalid.fieldvalue.fieldName=你的消息 fieldName是动作类中对应的字段.
3,或者在国际化的消息资源包中增加以下内容,原理和国际化的东东一样
注意:
转换的时候,String和Integer基本类型的不用管,因为struts会自己转换
Date类型的也不用管,但是需要注意Date是地区敏感信息,需要与本地Date格式一致.最好是选择.
自定义类的类型转换
先建立一个Point
public class Point {
private Integer x;
private Integer y;
public Point(){}
public Point(Integer x,Integer y){
this.x = x;
this.y = y;
}
|
再建立一个PointConveor自定义类型转换器,继承StrutsTypeConverter,重写两个方法
public class PointConvertor extends StrutsTypeConverter {
public PointConvertor(){
System.out.println("调用了默认的构造方法");
}
//字符串类型转换成其他类型:String--->Point
public Object convertFromString(Map context, String[] values, Class toClass) {
if(toClass==Point.class){
String xy = values[0];
String s[] = xy.split("\\,");
return new Point(Integer.parseInt(s[0]),Integer.parseInt(s[1]));
}
return null;
}
//其他类型转换成字符串:用在显示的时候.struts对此有bug
public String convertToString(Map context, Object o) {
if(o instanceof Point){
Point p = (Point)o;
return p.getX()+","+p.getY();
}
return null;
}
}
|
自定义类型转换器完成后,需要对其进行配置,建立User
局部类型转换器设置
全局类型转换器设置
在src中建立xwork-conversion.properties,里面的值为(JavaBean类全名=转换器类全名)
注册类型转换器:
局部类型转换器:
要转换的属性所在的类相同的包下:建立类名-conversion.properties的配置文件
luckpoint=com.itheima.convertor.PointConvertor内容:字段名=自定义的类型转换器的类全名 |
全局类型转换器:
在WEB-INF/classes目录下,建立一个名为xwork-conversion.properties的配置文件
com.itheima.domain.Point=com.itheima.convertor.PointConvertor //内容:目标类型的类全名=自定义的类型转换器的类全名 |
文件的上传和下载
文件上传是由拦截器来实现的
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
先设置配置文件struts.xml(别忘了打开静态开关)
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<package name="default" extends="struts-default">
<action name="uploadUI" class="com.itheima.action.UploadAction" method="uploadUI">
<result>/upload.jsp</result>
</action>
<action name="upload" class="com.itheima.action.UploadAction" method="upload">
<result>/1.jsp</result>
<result name="input">/upload.jsp</result>
<result name="error">/error.jsp</result>
</action>
<action name="download" class="com.itheima.action.UploadAction" method="download">
<result type="stream">
<param name="contentType">application/octet-stream</param>
<!-- 下载中文名称的文件:在配置文件中使用OGNL表达式对中文文件名进行URL编码-->
<param name="contentDisposition">attachment;filename=${@[email protected](photoFileName,'UTF-8')}</param>
<param name="inputName">myInputStream</param>
</result>
</action>
</package>
</struts>
|
写一个动作类,UploadAction继承ActionSupport.
建立4个变量,并生成set和get方法.其中photoContentType的set方法,能够被框架执行,并存进来MIME类型
public class UploadAction extends ActionSupport {
private File photo;
private String photoFileName;
private String photoContentType;
private InputStream myInputStream;
|
public String download() throws FileNotFoundException{
photoFileName = "整容广告.jpg";
//就是myInputStream赋值
String storeDirectory = ServletActionContext.getServletContext().getRealPath("/files/整容广告.jpg");
myInputStream = new FileInputStream(storeDirectory);
return SUCCESS;
}
public String upload(){
try {
InputStream in = new FileInputStream(photo);
String storeDirectory = ServletActionContext.getServletContext().getRealPath("/files");
OutputStream out = new FileOutputStream(storeDirectory+"/"+photoFileName);
int len = -1;
byte b[] = new byte[1024];
while((len=in.read(b))!=-1){
out.write(b, 0, len);
}
in.close();
out.close();
return SUCCESS;
} catch (Exception e) {
e.printStackTrace();
return ERROR;
}
}
|
上传jsp页面
<body>
<s:form action="upload" enctype="multipart/form-data">
<s:file label="靓照" name="photo"></s:file>
<s:submit value="上传"></s:submit>
</s:form>
</body>
|
使用标准插件---JFreeChart使用(插件其实就是一个结果视图)
所需jar包:
/jfreechart-1.0.13.jar/
jcommon-1.0.16.jar/
struts2-jfreechart-plugin-2.1.8.1.jar
建立ChartAction动作类
public class ChartAction extends ActionSupport {
private JFreeChart chart;
public JFreeChart getChart() {
return chart;
}
public String execute() throws Exception {
ValueAxis xAxis = new NumberAxis("年度");
ValueAxis yAxis = new NumberAxis("产值");
XYSeries xySeries = new XYSeries("绿豆");
xySeries.add(0,300);
xySeries.add(1,200);
xySeries.add(2,400);
xySeries.add(3,500);
xySeries.add(4,600);
xySeries.add(5,500);
xySeries.add(6,800);
xySeries.add(7,1000);
xySeries.add(8,1100);
XYSeriesCollection xyDataset = new XYSeriesCollection(xySeries);
XYPlot xyPlot = new XYPlot(xyDataset,xAxis,yAxis,new StandardXYItemRenderer(StandardXYItemRenderer.SHAPES_AND_LINES));
chart = new JFreeChart(xyPlot);
return SUCCESS;
}
}
|
进行配置
<action name="showchart" class="com.itheima.action.ChartAction">
<result name="success" type="chart">
<param name="height">400</param>
<param name="width">600</param>
</result>
</action>
|
jsp页面
<body>
上传成功!<br/>
<s:url action="showchart" var="url"></s:url>
<img alt="图图" src="${url}">
</body>
|
使用插件,必须要先知道插件的用途,一般只要组织数据就好了,没必要深究.
伪装URL地址
建立JavaBean,User,建立3个变量,写构造方法get和set方法,还有toString方法
public class User {
private Integer id;
private String username;
private String nick;
public User(){}
public User(Integer id, String username, String nick) {
super();
this.id = id;
this.username = username;
this.nick = nick;
}
|
写一个动作类
public class UserAction extends ActionSupport{
private Integer id;
private List<User> users;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<User> getUsers() {
users = new ArrayList<User>();
users.add(new User(1, "wangchao", "超人"));
users.add(new User(2,"renmin","敏姐"));
users.add(new User(3,"renmin","敏姐"));
users.add(new User(4,"renmin","敏姐"));
users.add(new User(5,"renmin","敏姐"));
return users;
}
public String showAll(){
return SUCCESS;
}
public String queryOne(){
//根据id的值,调用serivce,找到那个用户
List<User> us = getUsers();
for(User u:us){
if(id.equals(u.getId())){
System.out.println(u);
}
}
return null;
}
}
|
进行struts.xml文件配置
<constant name="struts.devMode" value="true" />
<constant name="struts.action.extension" value="html"></constant>
<constant name="struts.enable.SlashesInActionNames" value="true"></constant>
<!-- <constant name="struts.mapper.alwaysSelectFullNamespace" value="true"></constant> -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<package name="default" extends="struts-default">
<action name="showAll" class="com.itheima.action.UserAction" method="showAll">
<result>/list.jsp</result>
</action>
<action name="users/*" class="com.itheima.action.UserAction" method="queryOne">
<param name="id">{1}</param>
</action>
</package>
|
写一个jsp页面
<body>
<table border="1">
<tr>
<th>ID</th>
<th>USERNAME</th>
<th>NICK</th>
<th>OPERATION</th>
</tr>
<s:iterator value="users" status="s">
<tr bgcolor='<s:property value="#s.odd?'#c3f3c3':'#f3c3f3'"/>'>
<td>
<s:property value="id"/>
</td>
<td>${username}</td>
<td>${nick}</td>
<td>
<a href="${pageContext.request.contextPath}/users/${id}.html">查询一个</a>
</td>
</tr>
</s:iterator>
</table>
</body>
|
防止表单重复提交
a在输入表单中加入<s:token/>标签
b在要防止重复提交的动作配置中加入token拦截器
c提交会出错,需要配置一个结果
d如果在返回的页面中要显示错误消息提示,使用s:actionErrors
e要覆盖默认的提示,请在国际消息资源文件中加入struts.messages.invalid.token=你的提示信息
1,写一个配置文件需要加入token拦截器
<!-- Redirect After Post(提交完后,请进行重定向,可以防止表单重复提交)
<action name="addUser" class="com.itheima.action.UserAction" method="addUser">
<result type="redirect">/success.jsp</result>
</action>
-->
<action name="addUser" class="com.itheima.action.UserAction" method="addUser">
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result>/success.jsp</result>
<!-- 第一个结果视图是invalid.token -->
<result name="invalid.token">/regist.jsp</result>
</action>
|
2.写jsp页面
<body>
<s:actionerror/><!--显示错误消息提示.
<s:form action="addUser">
<s:textfield label="用户名" name="username"></s:textfield>
<s:textfield label="昵称" name="nick"></s:textfield>
<s:submit name="submit" value="注册"></s:submit>
<s:token></s:token>
</s:form>
</body>
|