Struts2_2_解决配置文件冗余_动作类对象数据封装_数据类型转换_表单数据信息提示

Struts2第二天学习

解决struts.xml文件的冗余

将所有的配置文件放在一份struts.xml将会导致, 文件繁杂且容易出错, 将有如下的解决方案:

分文件编写Struts2的配置文件:对于具有类似功能的action就可以放入不同对应的xml文件中, 最后使用struts标签中的<include file="user.xml"></include>处理例如:1. struts.xml<struts><constant name="struts.devMode" value="true"></constant><include file="A.xml"></include><include file="B.xml"></include></struts>2. A.xml<struts><package name="A" extends="struts-default"><action name="addA"><result>/success.jsp</result></action></package>	</struts>3. B.xml<struts><package name="B" extends="struts-default"><action name="addB"><result>/success.jsp</result></action></package>	</struts>

封装请求数据到对象中

  • 静态参数封装: 使用struts.xml配置param参数, 调用动作类的setXXX方法做数据注入,默认编码utf-8, 不会出现乱码问题
例: struts.xml配置<struts><package name="user" extends="struts-default"><action name="addUser" class="com.action.UserAction" method="saveUser"><param name="username">A</param><param name="age">18</param></action></package>	</struts>在动作类中的配置:public class UserAction extends ActionSupport{private String username;private int age;public String getUsername(){return username;}public void setUsername(String username){this.username = username;}public int getAge(){return age;}public int setAge(){this.age = age;}public String saveUser(){ //action方法return null;	}	}
当访问index.jsp中addUser操作的时候, struts.xml触发saveUser的action, 将username, age中参数通过UserAction中的set方法做注入操作
整个注入的过程中, 首先会自动为UserAction生成一个user对象, 将username, age通过set方式进行赋值
Struts2是通过staticParams的拦截器做注入操作, 这里注意必须保证, 注入的param标签中的name必须与Action类中的属性同名
在param中的age参数18是作为字符串出现的, 当使用set操作的时候, 字符串自动转换为数字
  • 动态参数封装: 就是对用户提交的表单数据封装到实体模型中
当用户使用表单提交数据的时候, 在Servlet中, 直接使用request.getParameter("参数名")处理
而在Struts2中, 系统默认将表单数据一一通过set操作封装到Action类的属性中
在struts.xml不需要做参数配置, 例如:<action name="addUser" class="com.action.UserAction" method="saveUser"></action>无需指定param
注: 此处的操作是由params拦截器完成的, 通过拦截器将表单数据填入对应的Action类中同名同类型使用此种方式出现的问题是: Action不应该处理对JavaBean数据的处理, Action的主要功能是对用户操作的反馈针对上述的问题, 提出如下的操作

1. 动作类与实体模型分开

在动作类中不采用上面的方式(将username, age等属性放入动作类), 而是将数据单独封装为User类处理
例:public class UserAction extends ActionSupport{private User user;public String getUser(){return user;}public void setUser(User use){this.user = user;}public String saveUser(){ //action方法return null;	}	}
在index中, 作如下处理
用户名: <input name="text" name="user.username"/>
此处说明: user.username就类似于Servlet中的将数据存入request, session中传递
注: 在index.jsp中要注意: user.username中, user必须与Action中属性名相同, username必须与User中的属性名相同在表单数据封装到实体类中分为以下2个步骤:1. 通过Action类中的get方法查看当前User实例是否存在, 不存在则new一个, 然后使用Action中的set方法为bean引用赋值2. 调用action中get方法获得bean对象, 通过注入的方式为实例对象赋值, 整个过程中在struts.xml无需做配置, 过滤器会自动将表单中数据封装到action中对象上
此时的赋值一样是param拦截器进行处理
需要注意的, 当在上述的1操作中, 如果bean的实例对象存在, 那么就不会执行set操作, 直接执行get操作, 最后对该实例对象赋值

2. 在动作类与实体模型分开的基础上, 进行模型驱动

如果只是单纯地将动作类与实体模型分开, 容易出现的问题就是在写jsp的过程中必须保证, 模型变量之间的联系, 如果某个地方的变量名错误, 很容易导致错误
当采用模型驱动的方式, 在一定程度上可以减轻jsp的开发
如标题所说, 模型驱动就是使用已存在的bean实例驱使jsp中表单数据进行封装, 而不是让jsp去指定向哪个实例中的哪个属性进行封装数据
模型驱动的2个步骤:
1. 让Action实现ModelDriven接口, 实现getModel方法(返回值泛型)
2. 使用模型驱动, 数据模型必须自己实例化

例:Action类: public class UserAction extends ActionSupport implements ModelDriven<User>{private User user = new User();public User getModel(){//重写ModelDriven中的getModelreturn user;}public String registerUser(){return null;	}}index.jsp:用户名:<input type="text" name="username"/><br/>年龄:<input type="text" name="age"/><br/><input type="submit" value="注册"/>struts.xml:<action name="registerUser" class="com.action.UserAction" method="registerUser"></action>

注: 模型驱动使用的是ModelDriven拦截器

数据类型转换

  • 需要注意的是, 通过客户端填写的数据都是String或String[]
 - 获取表单中字符串数据 -> 使用set方法填充模型数据 -> POJO- POJO -> 使用get方法获取数据 -> jsp显示(字符串类型)
注: 在set填充数据的时候Struts2会根据数据类型做自动转换处理

注: 系统的类型转换在某些方面可能不满足实际需求, 需要我们自己定义类型转换器.

  • 下面介绍一下自定类型转换器

  • 在Struts2中具有一个TypeConvert的接口, 当实现该接口就可完成自定义类型的转换, 存在的问题是该接口中的convertValue方法显得过于繁琐, 因此一般采用继承TypeConvert的子类解决问题

  • 编写类继承TypeConvert的子类StrutsTypeConverter, 实现抽象方法: public abstract Object convertFromString(Map context, String[] values, Class toClass); 以及 public abstract String convertToString(Map context, Object o);

convertFromString是将用户输入的String数据转化为指定类型
convertToString是将指定该类型转化为String类型
context: 存放动作访问的所有数据
values: 存放用户输入的值
toClass: 要转换的目标类

以转换日期为例:

public class MyTypeConverter extends StrutsTypeConverter{private DateFormat df = new SimpleDateFormat("mm/dd/yyyy"); //例如将1/1/2019变为字符串, 按照当地时间格式存入数据库//重写convertFormStringpublic Object convertFromString(Map context, String[] values, Class toClass){if (values == null || values.length == 0){return null;}String str = values[0];if(toClass == java.util.Date.class){try{return df.parse(str);} catch(Exception e){e.printStackTrace()	return null;}}}return null;
}
//重写convertToString
public abstract String convertToString(Map context, Object o){if(o instanceof Date){return df.format((Date)o);}	return null;
}
  • 注册转换器
 - 局部注册: 在javaBean包下建立一个 javaBeanName-conversion.properties的文件javaBeanName代表类名, 文件名必须按照上面的格式在.properties文件中将需要转换的属性注册转换器例: birthday=com.convert..MyTypeConverter- 全局类型转换器: 需要使用转换的数据类型注册转换器在src路径下, 建立一个xwork-conversion.properties属性的文件, 这里的文件名也是固定的例: java.util.Date=com.convert..MyTypeConverter此处由于针对全局元素, 所以直接将Data类型作为转换器注册对象

经过上述的步骤就能将指定类型转换为本身需要的类型

转换失败后的处理

转换失败后不能将异常抛给用户, 所以, 一般采用如下操作:

在结果视图标签中, 添加<result name="input">/index.jsp</result>
当出现异常的时候, Struts2会自动切换到input视图

处理input回显数据

常规的Servlet回显数据使用的是将数据存入request中, 通过EL表达式在表单中回显数据
而Struts2采用的是配置标签, 使用Struts2自带标签, 在使用input回显操作的时候将会对数据做自动填充, 填充至表单中, Struts2中自带的标签就是对原本的html标签的一种修饰以及功能加强
如下操作:

<%@ taglib uri="/struts-tags" perfix="s"%> <%--使用Struts2自带标签--%>
<s:form><s:textfield name="username" label="用户名"></s:textfield><s:textfield name="password" label="密码"></s:password>
</s:form>
剩余标签以及内部属性,自行查阅
当使用Struts2提供的标签库的时候, 数据的回显, 不再需要"request封装"

表单信息提示

当用户提交表单数据的时候, 需要对表单数据做验证操作, 如果信息错误则给用户一个错误提示, 在Servlet中, 采用的是request封装数据, 结合js做验证
在Struts2中采用的也是标签配置, 例如:

<s:fielderror></s:fielderror> <%--字段错误提示, 此处的s借用上面的--%>
<s:actionerror/> <%--动作错误--%>
当字段出现错误的时候, 做数据回显, 会作自动的提示操作, 告诉用户对应的输入出现什么样的错误
注: 当出现数据类型转化错误, 将会触发conversionError的拦截器(前提必须是Action继承ActionSupport), 该拦截器将会进入input视图如果没有对字段信息做处理, 将会以默认的方式显示, 显示一堆英文对用户不友好, 所以下面将针对编写自定义信息提示做叙述

自定义表单数据信息提示以及验证规则

一般采用客户端与服务端共同对信息进行验证
下面介绍Struts2的服务端验证, 下面的所有的操作都是基于Action类继承ActionSupport来处理的

  • 自定义表单数据信息提示
form表单中的数据总是针对JavaBean某个字段而言, 所以有以下处理:
在JavaBean路径下, 新建一个与Bean类名相同的properties的文件, 然后针对需要自定义的字段进行信息配置
例如:在User.properties中invalid.fieldvalue.birthday=出生日期有误, 使用yyyy-MM-dd格式注: 在properties文件中, 中文会自动用Unicode编码代替文件的key必须是invalid.fieldvalue.属性名文件value代表显示的中文提示信息
  • 处理Action类中动作方法的验证
    使用重写validate方法解决验证
在ActionSupport中提供了一个validate方法, 那是系统默认的验证操作, 自定义验证规则就需要重写父类方法
例:public void validate(){if (StringUtils.isEmpty(user.getUsername)){addFieldError("username", "用户名必须输入");}	}
//在Struts2中提供一个Map用于封装操作错误信息, 通过addFieldError方法添加错误信息
//"username"代表错误信息的字段名, "用户名必须输入"代表错误提示信息

注: 使用上面的validate操作具有一个弊端, 就是会对所有的动作操作做验证规则, 如上面的操作, 如果user的username一直为空, 当用户执行其他动作操作, 就会报错
例如:

在Action类中具有一个findAll操作
public String findAll(){return SUCCESS;
}
当用户没有注册( 此时的username为null )而去执行findAll操作, 出现的问题就是: 即使findAll操作成功. 但是依旧抛异常
出现这种情况的原因就是: validate对所有的动作都进行验证

解决上述问题, 采用有如下的方案:

  • 给不需要验证的动作添加一个@SkipValidation注解
    如上面的操作, 为findAll添加一个@SkipValidation注解, 使得跳过findAll, 不去验证它

  • validation方法遵循书写规范
    将上面的validate改为validateRegist, 使得用户注册的时候只验证regist动作, 而不去验证findAll动作

声明式验证, 不使用函数式验证方式, 而是编写xml配置文件, 制定验证规则
在动作类所在的目录下, 新建一个 动作类类名-validation.xml的文件
例如:

文件名: UserAction-validation.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC"-//Apache Struts//XWork Validator 1.0.3//EN""http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"><validators><field name="username"><field-validator type="requiredString"><message>请输入用户名</message></field-validator></field>	</validators>对username属性做requiredString验证(表示username不能为空)

注: 它是针对动作类中所有的动作方法做验证
在动作类所在的目录下, 新建一个 动作类类名-动作名-validation.xml的文件就可以实现对特定的动作进行验证
例如:

文件名: UserAction-regist-validation.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC"-//Apache Struts//XWork Validator 1.0.3//EN""http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd"><validators><field name="username"><field-validator type="requiredString"><message>请输入用户名</message></field-validator></field></validators>Struts.xml中代码<package name="user" extends="struts-default"><action name="findAll" class="com.action.UserAction"><result>/success.jsp</result></action><action name="regist" class="com.action.UserAction" method="regist"><result type="redirect">/success.jsp</result><result type="exists">/message.jsp</result><result type="input">/success.jsp</result> <%--视图回显--%></action></package>此时的操作就只是单纯针对于regist
  • 验证器参数注入

只是单纯地配置validation.xml并不能满足对输入数据的检查要求, 它只能做到根据field标签中type类型来检查数据, 所以需要使用验证器参数注入增强验证功能
常用验证器自行百度
验证器参数注入有两种方式, 如下:
基于字段: 先获取需要验证的字段名, 然后对其内容进行验证
例如:

  <validators><field name="username"><field-validator type="requiredString"><param name="trim">false</param><message>请输入用户名</message></field-validator></field></validators>上面针对type为requireString的验证器做trim注入, 将该验证器的trim属性设置为false表示验证器不除去空格
基于验证器: 先判断当前需要验证的类型属于哪一类, 然后再判断需要验证哪个字段
例如:
  <validators type="requiredString"><validator><param name="fieldname">username</param><message>请输入用户名</message></validator></validators>对username做注入, 表示对username做判断requiredString操作

注: 一个验证器只能处理一个验证请求, 需要对同一属性做多种条件判断, 需要多个< field-validator>, 例如对password判断, 需要判断password长度, 以及password是否为已输入.

<validators><field name="password"><field-validator type="requiredString"><message>请输入密码</message></field-validator><field-validator type="stringlength"><param name="minlength">3</param><param name="maxlength">8</param><message>密码长度${minlength}~${maxlength}</message></field-validator></field></validators>

还需要注意的是, 密码中含有特殊字符, 可以将密码作为文本处理, 防止特殊字符出错

上面有错, 还请指出, 如果认为我写的还不错, 还请点个赞, 多多支持一下, O(∩_∩)O~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468721.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高版本号chrome安装flashplayer debuger后无法使用的问题

起因应该是苹果公司指出flash player的安全问题&#xff0c;还有各种原因导致google将在未来取消NPAPI的支持&#xff0c;所以fp们就悲剧了在高版本号chrome&#xff08;42以上&#xff09;默认是关闭外部安装的插件使用的&#xff0c; 所以在地址栏通过chrome://flags/#enable…

android 7.1 apk的systemuid [2]

1 上周发的一篇文章《android 7.1 apk的systemuid和系统应用Setting相同导致开机找不到库的问题》&#xff0c;然后有个小伙伴找我讨论了&#xff0c;觉得自己的脑洞了打开了很多&#xff0c;所以针对这个问题再次做一次总结&#xff0c;如果大家有好的建议也可以在文末留言。…

python连连看小游戏_python实现连连看游戏

编程一直是在课余时间&#xff0c;放假时间自学&#xff0c;到现在为止也有半年了。这是我自己用python实现的连连看&#xff0c;也是第一个完成的游戏&#xff0c;虽然极其简陋。思路呢&#xff0c;一开始是想要从一个点出发开始寻路&#xff0c;遇到数字就换一条路&#xff0…

Struts2_3_国际化处理_自定义拦截器_文件上传及下载_OGNL

Struts2国际化处理 浏览器根据当前的语言环境自动查找对应的语言环境资源包, 使jsp显示合适的语言数据环境 Struts2实现国际化, 动作类必须继承ActionSupport 创建资源包 资源包由多个properties文件组成, properties文件的文件名必须满足命名规范: 文件名_语言代码_国家代码…

RBAC 基于角色的访问控制

RBAC&#xff08;Role-Based Access Control&#xff0c;基于角色的访问控制&#xff09;&#xff0c;就是用户通过角色与权限进行关联。简单地说&#xff0c;一个用户拥有若干角色&#xff0c;每一个角色拥有若干权限。这样&#xff0c;就构造成“用户-角色-权限”的授权模型。…

C语言书籍大全

学习过linux的同学应该都知道&#xff0c;linux系统几乎都是用c编写&#xff0c;包括现在主流的android,ios系统&#xff0c;都是使用c编程。为了方便大家学习C语言&#xff0c;小编搜集整理了市面几乎所有的C语言书籍&#xff0c;下面给出部分比较有名的&#xff0c;在微信公众…

python子进程修改父进程内变量_如何将父变量传递给python中的子进程?

这里的简单答案是&#xff1a;不要使用subprocess.Popen,使用multiprocessing.Process.或者,更好的是,multiprocessing.Pool或concurrent.futures.ProcessPoolExecutor.使用子进程,程序的Python解释器根本不了解子进程;据它所知,子进程正在运行Doom.所以没有办法直接与它共享信…

设计模式_1_工厂模式与抽象工厂

工厂模式 工厂模式(创建型模式):创建对象接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建延伸到子类进行 主要解决接口选择问题,明确计划不同条件下执行创建不同实例 通过子类实现工厂实例,创建过程在其子类执行 优点:提高扩展性,屏蔽产品具体实现,调用者只关心产品…

android 7.1 apk的systemuid相同导致问题[2]

1上周发的一篇文章《android 7.1 apk的systemuid和系统应用Setting相同导致开机找不到库的问题》&#xff0c;然后有个小伙伴找我讨论了&#xff0c;觉得自己的脑洞了打开了很多&#xff0c;所以针对这个问题再次做一次总结&#xff0c;如果大家有好的建议也可以在文末留言。2先…

uasset python_Unreal Python 结合 C++ 开发蓝图库插件

本文章转载自 智伤帝的个人博客 - 原文链接前言上个月的这个时候我写了一篇文章关于如何嵌入 PySide 调用 Qt 的 GUI 开发。 链接Python 虽然很好&#xff0c;但是有些功能&#xff0c;并没有从 C 里面暴露出来。这种情况就需要通过 C 的蓝图开发来将这部分的功能进行暴露。这样…

设计模式_2_单例模式

单例模式 单例模式(创建型模式): 涉及到的单一的类,该类只负责自己对象的创建,并且只有单个对象被创建,提供唯一的对象访问方式,可直接访问 注: 只能有一个实例作为全局的访问点, 构造函数私有单例类只能自己创建自己唯一的实例, 必须给所有其他对象提供这一实例; !!!使用syn…

送书送书送书,(包邮)送,仅此而已!

亲爱的各位fans们&#xff0c;大家好&#xff0c;这次小编联合5个公众号朋友一起送书&#xff0c;一共送出 6本&#xff0c;当然包邮&#xff01;感谢本次活动的赞助商北京大学出版社。6个公众号联合送书&#xff0c;一共6本书&#xff0c;每个公众号送1本(包邮)。 6个公众号同…

Java多线程_1_Java内存模型_内存模型的3大特性

Java内存模型: 内存分布情况及其关系: 主内存:Java内存模型规定所有的变量都保存在主内存中 工作内存:每个线程都有自己的工作内存,保存了该线程使用到的变量的主内存副本拷贝 主内存与工作内存的关系: 线程对变量的所有操作都必须在自己的工作内存中进行,不能直接读写主内存…

requests 获取div_爬虫系列第五篇 使用requests与BeautifulSoup爬取豆瓣图书Top250

上一篇我们学习了BeautifulSoup的基本用法&#xff0c;本节我们使用它来爬取豆瓣图书Top250。一、网页分析我们爬取的网页的url是https://book.douban.com/top250?icnindex-book250-all。首页如图与豆瓣电影Top250差不多&#xff0c;将页面拉到最底部&#xff0c;可以看到分页…

python--socket套接字/TCP

socket套接字/TCP 一 客户端/服务器架构 C/S架构&#xff0c;包括 硬件C/S架构&#xff08;打印机&#xff09;软件C/S 架构&#xff08;web服务&#xff09;C/S架构的软件&#xff08;软件属于应用层&#xff09;是基于网络进行通信的Server端要&#xff1a; 1.力求一直提供服…

uniapp封装网络请求_八张图带你走进“通过一个完美请求封装一个网络模块”

本文提供视频课程讲解&#xff0c;需要的小伙伴可以点赞私信‘’网络模块‘’前往领取学习大纲1、网络模块在应用中的地位1.1当今占统治地位的网络组件OKHttp OkHttp 是一个相对成熟的解决方案&#xff0c;据说 Android4.4 的源码中可以看到 HttpURLConnection 已经替换成 OkHt…

luogu P1046 陶陶摘苹果

二次联通门 : luoguP1046 /*这个题好难.....由苹果树可知这应该是个树结构的题所以很自然的想到了用树链剖分来搞一下连边 最后查询以1为根节点的子树的权值和...从前闲的没事写着玩... */ #include <cstdio> #define Max 3300void read (int &now) {now 0;char wor…

毕业的这0111年

1.2004年&#xff0c;不知道大家对这个时间有没有感觉&#xff0c;那几年我正在读高中&#xff0c;韩寒的《三重门》席卷校园&#xff0c;同样还有郭敬明的《夏至未至》。那时候的我&#xff0c;还挣扎在温饱阶段&#xff0c;我每天吃饭的时候都想上食堂的三楼吃风味&#xff0…

Struts2_4_ActionMap与ValueStack详解_Struct2的EL及常用标签_防止表单数据重复提交

接着Struts2_3_day的讲 注:使用Struts2的< s:debug>< /s:debug>就可获取数据储存的分布图 StrutsPrepareAndExecuteFilter都会创建一个ActionContext和ValueStack对象, 所以Struts2的数据存储分为两类: ActionMap(contextMap)以及ValueStack; ActionMap中都是以m…

python快递代取系统_代取快递的变现方式,校园跑腿的经营范围有多大?

原标题&#xff1a;代取快递的变现方式&#xff0c;校园跑腿的经营范围有多大&#xff1f;进入大学&#xff0c;随着越来越多的学生加入到网购的行列。快递在学校也是堆积成山&#xff0c;高校校园快递市场也日渐红火。但往往带来的也有更多的麻烦&#xff0c;学生取快递时间变…