1. 程式人生 > >框架 day29 Struts2-上傳,下載,OGNL與Struts2的結合(值棧與ActionContext),Struts2標籤,Token機制

框架 day29 Struts2-上傳,下載,OGNL與Struts2的結合(值棧與ActionContext),Struts2標籤,Token機制

檔案上傳

1.1回顧

*瀏覽器端:

       <form    method="post"     enctyp="multipart/form-data">

              <input   type="file" name="image">

              <input   type="submit">

*伺服器端:

       手動方式

       第三方:commons-fileupload  核心類ServletFileUpload

1.2介紹

*struts通過攔截器進行檔案上傳,攔截器名稱:fileUpload

*預設棧中引用fileUpload攔截器,及預設情況struts支援檔案上傳。

1.3分析

 

關聯值棧解析圖


Struts2預設使用的是commons-fileUpload元件完成上傳的,使用Struts2會大量簡化上傳檔案的開發。

這一工作由fileUpload攔截器來完成。它會檢視當前請求的enctype是否為multipart/form-data,

如果不是就會直接“放行”;如果是,那麼它會去解析表單,然後把解析的結果傳遞給Action的屬性!

fileUpload攔截器對會對Action提供很大的“幫助”,同時它也會對Action提出一些“小小的要求”。Action需要提供3個屬性:

*        File fieldName

*        String fileContentType

*        String fileFileName;

三個屬性的字首都(fieldName)必須與檔案表單項名稱一致,

例如有檔案表單項內容為:<input type=”file” name=”myUpload”/>,其中表單項名稱為:myUpload,

那麼Action就必須要有如下3個屬性:

*       private File myUpload

*       private String myUploadContentType

*       private String myUploadFileName

上傳配置

可以通過Struts2的常量來完成對上傳的配置,下面是與上傳相關的常量:

*struts.multipart.parser:指定使用的上傳元件,預設值為jakarta,表示使用commons-fileupload元件,Struts2還支援cos和pell;

*struts.multipart.saveDir:臨時目錄,如果沒有指定臨時目錄,那麼臨時檔案會在Tomcat的work目錄中;

*struts.multipart.maxSize:整個大小限制,預設值為2097152,即2M。注意,這個限制是整個請求的大小,而不是單一檔案的大小。

當上傳的表單超出了限制時,攔截器會向fieldError中新增錯誤資訊!當執行wokflow攔截器時,會發現fieldError中存在錯誤,這時就會跳轉到input檢視,所以我們需要為Action指定input檢視。

fileUpload攔截器也有3個引數,我們可以給fileUpload攔截器配置這3個引數:

*maximumSize:上傳的單個檔案的大小限制;

*allowedTypes:允許上傳檔案的型別,多個型別以逗號隔開;

*allowedExtensions:允許上傳檔案的副檔名,多個副檔名以逗號隔開;

<struts>
	<constant name="struts.devMode" value="true" />
	<constant name="struts.multipart.maxSize" value="1048576" />
	<package name="s8" namespace="/" extends="struts-default">
		<action name="UploadAction" class="cn.itcast.upload.action.UploadAction">
			<result name="input">/demo1/upload.jsp</result>
			<param name="savepath">/WEB-INF/uploads</param>
			<interceptor-ref name="defaultStack">
				<!-- 限制單個檔案大小上限為512K -->
				<param name="fileUpload.maximumSize">524288</param>
				<param name="fileUpload.allowedExtensions">jpg,png,bmp</param>
			</interceptor-ref>
		</action>
	</package>
</struts>


1.4單檔案實現

*編寫jsp提供表單

<body>
	<form action="${pageContext.request.contextPath}/uploadDemo01Action"
	method="post" enctype="multipart/form-data">
		選擇檔案:<input type="file" name="image" /> <br/>
		<input type="submit" value="單檔案上傳"/>
	</form>
</body>

*編寫action提供欄位及setter

public class UploadDemo01Action extends ActionSupport {	
	
	// #1 檔案內容,型別必須是 File
	private File image;
	// #2 檔名稱:固定字尾FileName
	private String imageFileName;
	// #3 檔案型別:固定字尾:ContentType
	private String imageContentType;
	
	// #4 提供setter方法
	public void setImage(File image) {
		this.image = image;
	}
	public void setImageFileName(String imageFileName) {
		this.imageFileName = imageFileName;
	}
	public void setImageContentType(String imageContentType) {
		this.imageContentType = imageContentType;
	}
		
	
	@Override
	public String execute() throws Exception {
		
		//父目錄
		String parentDir = ServletActionContext.getServletContext().getRealPath("/WEB-INF/upload/");
		File file = new File(parentDir,imageFileName);
		//儲存
		FileUtils.copyFile(image, file);
		
		return NONE;
	}

}


1.5多檔案實現

