首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

基於反射的通用 Ajax 處理器:消除大量 ASHX 文件的煩惱

     編輯:關於SqlServer

痛點:傳統 Ajax 處理的擴展性困境

在早期 ASP.NET WebForms 時代,使用 jQuery Ajax 通常需要為每個後端操作創建一個獨立的 .ashx 處理程序或 .aspx 頁面。隨著功能增長,專案中會出現幾十甚至上百個處理文件,維護成本極高:

  • 文件數量爆炸,項目結構混亂
  • 公共邏輯(權限驗證、異常處理、序列化)重復編寫
  • 路由管理困難,前端調用 URL 分散難以統一

解決方案:單一入口 + 反射分發

核心思想:一個通用 HttpHandler 接收所有請求,根據參數動態調用業務類方法。

1. 處理器基礎架構

public class AjaxHandler : IHttpHandler, IRequiresSessionState
{
    public void ProcessRequest(HttpContext context)
    {
        // 核心分發邏輯
    }

    public bool IsReusable => false;
}

實現 IRequiresSessionState 以支持 Session 讀寫。

2. 核心分發邏輯

// 獲取調用信息:命名空間.類名.方法名
string methodFullName = context.Request["METHOD"]; 
string dllName = context.Request["DLL"]; // 程序集名稱

// 解析類名與方法名
int lastDot = methodFullName.LastIndexOf('.');
string className = methodFullName.Substring(0, lastDot);
string methodName = methodFullName.Substring(lastDot + 1);

// 反射創建實例
Assembly asm = Assembly.Load(dllName);
Type type = asm.GetType(className);
object instance = Activator.CreateInstance(type);

// 參數綁定(關鍵難點)
List paramValues = new List();
List paramTypes = new List();

foreach (string key in context.Request.Params.AllKeys)
{
    if (key.StartsWith("[__DOTNET__]"))
    {
        string typeName = key.Substring(12); // 移除前綴獲取類型名
        Type paramType = Type.GetType(typeName, true, true);
        paramTypes.Add(paramType);
        
        string value = context.Request.Params[key];
        object paramValue = DeserializeParameter(value, paramType);
        paramValues.Add(paramValue);
    }
}

// 調用目標方法
MethodInfo method = type.GetMethod(methodName, paramTypes.ToArray());
// 權限檢查:必須標記 [AjaxMethod] 特性
if (method.GetCustomAttributes(typeof(AjaxMethodAttribute), true).Length == 0)
    throw new Exception("無權限調用,缺少 [AjaxMethod] 特性");

object result = method.Invoke(instance, paramValues.ToArray());

// 統一返回值序列化處理
SerializeAndReturn(context, result);


3. 參數反序列化策略

  • 值類型/字符串:使用 Convert.ChangeType
  • 複雜類型:使用 JSON.NET 的 JsonConvert.DeserializeObject
  • 數組/集合:同複雜類型處理

4. 返回值統一序列化

private void SerializeAndReturn(HttpContext context, object result)
{
    if (result == null) return;

    Type resultType = result.GetType();
    string callback = context.Request["callback"]; // JSONP 支持

    if (resultType.IsClass && resultType != typeof(string))
    {
        if (resultType == typeof(XmlDocument))
        {
            context.Response.ContentType = "text/xml";
            context.Response.Write(((XmlDocument)result).OuterXml);
        }
        else if (resultType == typeof(DataTable))
        {
            string json = JsonHelper.DataTableToJson((DataTable)result);
            WriteResponse(context, callback, json, "application/json");
        }
        else
        {
            string json = JsonConvert.SerializeObject(result);
            WriteResponse(context, callback, json, "application/json");
        }
    }
    else
    {
        WriteResponse(context, callback, $"\"{result}\"", "application/json");
    }
}

5. 自定義權限特性


[AttributeUsage(AttributeTargets.Method)]
public class AjaxMethodAttribute : Attribute { }

只有標記此特性的公共方法才可被遠程調用,防止任意代碼執行風險。

6. 配置與前端調用

Web.config 註冊:

<httpHandlers>
  <add path="Ajax.ashx" verb="*" type="MyApp.AjaxHandler, MyApp" />
</httpHandlers>

jQuery 調用示例:

$.ajax({
    url: "/Ajax.ashx",
    data: {
        "METHOD": "MyApp.Services.UserService.GetUser",
        "DLL": "MyApp.Services",
        "[__DOTNET__]System.String": "user123",
        "[__DOTNET__]System.Int32": "10"
    },
    success: function(json) { console.log(json); }
});

優缺點權衡

優點缺點/風險
極大減少處理文件數量反射性能開銷(可緩存 Type/MethodInfo 緩解)
集中處理異常、日誌、權限、序列化參數綁定邏輯複雜,需妥善處理類型轉換異常
前端調用統一入口,易於攔截器擴展失去靜態類型檢查,重構時易出錯
天然支持 JSONP 跨域安全風險:必須嚴格驗證 [AjaxMethod] 特性

現代替代方案

在 ASP.NET Core / Web API 時代,這種模式已被以下技術取代:

  • Controller + Action:原生路由、模型綁定、依賴注入
  • Minimal APIs:輕量級端點定義
  • SignalR:雙向實時通信

但對於維護舊系統或無法升級框架的項目,此模式仍是極具價值的架構優化手段。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved