Struts2的标签与OGNL的配合使用、struts2的其它标签、防止表单重复提交-day06完结待续

第一节 Struts标签与OGNL的配合使用

1.1 iterator遍历标签

  1. 先提供一个Student.java,name、age、city的getters、setters、有参构造。
  2. Demo03Action.java
package com.it.web.action;

import java.util.ArrayList;
import java.util.List;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;

import com.it.model.Student;
import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Demo03Action extends ActionSupport{

	//action里提供属性,并提供get方法,这个属性数据才会被存储在值栈
	private List<Student> stuList;
	
	public List<Student> getStuList() {
		return stuList;
	}

	public String list(){
		
		//jsp一般从值栈取数据
		//暂不使用数据库
		stuList = new ArrayList<Student>();
		stuList.add(new Student("shu", 18, "广州"));
		stuList.add(new Student("zhangsan", 28, "汕头"));
		stuList.add(new Student("lisi", 38, "汕尾"));
		stuList.add(new Student("wangwu", 48, "潮州"));
		stuList.add(new Student("zhaoliu", 58, "合肥"));
		stuList.add(new Student("wanger", 8, "南京"));
		
		return SUCCESS;
	}
}

  1. struts.xml
	<!-- 开发模式 -->
	<constant name="struts.devMode" value="true"></constant>
	
	<package name="p1" extends="struts-default">
		<action name="list" class="com.it.web.action.Demo03Action" method="list">
			<result>/demo03.jsp</result>
		</action>
	</package>
  1. demo03.jap
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo03.jsp</title>
</head>
<body>
<table border="1">
	<tr>
		<td>状态</td>
		<td>名字</td>
		<td>年龄</td>
		<td>城市</td>
	</tr>
	<!-- 使用struts的遍历标签:s:iterator遍历,如同for循环
		 1.value:写值栈变量名,是OGNL表达式,先从值栈中找
		 2.var:如果指定var属性,会把当前遍历的变量名,也就是var的值,存到contextMap
		 3.status:用于记录一些遍历时的状态或属性
		 	a.boolean isOdd(); 		奇数
		 	b.boolean isEven(); 	偶数
		 	c.boolean isFirst();	第一个
		 	d.boolean isLast();	    最后一个
		 	e.int getIndex();		索引,从0开始
		 	f.int getCount();		遍历的个数,从1开始
	-->
	<s:iterator value="stuList" var="stu" status="st">
		<tr>
			<!--遍历取值的方式:
				左边:第一种OGNL表达式:<s:property value="#stu.name"/>表示从contextMap取值
				右边:第二种EL表达式:${stu.name},也会从contextMap取值,更为简便,推荐使用
			-->
			<td>${st.count}</td><!-- EL取遍历的个数、取name、age、city-->
			<td><s:property value="#stu.name"/>=${stu.name}</td>
			<td><s:property value="#stu.age"/>=${stu.age}</td>
			<td><s:property value="#stu.city"/>=${stu.city}</td>
		</tr>
	</s:iterator>
</table>

<s:debug></s:debug>
</body>
</html>
  1. 效果
    在这里插入图片描述

  2. 一些解释
    在这里插入图片描述

在这里插入图片描述

