程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 關於ASP.NET WebAPI中HTTP模型的相關思考,asp.netwebapi

關於ASP.NET WebAPI中HTTP模型的相關思考,asp.netwebapi

編輯:關於.NET

關於ASP.NET WebAPI中HTTP模型的相關思考,asp.netwebapi


   對於.NET的分布式應用開發,可以供我們選擇的技術和框架比較多,例如webservice,.net remoting,MSMQ,WCF等等技術。對於這些技術很多人都不會陌生,即時沒有深入的了解,但是肯定聽說過,每種技術都各有優勢和適用范圍,沒有絕對的好壞,只有相對的合適程度。不過可惜了,今天我們講解的主題不是這幾種技術,今天主要講解的是ASP.NET WebAPI。

   對於ASP.NET WebAPI的優勢和特點,在這裡就不講了,需要用到的自然就會選擇,也不需要我浪費篇幅去講解這些,這篇博文主要講解ASP.NET WebAPI中的HTTP消息的結構和處理消息的核心對象。

一.WebAPI的HTTP概述:

   有關HTTP協議的相關內容在這裡就不做介紹,在筆者前面的博文中已經做過介紹,現在提供一下地址,因為過多的贅述就是浪費時間,我就姑且看這篇博文的讀者已經對HTTP協議和WebAPI都有所了解。博文地址:

http://www.cnblogs.com/pengze0902/p/5976388.html

http://www.cnblogs.com/pengze0902/p/6224792.html

http://www.cnblogs.com/pengze0902/p/6230105.html

   1.在.NET4.5之前的版本中,處理HTTP的核心對象:

      (1).在客戶端:System.Net.HttpWebRequest用於初始化HTTP請求,處理相關的響應; System.Net.HttpWebResponse處理HTTP響應頭和數據讀取的檢索。

      (2).在服務器端:System.Web.HttpContext,System.Web.HttpRequest,System.Web.HttpResponse類用在ASP.NET上下文中,代表單個請求和響應。System.Net.HttpListenerContext類,提供對HTTP請求和響應對象的訪問。

   2.在.NET4.5版本中,處理HTTP的核心對象:

      (1).在客戶端和服務器端使用同樣的類。(HttpRequestMessage和HttpResponseMessage對象中不包含上下文消息,所以可以在服務器和客戶端共用。)

      (2).由於在.NET4.5中引入了TAP(異步任務模型),所以在新的HTTP模型中,處理HTTP請求的方法可以使用async和awit實現異步編程。(可以簡單高效的實現異步編程)

    我們對於新舊的HTTP編程模型時,會很容易的發現在新版本的HTTP模型中,無論是編程的難度和代碼編寫的精簡度,已經執行的效率都是很高的。在對於Web項目的開發中,我們對HTTP知識的了解是必要的,對於ASP.NET的HTTP處理的原理在這裡就不做具體的介紹,網上也有比較多的文章可供閱讀和了解。

    對於ASP.NET的HTTP處理方式的了解,是我在開發微信公眾平台時進一步學習的,微信公眾平台提供了對外訪問的接口,我們的程序和服務器對微信服務器的接口進行請求訪問,微信服務器獲取HTTP請求後,返回處理結果,本地服務器獲取返回結果。這樣一個請求-響應模式,組成一個會話。對於微信公眾平台的開發對於很多剛學習.NET的人來說有些高大(當然這是相對而言),即時開發過很多次這個類別的程序的人(調用第三方接口的開發)也不一定可以很清晰的知道這個其中的原理,筆者覺得對於這樣的第三方平台的開發,其主要的核心部分就是對於HTTP協議的處理,建立請求、獲取響應消息和解析消息這三大步驟,返回的消息內容一般為json或者xml,獲取響應消息後,主要是對消息內容的反序列化,獲得消息的實體信息,進而在程序中進一步處理。

    在WeAPI中消息的產生和解析,以及消息的格式都是可以動態的創建和協商,下面我們進一步的了解實現這一過程的核心對象。

二.WebAPI的HTTP消息解析:

      HTTP協議的工作方式是在客戶端和服務器之間交換請求和響應消息,那麼這也就可以說明HTTP的核心就是消息,對於“消息”的了解,我們只要知道消息分為“消息頭部”和“消息內容”,我們接下來的對新HTTP編程模型的介紹的主體就是“消息頭部”和“消息內容”。

      在命名空間System.Net.Http中,具有兩個核心對象:HttpRequestMessage和HttpResponseMessage。兩個對象的結構如下圖:

名稱 說明 Version 獲取或設置 HTTP 消息版本 Content 獲取或設置 HTTP 消息的內容 Method 獲取或設置 HTTP 請求信息使用的 HTTP 方法 RequestUri 獲取或設置 HTTP 請求的 Uri Headers 獲取 HTTP 請求標頭的集合 Properties 獲取 HTTP 請求的屬性集 ToString 返回表示當前對象的字符串

        該對象主要用於表示 HTTP 請求消息。對於該對象的這些屬性和方法,大部分應該都不會陌生,因為一個HTTP消息中主要包含頭部、消息內容等等,在這裡主要介紹一個屬性Properties,該屬性並不屬於任何標准的HTTP消息,當消息傳輸時,不會保留該屬性。

         (2).Properties屬性解析:

[__DynamicallyInvokable]
public IDictionary<string, object> Properties
{
    [__DynamicallyInvokable]
    get
    {
        if (this.properties == null)
        {
            this.properties = new Dictionary<string, object>();
        }
        return this.properties;
    }
}

    有以上的代碼可以很明顯的看出該屬性只有一個只讀屬性,並返回一個IDictionary<string, object>。當消息在服務器或者客戶端本地進行處理時,該屬性用於保存附加的消息信息。該屬性只是一個通用的容器,保存本地消息屬性。(與接受消息的連接相關的客戶端認證;將消息與配置路由進行匹配,得到的路由數據)

   2.HttpResponseMessage對象解析:

        (1).HttpRequestMessage主要屬性和方法概述:

名稱 說明 EnsureSuccessStatusCode 如果 HTTP 響應的 IsSuccessStatusCode 屬性為  false, 將引發異常 StatusCode 獲取或設置 HTTP 響應的狀態代碼 ReasonPhrase 獲取或設置服務器與狀態代碼通常一起發送的原因短語 RequestMessage 獲取或設置導致此響應消息的請求消息 IsSuccessStatusCode 獲取一個值,該值指示 HTTP 響應是否成功

      對於該對象的一些屬性沒有列舉,因為在HttpRequestMessage對象已經介紹,如:Version、Content、Headers等,該對象主要用於表示 HTTP 響應消息。在這裡主要介紹StatusCode屬性。

       (2).StatusCode屬性:

