在 ASP.NET MVC 用 Response.Flush 實現簡易即時進度回報
0 |
【聲明】本文介紹的做法非正統網頁介面設計方式,純屬民俗療法之個人經驗分享,使用前請先徵詢自己的專案經理、主管或系統分析師意見。
在 ASP.NET WebForm 時代,遇到長時間執行的網頁操作,若不想花時間寫 AJAX 或讓程式複雜化,我會偷懶用 Response.Flush() 簡單實現「即時狀態更新」。(參考:關於Response.Flush的小小測試)。
註:一併附上比較精緻的即時進度回報寫法:
ASP.NET MVC 架構標準的輸出做法是 Action 回傳 ActionResult,再由 Controller 引用 ActionResult 輸出結果。但在 MVC Action 裡穿插 Response.Write() + Response.Flush() 還是可以提前傳回部分結果,例如:
using System;
using System.Threading;
using System.Web;
using System.Web.Mvc;
namespace MyWeb.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
Response.Write("<div>Starting job...</div>");
Response.Flush();
var rnd = new Random();
for (var i = 0; i < 3; i++)
{
Thread.Sleep(rnd.Next(1000) + 1000);
Response.Write($"<div>{DateTime.Now:mm:ss.fff} Step {i + 1} OK</div>");
Response.Flush();
}
return Content("Job completed.");
}
}
}
但這個做法在 IE 與 Chrome 都有點問題:在 IE 不會分批顯示,而是最後一次輸出;Chrome 則有逐條顯示,但斷句怪怪的,像是先印出 Step O,K 跟下一行 Step 2 後面才一起出來。
細究其原因,是各家瀏覽器對於 transfer-encoding: chunked 規範的實作差異所致,stackoverflow 有網友分享的各種客戶端軟體實測,提到 IE10+ 處理原則是累積到 4096 Bytes 才輸出、Chrome 則為 1024 Bytes。
依此原理,我們動點手腳,每次 Flush() 時補空白將輸出內容長度擴充到 4096:
void FlushMessage(string msg)
{
string h = "<div>" + HttpUtility.HtmlEncode(msg) + "</div>";
h = h.PadRight(4096);
Response.Write(h);
Response.Flush();
}
public ActionResult Index()
{
FlushMessage("Starting job...");
var rnd = new Random();
for (var i = 0; i < 3; i++)
{
Thread.Sleep(rnd.Next(1000) + 1000);
FlushMessage($"{DateTime.Now:mm:ss.fff} Step {i + 1} OK");
Response.Flush();
}
return Content("Job completed.");
}
經過這番修改,果然在 IE 與 Chrome 都能順利顯示:
如果不想靠填充資料換取效果又只針對 Chrome,則有更精巧的控制做法 - 例如:使用 Content-Type: text/event-stream、加上 X-Content-Type-Options: nosniff ... 等參考。但評估之後,我偏好填補長度超過門檻的做法,雖然簡單粗暴,但直覺有效。
Example of using Response.Flush() in ASP.NET MVC and how to make it work for Chrome and IE.
Comments
Be the first to post a comment