*編寫jsp提供表單

	<script type="text/javascript">
		function addFileUploadItem(){
			var itemTdObj = document.getElementById("itemTdId");
			//將原有的td中內容,新增一個字串,設定回td
			itemTdObj.innerHTML = itemTdObj.innerHTML + "<br/>
			<span>選擇檔案:<input type=\"file\" name=\"image\" /></span>";
		}
	</script>
</head>
<body>
	<!-- 使用JavaScript追加上傳選擇 -->
	<a href="javascript:void(0)" onclick="addFileUploadItem()">新增上傳選擇</a>
	
	
	<s:actionerror/>
	<form action="${pageContext.request.contextPath}/uploadDemo02Action"
	method="post" enctype="multipart/form-data">
		<table>
			<tr>
				<td id="itemTdId">
					<span>選擇檔案:<input type="file" name="image" /></span>
				</td>
			</tr>
			<tr>
				<td><input type="submit" value="多檔案上傳"/></td>
			</tr>
		</table>
		
	</form>
</body>

*編寫action提供欄位及setter

       接收多個檔案時,需要提供陣列獲得。

public class UploadDemo02Action extends ActionSupport {
	
	//提供接收的資料型別是陣列即可。
	//#1 檔案內容
	private File[] image;
	//#2檔名稱
	private String[] imageFileName;
	//#3檔案型別
	private String[] imageContentType;
	
	//#4 setter方法
	public void setImage(File[] image) {
		this.image = image;
	}
	public void setImageFileName(String[] imageFileName) {
		this.imageFileName = imageFileName;
	}
	public void setImageContentType(String[] imageContentType) {
		this.imageContentType = imageContentType;
	}
	
	
	public void add() throws Exception {
		
		//父目錄
		String parentDir = ServletActionContext.getServletContext().getRealPath("/WEB-INF/upload");
		
		//遍歷所有內容,並儲存
		for(int i = 0 ; i < image.length ; i ++){
			// 1 檔案內容
			File singleImage = image[i]; 
			// 2 檔名稱
			String singleFileName = imageFileName[i];
			
			// 3 儲存 
			File file = new File(parentDir , singleFileName);
			FileUtils.copyFile(singleImage, file);  // (流自動關閉,父目錄自動建立)
		}
		
	}

}


1.6國際化配置

1.6.1提供資原始檔

在上傳檔案時如果出現錯誤,那麼在input檢視顯示的錯誤資訊都是英文的。

如果想替換這些資訊,需要知道這些錯誤資訊的資源key,然後在我們自己的國際化資原始檔中指定這些key的新值即可。

與上傳相關的錯誤資訊都在org.apache.struts2包下的struts-message.properties檔案中。

struts.messages.error.uploading=Error uploading: {0}
struts.messages.error.file.too.large=The file is to large to be uploaded: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
struts.messages.upload.error.SizeLimitExceededException=Request exceeded allowed size limit! Max size allowed is: {0} but request was: {1}

內容:

struts.messages.upload.error.SizeLimitExceededException=\u6587\u4EF6\u4E0A\u4F20\u5927\u5C0F\u8D85\u8FC7\u9650\u5B9A,\u5B9E\u9645\u5927\u5C0F{1}\uFF0C\u9650\u5B9A\u7684\u6700\u5927\u503C{0}

1.6.2配置資原始檔

*需要覆蓋struts全域性配置資訊

*通過struts.xml 新增常量配置

可以在srcres.properties檔案,在這個檔案中物件以上資源key進行替換。

然後在struts.xml檔案中給出<constant name="struts.custom.i18n.resources" value="res/">即可


	<!-- 常量 -->
	<constant name="struts.devMode" value="true"></constant>
	<constant name="struts.custom.i18n.resources" value="cn/itcast/a_upload/UploadDemo02Action"></constant>

檔案下載

2.1回顧

*伺服器端傳送資料到瀏覽器,通知瀏覽器應該下載即可。

       設定頭:response.setHeader("content-disposition","attachment;filename=....");

       傳送資料流:response.getOutputSteam()

2.2struts分析


關聯值棧解析


2.3實現

2.3.1編寫action

public class DownloadDemo01Action extends ActionSupport {
	
	//確定下載資源流
	private InputStream target;
	public InputStream getTarget() {
		return target;
	}

	@Override
	public String execute() throws Exception {
		
		//確定下載資源, 必須保證資源流存在,如果返回null,將拋異常
		this.target = ServletActionContext.getServletContext().getResourceAsStream("/WEB-INF/download/105.jpg");
		return SUCCESS;
	}
}


2.3.2編寫配置檔案