[__DynamicallyInvokable]
public HttpStatusCode StatusCode
{
    [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    get
    {
        return this.statusCode;
    }
    [__DynamicallyInvokable]
    set
    {
        if ((value < ((HttpStatusCode) 0)) || (value > ((HttpStatusCode) 0x3e7)))
        {
            throw new ArgumentOutOfRangeException("value");
        }
        this.CheckDisposed();
        this.statusCode = value;
    }
}

     StatusCode屬性為枚舉屬性,該屬性可讀可寫,對於狀態碼這個概念,很多人都是比較了解的,在HTTP協議中,狀態碼主要是表示在消息的請求在服務器中處理的結果,狀態有2XX,3XX,4XX,5XX等等,具體表示的意義就不再描述。

     3.HTTP模型消息標頭解析:

          在HTTP中,請求和響應消息,以及消息內容自身,都可以使用稱為標頭的額外字段,包含更多的信息。

       (1).標頭分類:

標頭名稱 描述 HTTP模型標頭容器類 User-Agent 為請求提供擴展信息,描述產生這個請求的應用程序 HttpRequestHeaders Server 為響應提供關於源服務器軟件的擴展信息 HttpResponseHeaders Content-Type 定義請求或響應有效載荷正文中,資源表示使用的媒體類型 HttpContentHeaders

       (2).HttpHeaders抽象類分析:

名稱 描述 Add 添加指定的標頭及其值到 HttpHeaders 集合中。 TryAddWithoutValidation 返回一個值,該值指示指定標頭及其值是否已添加到HttpHeaders 集合,而未驗證所提供的信息。 Clear 從 HttpHeaders 集合中移除所有標頭。 Remove 從HttpHeaders集合中移除指定的標頭。 GetValues 返回存儲在HttpHeaders 集合中所有指定標頭的標頭值。 Contains 如果指定標頭存在於 HttpHeaders 集合則返回。 ToString 返回表示當前 HttpHeaders對象的字符串。

       HttpHeaders是一個抽象類,HttpRequestHeaders、HttpResponseHeaders、HttpContentHeaders三個類繼承了該類。接下來我們來了解一下Add()方法:

[__DynamicallyInvokable]
public void Add(string name, string value)
{
    HeaderStoreItemInfo info;
    bool flag;
    this.CheckHeaderName(name);
    this.PrepareHeaderInfoForAdd(name, out info, out flag);
    this.ParseAndAddValue(name, info, value);
    if (flag && (info.ParsedValue != null))
    {
        this.AddHeaderToStore(name, info);
    }
}

       Add()方法具有兩個重載版本,該方法可以向容器添加標頭,如果要添加的標頭有標准名,在添加之前標頭值會進行驗證。Add方法還會驗證標頭是否可以有多個值。

   4.HTTP消息內容解析:

      在.NET4.5版本的HTTP模型中,HTTP消息的正文由抽象基類HttpContent表示,HttpResponseMessage和HttpRequestMessage對象都包含一個HttpContent類型的Content屬性。

     (1).HttpContent主要屬性和方法:

名稱 描述 ReadAsByteArrayAsync 以異步操作將 HTTP 內容寫入字節數組。 SerializeToStreamAsync 以異步操作將 HTTP 內容序列化到流。 CopyToAsync 以異步操作將 HTTP 內容寫入流。 LoadIntoBufferAsync 以異步操作將 HTTP 內容序列化到內存緩沖區。 CreateContentReadStreamAsync 以異步操作將 HTTP 內容寫入內存流。 TryComputeLength 確定 HTTP 內容是否具備有效的字節長度。 Headers 根據 RFC 2616 中的定義,獲取內容標頭。

     (2).CopyToAsync()方法解析:

[__DynamicallyInvokable]
public Task CopyToAsync(Stream stream, TransportContext context)
{
    Action<Task> continuation = null;
    this.CheckDisposed();
    if (stream == null)
    {
        throw new ArgumentNullException("stream");
    }
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
    try
    {
        Task task = null;
        if (this.IsBuffered)
        {
            task = Task.Factory.FromAsync<byte[], int, int>(new Func<byte[], int, int, 
AsyncCallback, object, IAsyncResult>(stream.BeginWrite), new Action<IAsyncResult>(stream.EndWrite),
       this.bufferedContent.GetBuffer(), 0, (int) this.bufferedContent.Length, null); } else { task = this.SerializeToStreamAsync(stream, context); this.CheckTaskNotNull(task); } if (continuation == null) { continuation = delegate (Task copyTask) { if (copyTask.IsFaulted) { tcs.TrySetException(GetStreamCopyException(copyTask.Exception.GetBaseException())); } else if (copyTask.IsCanceled) { tcs.TrySetCanceled(); } else { tcs.TrySetResult(null); } }; } task.ContinueWithStandard(continuation); } catch (IOException exception) { tcs.TrySetException(GetStreamCopyException(exception)); } catch (ObjectDisposedException exception2) { tcs.TrySetException(GetStreamCopyException(exception2)); } return tcs.Task; }

    在使用消息內容時,需要使用HtppContent的方法或者擴展方法。在HttpContent中利用CopyToAsync()方法以推送方式訪問原始的消息內容,由方法代碼可以看出,該方法接受兩個參數,一個是流對象,一個是有關傳輸的信息(例如,通道綁定),此參數可以為 null。該方法可以把消息內容寫入到這個流中。

    在該方法的實現代碼中 創建了一個TaskCompletionSource<object>的泛型對象,該對象表示未綁定到委托的 Task<TResult> 的制造者方,並通過 Task 屬性提供對使用者方的訪問。SerializeToStreamAsync方法將傳入的流對象序列化,該方法為異步方法。

    我們需要注意的幾點,主要為委托的創建和使用,在C#中,盡量使用有.NET提供的委托類,不要自己去創建。還有一點就是在程序中對異常的處理方式,異常的捕獲具有層次性,並且調用了自定義的一個異常處理方法TrySetException。

    (2).ReadAsStreamAsync()方法解析:

      在獲取原始消息內容時,除了調用上面介紹的方法外,還可以調用ReadAsStreamAsync()方法以拉取的方式訪問原始的消息內容。

      在HttpContent中包含有另外兩個類似的方法,ReadAsStringAsync()和ReadAsByteArrayAsync()異步的提供消息內容的緩沖副本,ReadAsByteArrayAsync()返回原始的字節內容,ReadAsStringAsync()將內容解碼為字符串返回。

三.DotNet中新舊HTTP模型分析:

   1..NET4.5之前版本創建HTTP POST請求實例:

        public static string HttpPost(string postUrl, string postData)
        {
            if (string.IsNullOrEmpty(postUrl))
                throw new ArgumentNullException(postUrl);
            if (string.IsNullOrEmpty(postData))
                throw new ArgumentNullException(postData);
            var request = WebRequest.Create(postUrl) as HttpWebRequest;
            if (request == null)
                throw new ArgumentNullException("postUrl");
            try
            {
                var cookieContainer = new CookieContainer();
                request.CookieContainer = cookieContainer;
                request.AllowAutoRedirect = true;
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                var data = Encoding.UTF8.GetBytes(postData);
                request.ContentLength = data.Length;
                var outstream = request.GetRequestStream();
                outstream.Write(data, 0, data.Length);
                outstream.Close();
                //發送請求並獲取相應回應數據,獲取對應HTTP請求的響應
                var response = request.GetResponse() as HttpWebResponse;
                if (response != null)
                {
                    var instream = response.GetResponseStream();
                    var content = string.Empty;
                    if (instream == null)
                    {
                        return content;
                    }
                    using (var sr = new StreamReader(instream, Encoding.UTF8))
                    {
                        content = sr.ReadToEnd();
                    }
                    return content;
                }
            }
            catch (ArgumentException arex)
            {
                throw arex;
            }
            catch (IOException ioex)
            {
                throw ioex;
            }
            return null;
        }

   2..NET4.5版本創建HTTP POST請求實例:

async static void getResponse(string url)
        {
            using (HttpClient client = new HttpClient())
            {
                using (HttpResponseMessage response = await client.GetAsync(url))
                {
                    using (HttpContent content = response.Content)
                    {
                        string myContent = await content.ReadAsStringAsync();
                    }
                }
            }
        }
        async static void postResponse(string url)
        {
            while (true)
            {
                IEnumerable<KeyValuePair<string, string>> queries = new List<KeyValuePair<string, string>>()
            {
                new KeyValuePair<string, string> ("test","test")
            };
                HttpContent q = new FormUrlEncodedContent(queries);
                using (HttpClient client = new HttpClient())
                {
                    using (HttpResponseMessage response = await client.PostAsync(url, q))
                    {
                        using (HttpContent content = response.Content)
                        {
                            string myContent = await content.ReadAsStringAsync();

                            Console.WriteLine(myContent);
                        }
                    }
                }
            }
        }

四.總結:

   以上主要講解了.NET4.5之前和之後版本對HTTP編程模式的一些內容, 兩者的主要區別在於.NET4.5版本之前的HTTP編程模型會區分客戶端和服務器,兩者使用的對象存在不同,實現的原理上雖然存在一定的相似性,但是使用的類卻不同。.NET4.5之後的版本中,對象的使用沒有客戶端和服務器之分,兩者可以共用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved