Asp.Net WebAPI核心對象解析(三)

   對於.NET的分佈式應用開發,能夠供咱們選擇的技術和框架比較多,例如webservice,.net remoting,MSMQ,WCF等等技術。對於這些技術不少人都不會陌生,即時沒有深刻的瞭解,可是確定據說過,每種技術都各有優點和適用範圍,沒有絕對的好壞,只有相對的合適程度。不過惋惜了,今天咱們講解的主題不是這幾種技術,今天主要講解的是ASP.NET WebAPI。html

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

一.WebAPI的HTTP概述:

   有關HTTP協議的相關內容在這裏就不作介紹,在筆者前面的博文中已經作過介紹,如今提供一下地址,由於過多的贅述就是浪費時間,我就姑且看這篇博文的讀者已經對HTTP協議和WebAPI都有所瞭解。博文地址:編程

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

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請求和響應對象的訪問。cookie

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

      (1).在客戶端和服務器端使用一樣的類。(HttpRequestMessage和HttpResponseMessage對象中不包含上下文消息,因此能夠在服務器和客戶端共用。)app

      (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。兩個對象的結構以下圖:

      以上主要講解了HttpRequestMessage對象和HttpResponseMessage對象包含的主要內容,請求和響應消息均可以包含一個可選的消息正文,兩中消息類型以及消息內容,均可以使用響應的標頭。接下來具體瞭解一些消息的結構。

    1.HttpRequestMessage對象解析:

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

名稱 說明
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以後的版本中,對象的使用沒有客戶端和服務器之分,二者能夠共用。

相關文章
相關標籤/搜索