*配置<result>     

		<!-- 2 下載 -->
		<!-- 2.1 簡單下載 -->
		<action name="downloadDemo01Action" class="cn.itcast.b_download.DownloadDemo01Action">
			<result name="success" type="stream">
				<!-- 2.1.1 確定action執行的方法名稱,對應屬性,及將執行getter -->
				<param name="inputName">target</param>
				<!-- 2.1.2 確定響應頭 -->
				<param name="contentDisposition">attachment;filename=mm.jpg</param>
			</result>
		</action>


2.4下載的中文亂碼

*action類

響應頭中只能使用拉丁(Iso-8859-1)碼錶

 
public class DownloadDemo02Action extends ActionSupport {
	
	//確定下載資源流
	private InputStream target;
	public InputStream getTarget() {
		return target;
	}
	
	//確定下載檔名稱
	private String imageFileName;
	public String getImageFileName() throws UnsupportedEncodingException {
		//解決中文亂碼
		return new String(imageFileName.getBytes(),"ISO-8859-1");
	}

	@Override
	public String execute() throws Exception {
		
		//確定下載資源, 必須保證資源流存在,如果返回null,將拋異常
		this.target = ServletActionContext.getServletContext().getResourceAsStream("/WEB-INF/download/105.jpg");
		this.imageFileName = "美女.jpg";
		
		return SUCCESS;
	}
}

*配置(配置檔案中檔名使用了OGNL)

		<!-- 2.2 下載檔名稱中文亂碼 -->
		<action name="downloadDemo02Action" class="cn.itcast.b_download.DownloadDemo02Action">
			<result name="success" type="stream">
				<!-- 2.1.1 確定action執行的方法名稱,對應屬性,及將執行getter -->
				<param name="inputName">target</param>
				<!-- 2.1.2 確定響應頭 -->
				<param name="contentDisposition">attachment;filename=${imageFileName}</param>
			</result>
		</action>


OGNL與Struts2的結合

OGNL表示式

3.1介紹

*OGNL是Object Graphic Navigation Language(物件圖導航語言)的縮寫,比EL更強大的表示式語言(開源專案)。

*Struts2框架使用OGNL作為預設的表示式語言。

OGNL的功能介紹:

*EL一樣的JavaBean導航;

*呼叫物件方法;

*呼叫類的靜態方法;

*索引陣列元素;

*操作集合;

資料存放位置

         root:一個物件

         context:一組物件,底層使用Map,每一個物件存在一個名稱。

開源框架:第三方,

3.2操作演示

1取值

*根是(javabean)取值

//1 演示ognl的基本使用
	@Test
	public  void  fun1() throws Exception{
		//引數1: 填寫ognl表示式
		//引數2: Map => context 上下文
		//引數3: javaBean / List / Map.....  Root 根
//--------------------------------------------------------		
		User u = new User();
		u.setName("tom");
		
		String name =  (String) Ognl.getValue("name", new HashMap(), u);
	
		System.out.println(name);
	}


*根是list([n]語法)

	@Test
	public  void  fun2() throws Exception{
		//引數1: 填寫ognl表示式
		//引數2: Map => context 上下文
		//引數3: javaBean / List / Map.....  Root 根
//--------------------------------------------------------		
		List<User> list = new ArrayList<User>();
		User u1 = new User();
		u1.setName("tom");
		list.add(u1);
		//---------
		User u2 = new User();
		u2.setName("jerry");
		list.add(u2);
		
		//ognl表示式 預設從根下取資料
		
		String name =  (String) Ognl.getValue("[0].name", new HashMap(), list);
	
		System.out.println(name);
	}

*Map(context)中取

	@Test
	public  void  fun3() throws Exception{
		Map< String, Object> context = new HashMap<String, Object>();
		
		context.put("name", "tom");
		
		//-------------------------------
		User u2 = new User();
		u2.setName("jerry");
		
		
		String name = (String) Ognl.getValue("name", context, u2);
		
		System.out.println(name);
	}

2賦值

*表示式賦值

	@Test
	public  void  fun5() throws Exception{
		//演示賦值1
		User u = new User();
		
		 Ognl.getValue("name='tom'", new HashMap(), u);
		 
		 System.out.println(u.getName());
	}

*SetValue方法賦值

	@Test
	public  void  fun6() throws Exception{
		//演示賦值2
		User u = new User();
		
		 Ognl.setValue("name", new HashMap(), u,"jerry");
		 
		 System.out.println(u.getName());
	}

3呼叫方法

	@Test
	public  void  fun7() throws Exception{
		//演示方法呼叫(方法需要存在於根物件中)
		User u = new User();
		
		Ognl.getValue("setName('jack')", new HashMap(), u);
		 
		
		System.out.println(u.getName());
		
	}

4呼叫靜態方法

public class DateUtils {
	
	public static double PI = 3.14159265357;
	
	
	public static String getTime(){
		return new SimpleDateFormat("yyyy/MM/dd").format(new Date());
	}
	
	public static String echo(String str){
		return str;
	}
}

 	@Test
	public  void  fun8() throws Exception{
		//演示靜態方法呼叫(不受方法必須在根中的限制)
		User u = new User();
		
		String time = (String) Ognl.getValue("@[email protected]()", new HashMap(), u);
		 
		String echo = (String) Ognl.getValue("@[email protected]('hiahia~~')", new HashMap(), u);
		
		System.out.println(time);
		
		System.out.println(echo);
		
	}


5訪問靜態變數

	@Test
	public  void  fun9() throws Exception{
		//演示靜態方法呼叫(不受方法必須在根中的限制)
		User u = new User();
		
		double Pi= (Double) Ognl.getValue("@[email protected]", new HashMap(), u);
		 
		
		System.out.println(Pi);
		
	}

6數學運算子

	@Test
	public  void  fun10() throws Exception{
		//演示數學運算子
		User u = new User();
		
		int result= (Integer) Ognl.getValue("1+1", new HashMap(), u);
		 
		
		System.out.println(result);
		
	}

7表示式串聯“,”號連線

	@Test
	public  void  fun11() throws Exception{
		//演示","連線符
		User u = new User();
		
		//結果會返回最後一組ognl表示式的值
		String name = (String) Ognl.getValue("name='tom',name='aa'", new HashMap(), u);
		 
		
		System.out.println(name);
		
	}

8建立list

	@Test
	public  void  fun12() throws Exception{
		//演示 建立物件 (list)
		User u = new User();
		
		
		List list = (List) Ognl.getValue("{'tom','jerry','jack','rose'}", new HashMap(), u);
		 
		
		System.out.println(list);
		
	}

9建立map

	@Test
	public  void  fun13() throws Exception{
		//演示 建立物件 (map)
		User u = new User();
		
		
	Map map =	(Map) Ognl.getValue("#{'name':'tom','age':'18'}", new HashMap(), u);
		 
		
		System.out.println(map);
		
	}

10 建立物件,格式: new 物件()

	@Test
	public  void  fun14() throws Exception{
		//演示 建立物件 (user)
		User u = new User();
		
		
	User u2 =  (User) Ognl.getValue("new cn.itheima.bean.User()", new HashMap(), u);
		 
		
		System.out.println(u2);
		
	}

11 "#this" 表示當前物件引用

	@Test
	public void demo10() throws OgnlException{
		// #10, "#this" 表示當前物件引用
		
		User user = new User("rose","999");
		
		Object obj = Ognl.getValue("#this", new HashMap(), user);
		System.out.println(obj);
	}

12In與not in運算子

	@Test
	public  void  fun15() throws Exception{
		//演示 in
		User u = new User();
		
		
	boolean b = (Boolean) Ognl.getValue("'tom' in {'tom','jerry','jack','rose'}", new HashMap(), u);
		 
	boolean c = (Boolean) Ognl.getValue("'tom' not in {'tom','jerry','jack','rose'}", new HashMap(), u);
		
	System.out.println(b);//true
	
	System.out.println(c);//false
		
	}


13投影(瞭解)

	@Test
	public  void  fun16() throws Exception{
		//集合的投影(瞭解)
		List<User> list = new ArrayList<User>();
		//--------
		User u1 = new User();
		u1.setName("tom");
		list.add(u1);
		//---------
		User u2 = new User();
		u2.setName("jerry");
		list.add(u2);
		
		System.out.println(Ognl.getValue("#this.{name}", new HashMap(),list));
		
	}

14過濾(瞭解)

	@Test
	public  void  fun17() throws Exception{
		//集合的選擇(過濾)
		//集合的投影(瞭解)
		List<User> list = new ArrayList<User>();
		//--------
		User u1 = new User();
		u1.setName("tom");
		u1.setAge(10);
		list.add(u1);
		//---------
		User u2 = new User();
		u2.setName("jerry");
		u2.setAge(20);
		list.add(u2);
		
		
		System.out.println(Ognl.getValue("#this.{?age > 18}", new HashMap(),list));
	}
	
15_OGNL符號 # % $
#
可以通過#key獲得context資料
#{'k':'v' , ...} 建立map
%
%{表示式} 在struts標籤希望以OGNL表示式執行,實際輸出字串。強制按照OGNL表示式執行。
%{'表示式'} 在struts標籤希望字串,但實際按照OGNL表示式執行。必須是字串,不需要執行。
$
在配置檔案從值棧獲得資料。 xml
國際化資原始檔。properties

Strtus2 OGNL表示式的結合



*上下文物件是ActionContext;
*根物件是ValueStack,它是一個棧結構,提供了壓棧和彈棧等方法,通常棧頂元素是當前方法的Action物件;
*每個請求都有自己的ActionContext,每個ActionContext中都有自己的ValueStack;


struts2為OGNL表示式準備了兩個物件
ActionContext: 作為ognl表示式的Context
valueStack: 作為ognl表示式的Root


兩個物件的建立
*都是strutsPrepareAndExecuteFilter中準備好.


ActionContext和ValueStack
ActionContext<--------------互相引用-------------->ValueStack



ValueStack 值棧【★★★★】


介紹


*ValueStack貫穿action生命週期(一次請求建立一個action例項)


*值棧:在一次請求中,struts用於共享資料。
在action的目標方法(execute)中,用於存放資料
在jsp頁面中獲得資料。
*值棧建立過程:請求開始時,struts將建立值棧物件,將值棧物件新增到request作用域。當請求結束時,tomcat銷燬request物件,於此值棧銷燬



獲得值棧



值棧物件:ValueStack
// 1.1 從request作用域獲得值棧,固定的字串“struts.valueStack”
  HttpServletRequest request = ServletActionContext.getRequest();
  ValueStack valueStack =  (ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
// 1.2 【掌握】 
  ValueStack valueStack2 = ActionContext.getContext().getValueStack();

root 分析


*底層型別是:CompoundRoot,實際就是ArrayList。CompoundRoot物件提供更多的api,將List修改了棧。
public CompoundRoot cutStack(int index):從指定的索引開始(含)建立一個新的子集合
public Object peek():取棧頂物件
public Object pop():彈出棧頂物件
public void push(Object o):壓入新物件到棧頂
*root用於存放物件,此物件沒有名稱。
*獲得root中資料,通過使用屬性獲得


操作【★★★★★】

值棧資料的操作



public Map<String, Object> getContext():獲取contextMap
public CompoundRoot getRoot();獲取contextMap中的根物件。跟物件是一個List,實現了一個棧結構
public void setValue(String expr, Object value);
設定值。存在哪裡呢?注意:String expr是一個OGNL表示式。以#存在contextMap中,不以#開頭,相當於設定棧中物件的屬性(從棧頂擼到棧底)。
public String findString(String expr);
查詢物件,返回的是String型別。注意:String expr是一個OGNL表示式。
擴充套件:從根中棧頂搜尋指定的屬性,如果沒有找到,會當做key從contextMap中找。
public Object peek();獲取棧頂物件
public Object pop();彈棧頂物件出棧
public void push(Object o);壓棧
public void set(String key, Object o);
向根中加入資料。棧頂是一個Map,直接把資料放到Map中,如果棧頂不是Map,建立一個新Map,把資料放到Map中。
public int size();棧長度


存放資料

值棧的資料必須通過struts標籤在jsp中獲得

*valueStack.push() 將物件壓入棧中。一般情況壓入javabean

操作1:壓棧push

ActionContext.getContext().getValueStack().push(javabean)

jsp 通過 javabean屬性獲得


*valueStack.set(key,value) 給物件進行名稱,底層使用Map

操作2:底層使用自定義Map set

ActionContext.getCotnext().getValueStack().set(key,value)

jsp 直接 key獲得資料


		//1 獲得值棧
		// 1.1 從request作用域獲得值棧,固定的字串“struts.valueStack”
		HttpServletRequest request = ServletActionContext.getRequest();
		ValueStack valueStack =  (ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
		// 1.2 【掌握】 
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		
		
		// 2 root操作
		// 2.1 壓棧:給棧中新增物件,自定義javabean
		// * jsp 需要通過javabean屬性獲得
		valueStack.push(new User("小強","998"));
		// 2.1 可以給物件進行命名,底層使用Map
//		Map<String,String> data = new HashMap<String, String>();
//		data.put("ds", "屌絲");
//		valueStack.push(data);
		//等效
		valueStack.set("dzd", "屌中屌");

jsp獲得資料

*通過javabean的屬性,或 map的key ,直接獲得資料
	值棧 <br/>
	<h3>root</h3>
	<s:property value="userName"/>  <%-- 通過javabean屬性獲得資料 --%> <br/>
	<s:property value="ds"/>  <%-- 通過map key 獲得資料,相當於javabean --%> <br/>
	<s:property value="dzd"/>  <%-- 通過map key 獲得資料,相當於javabean --%> <br/>


context 分析






*context型別Map<String,Object>  給一個物件進行命名。value的型別Object,所以可以存放任意內容。
*通過 #key 獲得context中的資料

context提供一些固定key,表示不同的作用


application:表示application作用域(servletContext)
session:表示session作用域
request:表示request作用域
attr:依次從page、request、session、application獲得資料
parameters:所有的請求引數
root:對值棧的引用。//ActionContext.getContext().getValueStack()


ActionContext的主要操作



private Map<String, Object> context:contextMap
public Map<String, Object> getApplication():獲取ServletContext中的所有Attributes
public Map<String, Object> getContextMap():獲取contextMap的引用
public Map<String, Object> getParameters():獲取請求引數的Map
public Map<String, Object> getSession():獲取HttpSession中的所有Attributes
public ValueStack getValueStack():獲得ValueStack物件的引用
public Object get(String key):從contextMap中根據key獲得物件
public void put(String key, Object value):向contextMap中存放資料


存放資料

*ActionContext.getContext().put(key,value)
*context固定key,struts提供更方便api進行操作
固定key:session , ActionContext.getContext().getSession().put(key,value)

固定key:application ,ActionContext.getContext().getApplication().put(key,value)

ActionContext.getContext().put(key,value)

jsp 通過 #key獲得


		// 3 context操作 , 必須通過  #key獲得
		// * 給context設定內容
		ActionContext.getContext().put("gf", "request_gf");
		// * 給context的固定key=session設定內容,value=Map ,設定的資料對Map進行操作
		ActionContext.getContext().getSession().put("gf", "session_gf");
		// * 給context的固定key=application設定內容,value=Map,設定的資料對Map進行操作
		ActionContext.getContext().getApplication().put("gf", "application_gf");


jsp獲得資料

*通過 #key 獲得 context中的內容
	<h3>context</h3>
	<s:property value="#gf"/> <%-- 通過#key 獲得context中內容 --%> <br/>
	<s:property value="#session.gf"/> <%-- 通過#key 獲得固定值session的資料 --%> <br/>
	<s:property value="#application.gf"/>  <br/>



request作用域資料操作

action存放
public class VsDemo2Action extends ActionSupport {
	
	@Override
	public String execute() throws Exception {
		
		// 1 root 操作
		ActionContext.getContext().getValueStack().push("jack");
		ActionContext.getContext().getValueStack().push("rose");
		
		
		
		// 2 request作用域分析
		// * context資料
		ActionContext.getContext().put("ds", "context_ds");
		// * root資料
		ActionContext.getContext().getValueStack().set("ds", "root_ds");
		// * request作用域
		ServletActionContext.getRequest().setAttribute("ds", "request_ds");
		
		return SUCCESS;
	}

}



jsp獲得資料
<body>
	<s:debug></s:debug>

	值棧 <br/>
	<h3>root</h3>
	<s:property value="[1]"/>  <%--從下標1開始,獲得一個新的棧 --%> <br/>
	<s:property value="top"/>  <%--獲得棧頂資料 --%> <br/>
	<s:property value="[1].top"/>  <%--從新的棧獲得 棧頂資料 --%> <br/>
	
	
	<h3>request作用域</h3>
	<%-- 
		原始獲得方式 ,直接從context
	--%> 
	1 <s:property value="#ds"/> 		<br/>
	
	<%-- findValue('')
		先從root獲得
		如果沒有,再從context獲得
	 --%> <br/>
	2 <s:property value="ds"/> 	
	
	<%-- struts增強request,
		首先從 request作用域
		如果沒有,在執行findValue() 
			先root
			在context
	--%> <br/>
	3 <s:property value="#request.ds"/> 
	
	<%=request %>
</body>

Ognl和Struts使用上的結合:


表單提交,其中提交的鍵可以看作是ognl表示式
Action中有User物件,我們想直接將表單引數提交到User物件中封裝,
做法:
1>提交的引數的鍵=> user.name就會在值棧中查詢名為user的物件,並賦值到該物件的name屬性
2>使用ModelDriven,我們的Action在getModel方法中將User物件返回.ModelDriven攔截器會將我們返回的User物件放入值棧中(棧頂),
那麼 在表單中直接提交name,將會把name值裝入棧頂的user物件的name屬性

Struts2標籤



開啟struts-2.3.7\docs\WW\tag-reference.html
可以看到Struts2提供的所有標籤。其中分為“通用標籤”和“UI標籤”兩大部分


1_Struts2通用標籤之資料標籤



<s:property>(重要)


<s:property>標籤用來執行OGNL表示式,最為常用的方式是使用它在頁面中輸出ValueStack或ActionContext中的資料。
<s:property value=”#request.hello”/>,等於ActionContext.getContext().getRequest().get(“hello”)。



<s:set>
<s:set>標籤用來建立一個變數,儲存到指定的範圍內。
<s:set var=”myVar” value=”#parameters.score[0]” scope=”page”/>,
建立一個變數,儲存到page範圍,key為myVar,值為“#parameters.score[0]”的運算結果。

scope的可選值中的action是我們陌生的範圍,它是scope的預設值。它表示儲存到request和OgnlContext兩個範圍中。即比request範圍多出了一個OgnlContext範圍。

<s:set var=”myVar” value=”#parameters.score[0]” />
<s:property value=”#myVar”/>等同於ActionContext.getContext().get(“myVar”);
<s:property value=”#request.myVar”/>等同於ActionContext.getContext.getReuqest().get(“myVar”);


<s:push>

<s:push>標籤是把指定值暫時壓入到值棧中,當執行完<s:push>標籤後,壓入到值棧中的值會被彈出。
<s:push value=”’hello’”/>等於把hello字串壓入到值棧中後,馬上又彈出了,相當於什麼都沒做。
<s:push value=”#session.user”>把user壓入到值棧中
  <s:property value=”username”/>列印值棧元素的username屬性
  <s:property value=”password”/>列印值棧元素的password屬性
</s:push>把user從值棧中彈出


<s:url>

<s:url>標籤通常用來生成action路徑,它與<c:url>標籤很相似。
<s:url action=”TestAction”/>在頁面中列印/contextpath/TestAction.action。
也就是說它與<c:url>一樣會生成全路徑。而且無論給出字尾“.action”!
action屬性的值只需要與struts.xml檔案中<action>元素的name屬性值相同即可。
<s:url action=”TestAction” namspace=”/” />還可以指定名稱空間
<s:url action=”TestAction”>
  <s:param name=”name” value=”’張三’”/>
</s:url>
頁面中列印為:/ognl/TestAction.action?name=%E5%BC%A0%E4%B8%89
還可以為URL指定引數,其中引數包含中文時自動使用URL編碼。
其中<s:param>是用來作為子標籤的,它的作用是指定引數。它的value屬性值為Ognl表示式,
所以我們需要把“張三”用單引號引起來,表示Ognl表示式的字串常量。



<s:a>

它用來生成超連結,與<s:url>相似!
<s:a action=”TestAction” namespace=”/”>新增使用者
  <s:param name=”name” value=”’張三’”/>
</s:a>


<s:debug>

Debug標籤用於除錯,它在頁面中生成一個“[Debug]”超連結,單擊這個超連結,
可以檢視ValueStack和ActionContext中儲存的所有物件。


2_Struts2通用標籤之控制標籤



控制標籤很好理解,就是流程控制了。例如if、elseif等,以及iterator等


<s:if>、<s:elseif>、<s:else>

<!—
在瀏覽器中輸入:http://localhost:8080/tagtest/index.jsp?score=85
-->
<s:set name="score" value="#parameters.score[0]"/>
<s:property value="#score"/>: 
<s:if test="#score > 100 || #score < 0">
	<s:property value="'輸入錯誤'"/>
</s:if>
<s:elseif test="#score >= 90">
	<s:property value="'A'" />
</s:elseif>
<s:elseif test="#score >= 80">
	<s:property value="'B'" />
</s:elseif>
<s:elseif test="#score >= 70">
	<s:property value="'C'" />
</s:elseif>
<s:elseif test="#score >= 60">
	<s:property value="'D'" />
</s:elseif>
<s:else>
	<s:property value="'E'"/>
</s:else>



<s:iterator>

<s:iterator>標籤可以用來迭代一個集合,可以迭代的集合有:Collection、Map、Enumeration、Iterator或者是陣列。iterator標籤在迭代過程中,會把當前物件暫時壓入值棧,這樣在子標籤中就可以直接訪問當前物件的屬性(因為當前物件在棧頂),在標籤體執行完畢後,位於棧頂的物件就會被刪除,在迴圈的第二圈時,把新的當前物件再壓入值棧中。

<s:iterator value="{'zhangSan','liSi','wangWu' }">
	name: <s:property/><br/>
</s:iterator>

如果為<s:iterator>標籤指定了var屬性,那麼當前物件不只是壓入到了值棧中,而且還會被新增到OgnlContext中。

<s:iterator value="{'zhangSan','liSi','wangWu' }" var="name">
	name: <s:property value="#name"/><br/>
</s:iterator>




IteratorStatus類在org.apahce.struts2.views.jsp包下。下面是對該類常用方法的介紹:
*public int getCount():得到當前已經迭代的元素的總數。
*public int getIndex():得到當前迭代的元素的索引。
*public boolean isEven():判斷當前迭代的元素的個數是否是偶數。
*public boolean isOdd():判斷當前迭代的元素的個數是否是奇數。
*public boolean isFirst():判斷當前迭代的元素是否是第一個元素。
*public boolean isLast():判斷當前迭代的元素是否是最後一個元素。


在OGNL表示式中使用IteratorStatus類的方法時,可以直接使用:count、index、even、odd、first、last屬性。
<s:iterator value='{"one", "two", "three"}' status="status">
	<s:property value="#status.count"/>,
	<s:property value="#status.index"/>,
	<s:property value="#status.even"/>,
	<s:property value="#status.odd"/>,
	<s:property value="#status.first"/>,
	<s:property value="#status.last"/><br/>
</s:iterator>
<hr/>
<s:iterator value="#{'1':'one','2':'two','3':'three','4':'four'}" status="st">
<s:property value="key"/>:<s:property value="value"/><br/>
</s:iterator>


3_Struts2標籤UI標籤之表單標籤



Struts2的表單標籤還是比較好用的,但它也存在一些敗筆,
例如主題這一部分就不是很靈活。所以導致開發中沒有公司會使用它提供的主題。


Struts2標籤的優勢:
*簡化程式碼;
*自動資料回顯;
*指定主題樣式(說是優點,但很多人也會認為這是缺點);


<form action="<c:url value='/user/LoginAction.action'/>" method="post">
  使用者名稱 <input type="text" name="username"/><br/>
  密 碼 <input type="password" name="password"/><br/>
  <input type="submit" value="登入"/>
</form>
<hr/>
<s:form action="LoginAction" namespace="/user">
	<s:textfield name="username" label="使用者名稱" />
	<s:password name="password" label="密 碼" />
	<s:submit value="登入" />
</s:form>


a.表單標籤入門


<s:form>

通過action和namespace兩部分來指定請求路徑,action直接給<action>元素的name值即可,無需給出字尾“.action”;
method預設為post;
會自動新增id屬性,值與action屬性值相同;
整個表單會生成在<table>中。


<s:textfield>

對應<input type=”text”>標籤;
通過lable來生成<lable>標籤;


<s:password>

對應<input type=”password”>標籤;
通過label來生成<lable>標籤


<s:submit>
對應<input type=”submit”>標籤


b.表單主題


整個表單都會在<table>中生成,這也就說明無需為每個表單項新增<br/>。因為在表格中就無需再換行了。


生成在表格中是因為<s:form>標籤的theme屬性的預設值為xhtml,它表示一個主題樣式。這個主題樣式由Freemarker模板來完成


取消主題


在<s:textfield>的theme屬性指定為simple,那麼這個表單項就使用簡單主題;
在<s:form>的theme屬性指定為simple,那麼整個表單都使用簡單主題;
設定struts.ui.theme常量為simple,那麼所有表單標籤的預設主題都是simple


c.自動回顯


當表單提交後,再返回到表單頁面後,html標籤不可能幫我們回顯資料。而Struts2的表單標籤可以做到這一點。原因是當前Action就在值棧頂,而表單標籤會從值棧中獲取資料來回顯。


4_表單標籤之選擇性標籤



<s:redio>標籤


表單標籤中簡化比較大的標籤可以不在需要寫迴圈,還有就是自動回顯。我們都知道在下拉列表、單選、複選中,手動回顯是比較麻煩的事。但使用Struts2的表單標籤就方便多了。
<s:radio list="#{'male':'男','female':'女'}" name="gender"/>
list指定的是一個Map,預設key為實際值,而value為顯示值。
也可以為list屬性指定為list:
<s:radio list="{'男','女'}" name="gender"/>
這時實際值和顯示值是相同的


<s:checkboxlist>標籤

<s:checkboxlist list="#{'read':'看書','netplay':'上網','music':'音樂' }" name="hobby"/>

<s:select>標籤

下拉列表與上面兩個標籤也一樣,都是選擇性的!使用它也是無需迴圈,無需處理迴圈
<s:select name="city" list="#{'bj':'北京','sh':'上海','gz':'廣州'}"/>


Token機制

5.1表單重複提交(回顧)

*提交成功,點選重新整理按鈕

*提交成功,先回退,再提交

*點選提交,網路延時,記錄點選

5.2防止表單重複提交(回顧)

*驗證碼解決重複提交

*手動方式解決,token機制

       #1提供表單,表單中儲存hidden存在uuid(資料1)

       #2session需要儲存同樣的資料(資料2)

       #3當提交,伺服器需要比較資料1和資料2,比較之後伺服器端刪除

              一樣:正常提交

              不一樣:伺服器刪除了,瀏覽器重複提交了

5.3struts token

*<s:token> : 1.表單生成hidden  2.在伺服器session儲存生成資料(此資料struts生成,key不知道)

*token攔截器,預設棧沒有

       如果一樣,放行

       如果不一樣,返回“invalid.token”

5.4執行流程分析


struts.xml宣告使用預設攔截器和token攔截器及配置重複提交返回頁面

	<!-- #2 token:使用者註冊 -->
		<action name="userAction_*" class="cn.itcast.b_token.UserAction" method="{1}">
			<!-- 註冊成功 -->
			<result name="register">/b_token/success.jsp</result>
			<!-- 重複提交 -->
			<result name="invalid.token">/b_token/demo.jsp</result>
			
			<!-- 宣告使用預設攔截器棧 -->
			<interceptor-ref name="defaultStack"></interceptor-ref>
			<interceptor-ref name="token"></interceptor-ref>
		</action>