每遍历一次从值栈中取stuList(所以上面代码中不用加#号)放入contextMap,再从contextMap中取stu(要加#号)点上要的值(key),一直遍历到最后一个,也就是上图

1.2 OGNL投影(了解)

使用过滤条件投影

  1. 修改demo03.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo03.jsp</title>
</head>
<body>
<table border="1">
	<tr>
		<td>状态</td>
		<td>名字</td>
		<td>年龄</td>
		<td>城市</td>
	</tr>
	<!--
	一、OGNL投影∶是指可以给定过滤条件
		OGNL表达试加条件
		.?#过滤所有符合条件的集合如: stuList.{?#this.age>30}
		.^#过滤第一个符合条件的集合如: stuList.{#this.age>303
		.$#过滤最后一个符合余的集合如: stuList.{$#this.age>30}
	-->

	<!--所以年龄大于等于18的-->
	<s:iterator value="stuList.{?#this.age>=18}" var="stu" status="st">
		<tr>
			<td>${st.count}</td>
			<td><s:property value="#stu.name"/> </td>
			<td><s:property value="#stu.age"/></td>
			<td><s:property value="#stu.city"/></td>
		</tr>
	</s:iterator> 
</table>

<s:debug></s:debug>
</body>
</html>
  1. 效果
    在这里插入图片描述

使用指定属性投影

  1. 修改demo03.jsp
<table border="1">
	<tr><td>姓名</td></tr>
	<!-- 
		1、只需要name属性
		2、遍历时,不用指定var属性,默认的值就放在值栈的栈顶
		3、由于.{name}把student的name属性直接放在值栈的栈顶
		4、所以遍历取值时,不指定value,默认从值栈的栈顶取值 
	-->
	<s:iterator value="stuList.{name}">
		<tr>
			<td><s:property/></td>
		</tr>
	</s:iterator>
</table>

<s:debug></s:debug>
  1. 效果
    在这里插入图片描述
  2. 一些解释
<!--此时这样写会报错-->
<s:property value="#stu.name"/>

这时变量stu的类型为字符串,不是一个对象,再点name是取不到的
在这里插入图片描述
这样写就能取到,也可以像上面一样不写value

<table border="1">
	<tr><td>姓名</td></tr>
	<s:iterator value="stuList.{name}" var="stu">
		<tr>
			<td><s:property value="#stu"/></td>
		</tr>
	</s:iterator>
</table>

1.3 回顾Struts2中#,$,%符号的使用(重要)

1. #号

  1. 取contextMap中key时使用,例如:<s:property value="#name" />
  2. OGNL中创建Map对象时使用,例如:<s:radio list="#{‘male’:‘男’,‘female’:‘女’}" />

2. $

  1. 在JSP中使用EL表达式时使用,例如:${name}
  2. 在xml配置文件中,编写OGNL表达式时使用,例如文件下载时,文件名编码。
    struts.xml——>${@java.net.URLEncoder.encode(filename)}

3. %

  1. 在struts2中,有些标签的value属性取值就是一个OGNL表达式,例如:<s:property value=“OGNL Expression” />
  2. 还有一部分标签,value属性的取值就是普通字符串,例如:<s:textfield value=“username”/>,如果想把一个普通的字符串强制看成时OGNL,就需要使用%{}把字符串套起来。
  • 例如<s:textfield value="%{username}"/>。
  • <s:property value="%{OGNL Expression}" />也可以使用,但不会这么用。
  • %起的是一个强调的作用
    在这里插入图片描述

1.4 Struts的其它标签

s:set标签

  • 作用:存储变量
    • value:存入map中的值,是一个OGNL表达式
    • var:存在map中的key
    • scope:存储的范围application session request page和action
    • 不写scope,默认是action,它是在contextMap中和request中各存一份
  1. Demo04Action.java
package com.it.web.action;

import java.util.ArrayList;
import java.util.List;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;

import com.it.model.Student;
import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Demo04Action extends ActionSupport{
	
	private String username;
	private String password;
	public void setUsername(String username) {
		this.username = username;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		System.out.println(username);
		System.out.println(password);
		return super.execute();
	}
}

  1. struts.xml添加
	<action name="demo04" class="com.it.web.action.Demo04Action">
		<result>/demo04.jsp</result>
	</action>
  1. demo04.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo04.jsp</title>
</head>
<body>

<!-- s:set标签
	往作用域存数据
	value:值
	var:变量名
	scope:作用域,范围,可写类型:application,session,request,page和action
	      不写,就是action
 -->
 <s:set value="'shu01'" var="username1" scope="application"></s:set>
 <s:set value="'shu02'" var="username2" scope="session"></s:set>
 <s:set value="'shu03'" var="username3" scope="request"></s:set>
 <s:set value="'shu04'" var="username4" scope="action"></s:set>
 
 <!-- 取值 -->
 <s:property value="#application.username1"/>
 <s:property value="#session.username2"/>
 <s:property value="#request.username3"/>
<s:debug></s:debug>
</body>
</html>
  1. 效果
    在这里插入图片描述

注意:value="‘shu01’",如果不加单引号则取不到,双引号是当成OGNL表达式来使用去取值,但这里是要用它们本来的字符串,key的值来使用,所以要加单引号

s:action标签

  • 作用:在jsp里访问一个action
  1. demo05.jsp
<!--1. s:action标签
	在jsp里面访问一个action
	代表把list.action的数据显示在当前页面
 -->
 --------------------<br>
<s:action name="list" executeResult="true"></s:action> 

  1. 效果
    在这里插入图片描述

s:if标签、s:elseif标签、s:else标签

  • 相当于jstl中的c:choose、c:wher、c:otherwise
<!--存一个级别数据A︰高级讲师 B:中级讲师 C:初级讲师 D:工程师-->
<s:set value="'D'" var="level"/>

<s:if test="#level == 'A'">高级讲师</s:if>
<s:elseif test="#level == 'B'">中级讲师</s:elseif>
<s:elseif test="#level == 'C'">初级讲师</s:elseif>
<s:else>工程师</s:else>

在这里插入图片描述

s:url和s:a标签

	<!--
		2.s:url 【不好用】
 		声明一个url路径,存在contextMap
 		url里面只能传一个参数给后台,第一个参数
    -->
 <s:url action="demo04" var="myurl">
	<s:param name="password">123</s:param>
	<s:param name="username">shu</s:param>
 </s:url>
 <a href="<s:property value='#myurl'/>">点击</a> 
 

在这里插入图片描述
只获得了第一个参数
在这里插入图片描述

  • s:a标签
<!-- 3:s:a a标签-->
<s:a action="demo04">
	<s:param name="password">123</s:param>
	<s:param name="username">shu</s:param>
	点击
</s:a>
<s:debug></s:debug>

在这里插入图片描述
参数都能获取到,写法也简便
在这里插入图片描述

第二节 表单案例

表单补充:

<s:form>
	<!-- 两种写法 -->
	<s:select list="#{'gz':'广州','sz':'深圳'}" label="城市" name="city" headerKey="" headerValue="-请选择城市-"></s:select>
	<s:select list="#{'':'-请选择城市-','gz':'广州','sz':'深圳'}" label="城市" name="city"></s:select><br>
	<s:textarea label="备注" rows="10" cols="50" name="remark"></s:textarea><br>
	<s:submit value="提交"></s:submit>
	<s:reset value="重置"></s:reset>
</s:form>
<s:debug></s:debug>

在这里插入图片描述

模型驱动原理补充:

  • struts的ModelDrivenInterceptor 这个拦截器来完成的了模型驱动的工作
  • 这个可以从struts-defualt.xml找到这个拦截器
  • ModelDrivenInterceptor.java源码
    在这里插入图片描述

struts主题的设置

  • 默认主题的名称是XHTML,都是在struts的默认属性文件default.properties中定义着
    在这里插入图片描述

更改默认的主题方式

  • 第一步:在标签内加theme属性
    在这里插入图片描述
  • 第二步:配置全局的theme
    在这里插入图片描述
  • 效果:
    在这里插入图片描述

第三节 防止表单重复提交(拦截器)

防止表单重复提交原理图

在这里插入图片描述

Servelt的表单重复提交的一种解决方案

  • 在登录时,可以通过验证码存在session中来解决
  1. 登录页面login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/loginServlet" method="post">
	用户名:<input type="text" name="username"><br>&nbsp;码:<input type="password" name="password"><br>
	验证码:<input type="text" name="code">
	<img alt="" src="${pageContext.request.contextPath}/validateCodeServlet"><br>
	<input type="submit" value="提交">
</form>
</body>
</html>
  1. 创建验证码ValidateCodeServlet.java,导入ValidateCode.jar
package com.it.web.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.dsna.util.images.ValidateCode;

@WebServlet("/validateCodeServlet")
public class ValidateCodeServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//创建验证码对象
		ValidateCode code = new ValidateCode(100,30,4,4);//(宽,高,验证码个数,干扰线条数)
		
		//存储服务端验证码
		request.getSession().setAttribute("serverCode", code.getCode());
		System.out.println("ServerCode:" + code.getCode());
		
		//响应客户端
		code.write(response.getOutputStream());
	}
}
  1. 登录LoginServlet.java
package com.it.web.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet{

	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		//有中文,设置内容编码,防中文乱码
		response.setHeader("content-type", "text/html;charset=utf-8");
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		//获取客户端的验证码
		String clientCode = request.getParameter("code");
		String serverCode = (String) request.getSession().getAttribute("serverCode");
		//equalsIgnoreCase忽略大小写比较
		if(clientCode.equalsIgnoreCase(serverCode)){
			//验证码一致,表单提交,这里就只是简单的在页面写一句话
			response.getWriter().write("第一次表单提交");
			//删除session中存储的验证码,防止表单刷新重复提交和恶意提交
			request.getSession().removeAttribute("serverCode");
		}else{
			//不一致,可以给出提示或直接重定向
			response.getWriter().write("表单重复提交");
		}
		
		System.out.println(username);
		System.out.println(password);
	}
}

  1. 效果
    在这里插入图片描述
    输入正确的验证码提交后(用户名密码随便填):
    在这里插入图片描述
    刷新一下页面:
    在这里插入图片描述
    在这里插入图片描述

Struts的表单重复提交解决方案

1. 使用重定向

  1. login.jsp
<form action="${pageContext.request.contextPath}/login.action" method="post">
	用户名:<input type="text" name="username"><br>&nbsp;码:<input type="password" name="password"><br>
	<input type="submit" value="提交">
