【聲明】本文介紹的做法非正統網頁介面設計方式,純屬民俗療法之個人經驗分享,使用前請先徵詢自己的專案經理、主管或系統分析師意見。

在 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

Post a comment