當頁面內容來源於資料庫、運算產生、或需依請求參數動態組裝時,傳統做法產生暫存 .aspx/.html 檔案再導向,會造成磁碟 I/O、暫存檔清理、併發衝突等問題。**IHttpHandler (.ashx) 直接輸出記憶體流至 Response**,零磁碟落地、高效能、天然支援串流。
<%@ WebHandler Language="C#" Class="HtmlHandler" %>
using System;
using System.IO;
using System.Web;
public class HtmlHandler : IHttpHandler
{
public bool IsReusable => true; // 無狀態,可重用
public void ProcessRequest(HttpContext context)
{
HttpResponse response = context.Response;
HttpRequest request = context.Request;
// 1. 設定回應標頭
response.ContentType = "text/html; charset=utf-8";
response.Cache.SetCacheability(HttpCacheability.Public);
response.Cache.SetMaxAge(TimeSpan.FromMinutes(5)); // 快取 5 分鐘
response.BufferOutput = false; // 關閉緩衝,直接串流輸出
// 2. 取得動態內容 (此處示範字串,實際可從資料庫/快取/運算取得)
string htmlContent = GetDynamicHtml(request.QueryString["id"]);
// 3. 字串轉位元組陣列 (UTF-8 編碼)
byte[] htmlBytes = System.Text.Encoding.UTF8.GetBytes(htmlContent);
// 4. 建立記憶體流並分塊寫入輸出流 (模擬大內容串流)
using (MemoryStream ms = new MemoryStream(htmlBytes))
{
const int BufferSize = 16 * 1024; // 16KB 緩衝區
byte[] buffer = new byte[BufferSize];
int bytesRead;
while ((bytesRead = ms.Read(buffer, 0, BufferSize)) > 0)
{
response.OutputStream.Write(buffer, 0, bytesRead);
response.Flush(); // 立即發送給客戶端 (長連線/大內容有效)
}
}
}
private string GetDynamicHtml(string id)
{
// 模擬從資料庫查詢
if (string.IsNullOrEmpty(id)) id = "default";
return $@"<!DOCTYPE html>
<html><head><meta charset='utf-8'/><title>動態內容 - {id}</title></head>
<body><h1>成功: 動態生成 HTML</h1><p>ID: {id}</p><p>時間: {DateTime.Now}</p></body></html>";
}
}
<iframe src="HtmlHandler.ashx?id=12345" width="100%" height="400" frameborder="0"></iframe>
<!-- 或 AJAX 載入 -->
<div id="dynamicContent"></div>
<script>
$.get("HtmlHandler.ashx?id=12345", function(html) {
$("#dynamicContent").html(html);
});
</script>
| 成員 | 說明 |
|---|---|
IsReusable | 回傳 true 表示處理常式無狀態,可跨請求重用實例 (效能佳);false 表示每請求新建實例 (含狀態時)。 |
ProcessRequest(HttpContext) | 核心進入點,在此實作所有邏輯:讀取 Request、設定 Response 標頭、生成內容、寫入 OutputStream。 |
public abstract class StreamHandlerBase : IHttpHandler
{
public bool IsReusable => true;
public void ProcessRequest(HttpContext context)
{
try
{
PrepareResponse(context.Response);
using (var stream = GenerateContentStream(context.Request))
{
if (stream == null)
{
WriteError(context.Response, "Content generation failed");
return;
}
StreamToResponse(stream, context.Response);
}
}
catch (Exception ex)
{
WriteError(context.Response, "Server error: " + ex.Message);
}
}
protected virtual void PrepareResponse(HttpResponse response)
{
response.BufferOutput = false;
response.Cache.SetCacheability(HttpCacheability.NoCache); // 預設不快取
}
protected abstract Stream GenerateContentStream(HttpRequest request);
private void StreamToResponse(Stream source, HttpResponse response)
{
const int BufferSize = 32 * 1024;
byte[] buffer = new byte[BufferSize];
int read;
while ((read = source.Read(buffer, 0, BufferSize)) > 0)
{
response.OutputStream.Write(buffer, 0, read);
}
}
private void WriteError(HttpResponse response, string msg)
{
response.StatusCode = 500;
response.ContentType = "text/plain; charset=utf-8";
byte[] err = System.Text.Encoding.UTF8.GetBytes(msg);
response.OutputStream.Write(err, 0, err.Length);
}
}
// 具體實作
public class ReportHandler : StreamHandlerBase
{
protected override Stream GenerateContentStream(HttpRequest request)
{
string reportId = request.QueryString["id"];
// 從資料庫/服務取得報表 HTML 字串
string html = ReportService.GetReportHtml(reportId);
return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(html));
}
}
Flush(),瀏覽器可漸進式渲染,體驗更佳。SetMaxAge、SetETag;動態內容設 NoCache 或短期快取。text/html Gzip/Deflate,無需手動壓縮。IHttpAsyncHandler 或使用 HttpTaskAsyncHandler 釋放執行緒等待 DB/外部 API。| 特性 | .ashx (IHttpHandler) | .aspx (Page) | Web API / MVC Controller |
|---|---|---|---|
| 生命週期 | 極簡 (單一方法) | 完整 Page 生命週期 | Controller/Action 流程 |
| ViewState | 無 | 有 | 無 |
| 適用場景 | 串流輸出、圖片動態生成、檔案下載、RSS/Atom、Webhook 接收 | 完整頁面、表單、ViewState 需求 | REST API、JSON/XML、SPA 後端 |
| 效能開銷 | 最低 | 最高 | 中等 |
| 路由支援 | 需手動對應 / UrlRoutingModule | 內建 | 內建屬性路由 |
IHttpHandler (.ashx) 是 ASP.NET 輕量級輸出動態內容的最佳選擇:無 ViewState、無完整頁面生命週期、直接串流輸出、效能極佳。掌握 BufferOutput=false、分塊寫入、快取標頭、非同步擴展,即能構建高效能的動態 HTML、圖片、報表、檔案下載等串流端點。現代專案中,純 API 建議改用 ASP.NET Core Minimal API / Controller,但 .ashx 於舊系統維護、特定串流場景仍具不可替代價值。