TL; DR:我想實現此行爲:服務器端處理請求
- 我裝飾用自定義控制器的具體POST操作 屬性,它僅指定一個自定義int參數,以秒爲單位, like [PreventSpam(Seconds = 10)] =>如果POST對象相同且兩者之間的時間間隔小於「秒」,則來自同一客戶端 的兩個請求被視爲「重複」
- 處理請求時,如果它被檢測爲「重複」 (根據以前的定義),它不應該被處理。 應該返回先前的非重複請求的ActionResult(不管它是什麼,視圖,JSON ..)。當然, 意味着ActionResult應該已經被第一個 請求緩存了。
如果我能實現這一點,我永遠不會有關於何時提交一個請求,因此複製的要求,而不必通過javascript來手工處理這一執行多次點擊的用戶再次擔心。
我遇到了MVC 5的下列問題:當用戶快速點擊表單的提交按鈕時,會生成多個相同的POST請求。
爲了安全起見,我們想解決這個問題的服務器端
我(沒有人可以阻止惡意用戶提交多個請求,如果我只能通過JavaScript ..禁用提交按鈕)想要達到以下結果:當在特定動作(假設它們之間的時間間隔小於10秒)上檢測到多個相同的請求時,僅考慮第一請求 - >所有以下請求都應該正確指向由第一個請求生成的「ActionResult」同樣需要被緩存,因此該行爲僅在實際執行之前一次性執行。
與本指南(http://rion.io/2013/02/24/prevent-repeated-requests-using-actionfilters-in-asp-net-mvc/),達到類似的東西(如果檢測到多個請求添加了的ModelState錯誤..)取得靈感,我想出了這個代碼:
public class PreventSpamAttribute : ActionFilterAttribute
{
//This stores the time between Requests (in seconds)
public int DelayRequest = 10;
//THIS IS CALLED *AFTER* THE FIRST ACTION HAS BEEN PROPERLY EXECUTED -> THE ACTIONRESULT OBJECT HAS BEEN DETERMINED
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
var cache = filterContext.HttpContext.Cache;
//I CACHE THE ACTION RESULT FOR "DelayRequest" SECONDS, USING THE GENERATED HASH AS KEY
cache.Add(_getHash(filterContext.HttpContext), filterContext.Result, null, DateTime.Now.AddSeconds(DelayRequest), Cache.NoSlidingExpiration, CacheItemPriority.Default, null);
base.OnResultExecuted(filterContext);
}
//THIS IS CALLED *BEFORE* CALLING THE ACTION
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var cache = filterContext.HttpContext.Cache;
var hash = _getHash(filterContext.HttpContext);
//IF I ALREADY HAVE A CACHED RESULT IT MEANS THAT THE USER CLICKED MULTIPLE TIMES,
//INSTEAD OF CALLING THE ACTION I SIMPLY RETURN THE ACTIONRESULT THAT I CACHED..
if (cache[hash] != null)
filterContext.Result = (ActionResult)cache[hash];
base.OnActionExecuting(filterContext);
}
//GENERATES UNIQUE HASH, CONSIDERING VARIOUS REQUEST PARAMETERS
private string _getHash(HttpContextBase httpContext)
{
//Store our HttpContext (for easier reference and code brevity)
var request = httpContext.Request;
//Store our HttpContext.Cache (for easier reference and code brevity)
var cache = httpContext.Cache;
//Grab the IP Address from the originating Request (very simple implementation for example purposes)
var originationInfo = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress;
//Append the User Agent
originationInfo += request.UserAgent;
//Now we just need the target URL Information
var targetInfo = request.RawUrl + request.QueryString;
//Generate a hash for your strings (this appends each of the bytes of the value into a single hashed string
return string.Join("", MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(originationInfo + targetInfo)).Select(s => s.ToString("x2")));
}
}
不幸的是我感覺這種方法存在一個主要問題:如果此操作需要一些時間來生成ActionResult,該怎麼辦?當重複的請求到達服務器時,不同的線程可能仍然在第一個線程上工作,以便ActionResult尚未被高速緩存。因此,請求再次被處理。即使動作本身是同步的,至於我的理解是否有多個對同一動作的同時請求,來自線程池的不同線程將被分配不同的請求,以便它們可以被同時執行。它是否正確?
我的最終目標: 處理各種事情,服務器端,即使用戶按下的形式多次提交按鈕,沒有什麼比按壓一個單一的時間不同應該發生
UPDATE 07/09/2016: 在每個表單中的隱藏字段存儲代是不是在這種情況下真正實用:在幾個我們的意見,我們手動調用通過JavaScript後的動作,以這種方式:
myUrl = '@Url.Action("MyAction","MyController")';
myPostObject = {
entityId: 15,
someValue: "aValue",
}
$.post(myUrl, myPostObject, callbackFunction);
必須手動編輯每個「myPostObject」以包含額外的GUID字段將非常耗時並且容易出錯。這就是爲什麼我們正在尋找完全服務器端的解決方案,並且只需要用自定義屬性修飾相關操作。
請注意,模型 - 視圖 - 控制器標籤是關於模式的問題。 ASP.NET-MVC實現有一個特定的標籤。 –
你當然是對的。感謝您指出了這一點。 –