在批量添加数据时候采用List或者Map或者Set
Set<Student> students = new Set<Student>();
// 客户端验证:js;服务器端验证:必须有
1对动作类中的所有方法进行验证
a.动作类需要继承ActionSupport方法,覆盖public void validate();
b.方法内部编写你的验证规则,出现错误信息时候想addFieldError中添加错误信息
动作类就是模型:
public void validate(){
if(birthday==null||"".equals(birthday))
addFieldError("birthday","请输入xxx");
}
动作类和模型分开:
public void validate(){
if("".equals(student.getUsername().trim()))
// 空格不认为是空的
addFieldError("student.birthday","请输入xxx");
}
// 只针对某一方法进行验证
public String regist(){
System.out.println("abc");
return SUCCESS;
}
public void validateRegist(){
if("".equals(student.getUsername().trim()))
// 空格不认为是空的
addFieldError("student.birthday","请输入xxx");
}
2.声明是验证:
将验证信息写到配置文件中,用的比较多
错误视图和消息验证同编程是验证一直
a.针对动作类中的所有动作方法进行验证
动作类就是模型
在动作类所在的包中:建立一个 动作类名-validation.xml配置文件
<validators>
<!--field指定要验证的字段名,name为字段的名字-->
<field name="birthday">
<!--指定验证规则:type是验证规则,required是验证输入内容不能为null-->
<field-validate type="required">
<!--不符合错误规则的消息提示-->
<message>请输入---</message>
</field-validate>
</field>
</validators>
b.针对动作类中指定的动作方法进行验证
在动作类所在的包中建立:动作类名-动作名-validation.xml配置文件
验证功能是validation拦截器控制的,错误回显是通过workflow控制的
<![CDATA[]]>标记所包含的内容将会被表示为纯文本,比如<![CDATA[<]]>表示为“<”
在xml有些字符是不能被直接传入的,需要进行转译操作,比如“<”,“>”,“&”,直接传入xml会被报错
要转译成“<;”,“&bt;”,“&;”实体,这样才能被保存到xml文档中。
那么,在程序读取的时候,解析器会自动转换为“<”,“>”,“&”,比如:
<age> 20 < 30</age>
要写成:<age> 20 < 30</age>
注意:
1)注意序列字符之间不能有空格;
2)必须以“;”结束;
3)单独以“&”开始不被认为是转译
4)区分大小写
在xml需要进行转译的有:
(1)& &
(2)< <
(3)> >
(4)" "
(5)' '
所以:使用<![CDATA[]]>用来包含不被xml解析器解析的内容
注意:
(1) 此部分不能再包含”]]>”;
(2) 不允许嵌套使用;
(3)”]]>”这部分不能包含空格或者换行。
最后,说说<![CDATA[]]>和xml转移字符的关系,它们两个看起来是不是感觉功能重复了?
是的,它们的功能就是一样的,只是应用场景和需求有些不同:
(1)<![CDATA[]]>不能适用所有情况,转义字符可以;
(2) 对于短字符串<![CDATA[]]>写起来啰嗦,对于长字符串转义字符写起来可读性差;
(3) <![CDATA[]]>表示xml解析器忽略解析,所以更快。
在声明是验证的配置文件中:动作类名-动作名-validation.xml
<validators>
<field name="username">
<field-validate type="regex">
<param name="regexExpression"><![CDATA[[A-Za-z]{3,8}]]></param>
<message>输入的username必须为3-8字符</message>
</field-validate>
</field>
<field name="password">
<field-validate type="requiredstring">
<message>请输入密码</message>
</field-validate>
<field-validate type="regex">
<param name="regexExpression"><![CDATA[/d{3,8}]]></param>
<message>你的密码必须有3-8位数字组成</message>
</field-validate>
</field>
<!--必须选择性别-->
<field name="sex">
<field-validate type="required">
<message>请选择性别</message>
</field-validate>
</field>
<field name="email">
<field-validate type="email">
<message>请输入正确的邮箱</message>
<field-validate>
</field>
<field name="grade">
<field-validate type="int">
<param name="min">15</param>
<param name="max">100</param>
<message>成绩必须在${min}~${max}之间</message>
</field-validate>
</field>
</validators>
struts2的拦截器
modelDriven:模型驱动
ServletConfig:获取servletAPI
staticParams:静态参数注入
params:动态参数注入
validate:输入验证,声明之验证
自定义拦截器:和过滤器的功能差不多,充当保安的作用
a.编写一个类,继承 AbstractInterceptor抽象类
public class Demo1Interceptor extends AbstractInterceptor{
// 返回值:最终结果的返回值,就是一个逻辑结果视图,对应struts.xml的result
public String intercept(Invocation invocation){
System.out.println("拦截器前");
String rtValue = invocation.invoke();// 方向
System.out.println("拦截器后");
return rtValues;
}
}
b.配置拦截器
<package name="p1" extend="struts-default" namespace="/user">
<interceptors>
<!--定义拦截器-->
<interceptor name="Demo1Interceptor" class="com.deng.Demo1Interceptor/>
<interceptors>
</package>
c.使用拦截器
<action name="Demo1Action" class="com.deng.Demo1Action">
<interceptor-ref name="Demo1Interceptor"></interceptor>
<result>/1.jsp</result>
</action>
==编写一个执行动作方法前验证用户是否登录的拦截器
a.编写一个类,对所有动作都执行拦截的拦截器
public class LoginCheckInterceptor extends AbstractInterceptor{
public String intercept(ActionInvocation invocation) throws Exception{
// 通过session检测你有没有登录
Session session = ServletActionContext.getRequest().getSession();
Object obj = session.getAttribute("user");
// 没有登录,转向登录界面
if(obj==null){
return "login";
}
// 登录,方形
String rtValue = invocation.invoke();
return rtValue;
}
}
// 上传文件的要求
1.form的method方法必须是post类型
2.表单提交的enctype类型必须为multipart/form-data
3.input type="file" 输入与类型为file
文件上传
public class Demo1Action extends ActionSupport{
private String name;
private File photo;// 必须是File类型,名字对应表单的输入与
private String photoFileName;// 上传文件的文件名,必须为XXXFileName
private String photoContentType; // 上传文件的文件类型,必须为XXXContentType
public String uploadFile(){}
}
上传失败时候,会提示错误
<s:actionerror/>
修改上传文件的总大小,在struts.xml
<constant name="struts.multipart.maxSize" value="10247847"></constant>
限制上传的文件类型
allowedTypesSet:允许上传的MIME类型,多个用逗号隔开
allowedExtendsSet:允许上传的扩展名,多个用逗号隔开
<action name="uploadFile" class="com.deng.Action" method="uploadFile">
<interceptor-ref name="defaultStack"
<param name="fileUpload.allowedExtends">.jpg,.gif,.png</param>
</interceptor>
</cation>
文件下载:结果类型的使用
名字为stream的结果类型处理文件下载
1.动作类遵守一定的规范
public class DownloadAction extends ActionSupport{
private InputStream inputStream;// 建立一个输入流
private String fileName;
public String downloadFile(){
// 找到该文件的真实地址
String realPath = ServletActionContext.getServletContext().getRealPath("/pic.jpg");
// 获取文件名
fileName = URLEncoding(FilenameUtils.getName(realPath),"UTF-8");
// 放到输入流中
inputStream = new FileInputStream(inputStream);
return SUCCESS;
}
}
2.编写struts.xml中的配置
<action name="fileDownload" class="com.deng.fileDownload" method="downloadFile">
<result type="stream">
// 指定动作类中的输入流的属性名
<param name="inputName>inputStream</param>
// 通知浏览器以下载的方式打开,获取文件名的方式是OGNL表达式,调用动作类中的getFileName方法
<param name="contentDisposition">attachment;filename=${fileName}</param>
// MIME类型
<param name="contentType">application/octet-stream</param>
</result>
</action>
OGNL表达式:在struts2中使用OGNL表达式,必须要要放到struts2的标签中
功能:
1.支持对象方法调用,如XXX.doSomething();
<s:property>相当于JSTL的<c:out>
调用任意对象的任意方法
<s:property value="abc".length()/>
2.类静态方法调用
3.访问OGNL上下文和ActionContext
4.操作集合对象
context上下文
context map包括:
key value
application ServletContext中的所有属性attributes
session HttpSession中的所有属性attributes
value stack 它是一个list
action 动作类
request ServletRequest中的所有属性
parameters Map map = request.getParameterMap()
attr 从四大与范围中搜索,${}
动作类的生命周期:每次访问都会重新创建动作类的实力,还会创建ActionContext,valueStack实力,一直保存在你的线程中
ContextMap和root站组成了值站
ServletContext sc = ServletActionContext.getServletContext();
ActionContext ac = ActionContext.getContext();
1.ActionContext API:操作contextMap中的数据
// ActionContext中常用的API
public class Demo1 extends ActionSupport{
public String m1(){
// 如何得到ActionContext的实力:绑定到当前线程中
//
ActionContext ac = ActionContext.getContext();
// 想ActionContext存放数据
ac.put("p1","p1value");
// 取出数据
String s = (String)ac.get("p1");
// 获取valueStack的值
ValueStack vs = ac.getValueStack();
// 想HttpSession中存放属性
HttpSession session = ActionContext.getRequest().getSession();
session.setAttribute("a1","a1value");
// 从HttpSession中获取相关的属性
String s = (String)ac.getSession().get("a1");
// 获取请求参数
String[] a = (String [])ac.getParameters().get("name");
// 获取当前动作的名称
ac.getName()
// 获取应用范围的数据
ServletContext sc = ServletActionContext.getContext();
sc.setAttribute("a2","a2value");
String s = (String)ac.getApplication().get("a2");
}
}
OGNL其他用法
1.获取jsp页面中的contextMap/root的值.获取contextMap中的数据,OGNL要用#开头
ActionContext.getContext().put("a1","a1value");
获取contextMap中的数据
<s:property value="#a1">
2.获取根(list)中对象的属性,直接写属性的名称,会从栈顶直接往下找
public String m1(){
ValueStack vs = ActionContext.getContext().getValueStack();
Calender c1 = Calender.getInstance();
c1.set(2000,01,22);
vs.push(c1):// 入栈
}
获取数据
<s:property value="month">
OGNL其他用法
1.在jsp中可以使用OGNL取得数据,也可使用el表达式获取数据
2.在jsp页面中构造list和map对象
构造list对象
<s:property value="{a,b,c}"/>
构造map对象
<s:property value="#{'a':'1','b':'2','c':'3'}"
<s:radio name="gender" list="#{'1':'男','0':'女'}"></s:radio>
<s:checkboxlist name="hobby" list="{eat,sleep,play}"></s:checkboxlist>
3.OGNL和字符串之间的转换
4.在配置文件xml或者properties 文件中也可以使用OGNL,形式是:${ognl}
<param name="Disposition">attachment;filename="${fileName}"</param>
struts2的常用标签
// property 输出数据到页面
<s:property value="name"/>
// value 不写,输出栈顶对象
<s:property/>
// set 放数据,默认放到action
scope:application|session|request|action|page
<s:set value="value1" var="v1" scope="session"></s:set>
<s:set value="value2" var="v2"></s:set>
contextMap:<s:property value="#v2"/>
requestScope:<s:property value="#request.v2"/>
// bean:给一个类起一个名字,将该对象放入contextMap中
<s:bean name="java.util.Date" var="now"></s:bean>
<s:property value="#now.time"/>
// action:指向一个动作
<s:action name="Demo2" executeResult="true"></s:action>
// iterator:forEach迭代
<table border="1">
<tr>
<th>key</tr>
<th>value</th>
</tr>
<!--指定了var:把当前遍历的元素放入到contextMap中,me是每一个变量实体-->
<s:iterator value="#request" var="me">
<tr>
<th><s;property value="#me.key"/></tr>
<th><s:property value="#me.value"/></th>
</tr>
</s:iterator>
</table>
<table border="1">
<tr>
<th>key</tr>
<th>value</th>
<th>序号</th>
</tr>
<!--指定var:把当前遍历的元素存放到根站的栈顶,Map.entry
status属性:指向一个对象,包含遍历元素的信息,放到contextMap中,该对象具有以下方法
isOdd
isEven
isLast
isFirst
getIndex
getCount
-->
<s:iterator value="#request" status="s">
<tr class="${s.odd?'odd':'even'}">// <tr class="<s:property value='#s.odd?"odd":"even"'/>"
<th><s;property value="key"/></tr>
<th><s:property value="value"/></th>
<th><s:property value="#s.count"/></th>
</tr>
</s:iterator>
</table>
<!--url结合参数使用-->
<s:url action="act1" url="u1">
<s:param name="username" value="hello"><s:param>e
</s:url>
<a href="${u1}">click</a> // 相当于调用getU1方法
// 同时采用用你的的方
<s:u action="ac2">click too <s:param name="username" value="hello too"></s:param></s:u>
<s:textfield name="username" label="用户名" requiredLabel="true"></s:textfield>
<s:radio name="sex" list="{'nan','nv'}" value="nan" label="性别"></s:radio>
<%
List<Customer> customers = new ArrayList<Customer>();
customers.add(1,"张三");
customers.add(2,"李四");
customers.add(3,"王五");
request.setAttribute("customers",customers);
%>
<s:checkboxlist name="ns" list="#required.customers" listKey="id" listValue="name" label="你的顾客"></s:checkboxlist>
<s:select name="ciyt" list="#{'bj':'北京','sh':'上海'}" label="城市"></s:select>
<s:select name="ylike" list="#request.customers" listKey="id" listValue="name" headerKey="" headerValue="--请选择--"></s:select>
防止表单重复提交
<action name="DemoAction" class="com.deng.DemoAction" method="m1">
<interceptor-ref>defaultStack</interceptor-ref>
<interceptor-ref>token</interceptor-ref>
<!--结果视图是token拦截器转向的-->
<result name="invalid.token">/msg.jsp</result>
</action>