在泛型處理常式(ashx)中利用HttpTaskAsyncHandler來完成async/await之需求

今天在撰寫泛型處理常式(ashx)時,遇到一個非同步的應用上問題,也就是ProcessRequest在async/await運行尚未完成時期,它本身就運行完成,因此直接跳離,導致await Task後的回傳結果根本無處控制與顯示,因為管理者不見啦!當下很直覺的想說,那我就在ProcessRequest前面加個async就可以了啊!才發現事實不是笨蛋想的那麼簡單。

我們都知道泛型處理常式(ashx)需要繼承並實作IHttpHandler介面,問題是IHttpHandlerr介面下根本不支援ProcessRequest去處理async/await的動作,這時候就需要繼承IHttpAsyncHandler來達成非同步功能,但是引用IHttpAsyncHandler又必須要實作一堆垃圾方法,有沒有更簡單的方式呢?答案就是繼承System.Web.HttpTaskAsyncHandler類別。

繼承System.Web.HttpTaskAsyncHandler類別後,你可以繼續使用你習慣的ASHX架構繼續開發程式,然後你可以複寫(override)一個叫ProcessRequestAsync的方法,它具備非同步(async)的處理能力,更妙的是,你不用再多寫一次IsReusable屬性(Property)了,程式碼變得更精簡了!真棒!

程式碼範例如下:

<%@ WebHandler Language="C#" Class="ajaxResponse" %>

public class ajaxResponse: System.Web.HttpTaskAsyncHandler
{
  //設定一個集合來存放結果集
  private System.Collections.Generic.List<ORM_Class> oResult;
  //泛型處理常式主要進入點
  public override async System.Threading.Tasks.Task ProcessRequestAsync(System.Web.HttpContext context)
  {
    //指定要存取的網址,壓入非同步工作中
    await PushToTask(new string[] {
      "http://date.jsontest.com",  //FreeJson
      "http://date.jsontest.com",  //FreeJson
      "http://date.jsontest.com"  //FreeJson
    });
    //輸出結果集之JSON
    context.Response.ContentType = "application/json; charset=utf-8";
    context.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(oResult));
  }

  //非同步工作佇列處理
  private async System.Threading.Tasks.Task PushToTask(System.Collections.Generic.IList<string> oURLs)
  {
    System.Collections.Generic.List<System.Threading.Tasks.Task> oTasks = new System.Collections.Generic.List<System.Threading.Tasks.Task>();
    System.Threading.SemaphoreSlim oControl = new System.Threading.SemaphoreSlim(oURLs.Count);
    oResult = new System.Collections.Generic.List<ORM_Class>();
    foreach (var cURL in oURLs)
    {
      await oControl.WaitAsync();
      oTasks.Add(System.Threading.Tasks.Task.Run(async () =>
      {
        try
        {
          using (System.Net.WebClient oWC = new System.Net.WebClient() { Encoding = System.Text.Encoding.UTF8 })
          {
            oResult.Add(Newtonsoft.Json.JsonConvert.DeserializeObject<ORM_Class>(
              await oWC.DownloadStringTaskAsync(new System.Uri(cURL))
            ));
          }
        }
        catch { return; }
        finally { oControl.Release(); }
      }));
    }
    //等候所有的工作完成
    await System.Threading.Tasks.Task.WhenAll(oTasks);
  }
}

//預計取用的ORM類別
class ORM_Class
{
  public string time { get; set; }
  public long milliseconds_since_epoch { get; set; }
  public string date { get; set; }
}

輸出的結果圖如下:

相關參考:

泛型處理常式 .ashx HttpTaskAsyncHandler IHttpAsyncHandler System.Threading.SemaphoreSlim System.Threading.Tasks.Task aSync aWait