在ASP.NET MVC3中使用Uploadify上傳文件時發(fā)現(xiàn),在后臺需要驗證登錄狀態(tài)的時候,Uploadify根本無法完成驗證,因此,在后臺只能使用非驗證狀態(tài)進行文件上傳 —— 眾所周知,這無異于給非登錄的不安分子提供了一個絕佳的機會。
為了解決這個問題,我們就需要去了解Uploadify,至少在我開始要解決這個問題的時候是這么想的.在Uploadify的官方網(wǎng)站的幫助文檔中找到了這么專門的一節(jié):Using Sessions with Uploadify.
這文章無疑對我們解決問題是個好的開端。也許你會問,客戶端也有Session么?如果你是以服務(wù)器那種角度考慮,我可以非??隙ǖ母嬖V你,當然——沒有! HTTP的請求是一個無狀態(tài)請求,Session的一切存在都以客戶端和服務(wù)端的交換標志而延續(xù)的。在大部分情況下,是在Cookies里面設(shè)置SessionId來達到此種目的。在客戶端不支持Cookies的時候,可能會在URI中加入標志來達到同樣的目的。至于更深入的討論就不屬于本文范圍。這里做出說明,是想讓我們的問題更能針對的解決,將問題縮小到:Uploadify如何將Cookies傳遞到服務(wù)端。
在上面提及的文檔中,官方給出了PHP的解決方案,代碼非常簡單:
/*PHP在客戶端初始化Uploadify的代碼*/
$('#file_upload).uploadify({
// Your normal options here
formData : { '<?php echo session_name();?>' : '<?php echo session_id();?>' }
});
/*PHP服務(wù)端的代碼*/
$session_name = session_name();
if (!isset($_POST[$session_name])) {
exit;
} else {
session_id($_POST[$session_name]); // 將當前的SessionId設(shè)置成客戶端傳遞回來的SessionId
session_start();
}
從代碼可以看出,官方處理的具體思路是這樣的:1. 加載含有Uploadify的頁面時,將Cookies中的SessionId寫入到Uploadify中的formData中;2. 在客戶端提交的時候,SessionId會被當作表單數(shù)據(jù)被Uploadify一并提交到服務(wù)端;3. 在服務(wù)端檢測,將提交上來的SessionId提取出來,并將當前狀態(tài)以提交的SessionId為藍本進行操作。這樣,就能對操作進行驗證,達到控制的目的。
有了這個藍本,在ASP.NET MVC3中解決起來也就不難了,按著官方的思路來即可:
1. 首先,將當前的狀態(tài)存入formData中:
/*視圖中的代碼,使用Razor語法.*/
$("#fileupload").uploadify({
'formData' : { @foreach(string k in Request.Cookies.AllKeys){
@:'cookie_@(k)' : '@Request.Cookies[k].Value',
}
'' : ''}
})
為了完全模擬客戶端狀態(tài),我們將所有的Cookies都放在了formData中,并且,為了以示區(qū)分,我們將鍵值名稱統(tǒng)一用cookie_開頭。
2. 客戶端處理安全之后,我們就需要處理服務(wù)端了,在MVC中,我們沒有PHP那種Session_id(Id)這種現(xiàn)成方法可以用(如果你有,請告訴我~),我們需要在Global.asax中設(shè)置,代碼如下:
protected void Application_BeginRequest(object sender, EventArgs e)
{
try
{
HttpRequest request = HttpContext.Current.Request;
foreach (string k in request.Form.Keys)
{
if (k.IndexOf("cookie_") >= 0)
AppendingCookies(k.Remove(0, 7), request.Form[k], request);
}
}
catch { }
}
private void AppendingCookies(string cookieName, string cookieValue, HttpRequest request)
{
HttpCookie hc = request.Cookies.Get(cookieName);
if(null == hc)
hc = new HttpCookie(cookieName);
hc.Value = cookieValue;
request.Cookies.Set(hc);
}
我們在開始請求的時候,將請求攔截,并檢查請求中提交上來的表單數(shù)據(jù),如果發(fā)現(xiàn)有以"cookie_"開頭的數(shù)據(jù),就將此數(shù)據(jù)提取出來,設(shè)置到當前請求的Cookies中。
恩,事情到這里,顯得就很完美了,Uploadify用起來已經(jīng)和普通的請求沒有任何差別了。但有一個小小的缺憾,就是每次請求,無論請求那一個數(shù)據(jù),都會進行這么一遍操作,萬一要是其他表單中真有這么一個"cookie_"開頭的數(shù)據(jù),那還真就不太好了,為了解決這個問題,我們需要對操作進行一點點改進,改進后代碼如下:
protected void Application_BeginRequest(object sender, EventArgs e)
{
try
{
RouteData rd = RouteTable.Routes.GetRouteData((HttpContextBase)new HttpContextWrapper(HttpContext.Current));
string c = rd.GetRequiredString("controller");
string a = rd.GetRequiredString("action");
if (c.ToLower() == "uploadfile" && a.ToLower() == "upload")
{
HttpRequest request = HttpContext.Current.Request;
foreach (string k in request.Form.Keys)
{
if (k.IndexOf("cookie_") >= 0)
AppendingCookies(k.Remove(0, 7), request.Form[k], request);
}
}
}
catch { }
}
private void AppendingCookies(string cookieName, string cookieValue, HttpRequest request)
{
HttpCookie hc = request.Cookies.Get(cookieName);
if(null == hc)
hc = new HttpCookie(cookieName);
hc.Value = cookieValue;
request.Cookies.Set(hc);
}
這里uploadfile是MVC中的控制器,upload是Action,我們在操作之前,檢視一下當前的請求是否是文件上傳,如果是,我們再來執(zhí)行這些操作,這樣,出現(xiàn)問題的可能性就降低了。
寫到這里,我們就已經(jīng)完全解決Uploadify的狀態(tài)問題了,甚至比官方處理的還要好些,既然這樣,那就先告一段落。我們文章開始說過,這個不是Uploadify獨有的問題,而是Flash的問題,因此,在使用Flash和系統(tǒng)做交互時,該如何處理呢?其實在解決我們文件上傳驗證的問題的時候,我們已經(jīng)了解了處理此類問題的方法,我們完全可以參照Uploadify的處理方法來完成操作即可:
1. 將服務(wù)端的當前請求狀態(tài)悉數(shù)放入到客戶段準備和Flash交互的代碼中(可以是JS,可以是HTML);
2. 客戶端通過Flash將已經(jīng)存放到客戶端的請求狀態(tài)封裝到Flash的請求中,一并提交到服務(wù)端;
3. 服務(wù)端將Flash的請求中的Cookies信息提取出來,并設(shè)置到當前請求的Cookies中。
另外一種方法是,將Flash中的數(shù)據(jù)導出來,使用Js來進行提交,這種方式只需要將Flash中交互數(shù)據(jù)提取出來,JS的請求會自動將客戶端的Cookies傳遞到服務(wù)端,這種方法相對來說更加簡單高效。Uploadify也提供了HTML5版本,只是是收費的,但相信,用的應該就是這種方法。
本文為原創(chuàng),轉(zhuǎn)載請保留出處:MitchellChu's Blog