</form>
  1. struts.xml
	<action name="login" class="com.it.web.action.UserAction" method="login">	
			<result type="redirect">/success.jsp</result>
	</action>
  1. UserAction.java
package com.it.web.action;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;

import com.it.model.Student;
import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class UserAction extends ActionSupport{
	
	private String username;
	private String password;
	
	public void setUsername(String username) {
		this.username = username;
	}

	public String getUsername() {
		return username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	public String login(){
		System.out.println(username);
		System.out.println(password);
		return SUCCESS;
	}
}

  1. 效果
    在这里插入图片描述
    点击提交后:
    在这里插入图片描述
    无论刷新多少次:控制台都只打印登录成功那一次的username和password
    在这里插入图片描述

2. 使用<s:token/>生成令牌配合token拦截器

此种解决方式不太符合逻辑,它是产生了错误之后再告知用户,应该一直停留在当前界面

  1. login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login.action" method="post">
	用户名:<input type="text" name="username"><br>&nbsp;码:<input type="password" name="password"><br>
	<!-- 添加一个token令牌 -->
	<s:token></s:token>
	<input type="submit" value="提交">
</form>
</body>
</html>

查看源码:struts生成了一个隐藏的token令牌
在这里插入图片描述

  1. UserAction.java
package com.it.web.action;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;

import com.it.model.Student;
import com.it.model.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class UserAction extends ActionSupport{
	
	private String username;
	private String password;
	private String token;
	public void setUsername(String username) {
		this.username = username;
	}

	public String getToken() {
		return token;
	}

	public String getUsername() {
		return username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	

	public void setToken(String token) {
		this.token = token;
	}

	@Override
	public String execute() throws Exception {
		// TODO Auto-generated method stub
		System.out.println(username);
		System.out.println(password);
		return SUCCESS;
	}
}

  1. struts.xml
	<action name="login" class="com.it.web.action.UserAction">
			<!-- 进行token拦截 -->
			<!-- token不属于defaultStack,所以要添加defaulStack默认拦截器,不然struts默认的拦截功能失效 -->
			<interceptor-ref name="defaultStack"/>
			<interceptor-ref name="token"/> 
			<result >/success.jsp</result>
			<!-- 配置表单重复提交时的结果页面 -->
			<result name="invalid.token">/invalidtoken.jsp</result>
	</action>
  1. 效果
    在这里插入图片描述
    点击提交后:登录成功
    在这里插入图片描述
    但是,再次刷新会出现表单重复提交
    在这里插入图片描述
    在这里插入图片描述
    我们想要的是它停留在登录成功的页面,所有要使用tokensession拦截器

使用<s:token/>生成令牌配合tokensession拦截器

  1. 修改struts.xml
	<action name="login" class="com.it.web.action.UserAction">
			<!-- 进行token拦截 -->
			<!-- token不属于defaultStack,所以要添加defaulStack默认拦截器,不然struts默认的拦截功能失效 -->
			<interceptor-ref name="defaultStack"/>
			<!-- tokenSession只会处理第一次,当重复提交时不会处理 -->
			<interceptor-ref name="tokenSession"/> 
			<result >/success.jsp</result>
			<!-- 配置表单重复提交时的结果页面 -->
			<result name="invalid.token">/invalidtoken.jsp</result>
	</action>
  1. 效果
    在这里插入图片描述
    再刷新,也不会到表单重复提交页面,tokenSession只处理一次。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值