<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      MVC4 WebAPI(二)——Web API工作方式

      在上篇文章中和大家一起學習了建立基本的WebAPI應用,立刻就有人想到了一些問題:
      1.客戶端和WebService之間文件傳輸
      2.客戶端或者服務端的安全控制
      要解決這些問題,要了解一下WebAPI的基本工作方式。

      (一)WebAPI中工作的Class

      在MVC中大家都知道,獲取Request和Response使用HttpRequest和HttpResponse兩個類,在WebAPI中使用兩外兩個類:
      HttpRequestMessage 和HttpResponseMessage,分別用于封裝Requset和Response。除了這兩個類之外,還有一個常見的抽象 類:HttpMessageHandler,用于過濾和加工HttpRequestMessage和HttpResponseMessage

      (二)解決第一個問題

      其 實第一個問題之所以被提出來應該是和客戶端有關,如果客戶端的請求是我們手寫提交的,比如使用HttpClient封裝的請求,則要傳遞文件之前,我們一 般會進行一次序列化,轉化為二進制數組之類的,在網絡上傳輸。這樣的話,在Controller中的Action參數里,我們只需要接收這個二進制數組類 型的對象就可以了。
      但是如果客戶端是Web Form呢,比如我們提交一個Form到指定的Controller的Action中,這個Action要接收什么類型的參數呢?
      或者我們問另外一個問題,如果我將Web Form提交到一個WebAPI的Action中 ,我要怎么去取出這個表單中的數據呢?
      其 實我們應該想到:我們的Action設置的參數之所以能夠被賦值,是因為WebAPI的架構中在調用Action時將HTTP請求中的數據解析出來分別賦 值給Action中的參數,如果真是這樣的話,我們只需要在Action中獲取到HTTP請求,然后直接獲取請求里面的數據,就能解決上面的問題。
      這 種想法是正確的,只不過,此時的HTTP請求已經不是最原始的HTTP Request,而是已經被轉化成了HttpRequestMessage,在Action中,我們可以直接調用base.Requet來得到這個 HttpRequestMessage實例,通過這個實例我們就可以隨心所欲的取出HTTP請求中想要的數據。

      2.1從RequestMessage中獲取普通表單數據

      這里的普通表單是指不包含File的表單,也就是說表單的enctype值不是multipart/form-data,這時,表單的數據默認情況下是以Json來傳遞的
      如下頁面

      <form name="form" action="~/api/FormSubmit?key=11234" method="post">
          <input type="text" name="key" id="txtKey" />
          <br />
      
          <input type="text" name="value" id="txtValue" />
          <br />
          
          <input type="submit" id="btnSubmit" value="Submit" />
           
      </form>

      捕獲到的請求為

      提交到對應的Action為:

              [HttpPost]
              public async void submitForm()
              {
                  StringBuilder sb = new StringBuilder();
                  HttpContent content = Request.Content;
                  JsonObject jsonValue = await content.ReadAsOrDefaultAsync<JsonObject>();
                  foreach (var x in jsonValue)
                  {
                      sb.Append(x.Key);
                      string va ;
                      if (x.Value.TryReadAs<string>(out va))
                      {
                          sb.Append(va);
                      }
                  }
              }

      這樣最后可以得到 Json的值:{"key":"123","value":"123"}  sb處理后的值為:key123value123

       

      :在該action中使用到了關鍵字async和await,這些在4.5中新提出的關鍵字主要是用于進行多線程取值的,在MVCAPI的設計中,大部分的方法都被設計成類似于下面的方法

      public static Task<T> ReadAsOrDefaultAsync<T>(this HttpContent content);

      返 回值是一個Task,這種返回新線程的方法雖然可以提高系統的響應能力,但是多線程取值會給編碼帶來不便,所以新出的關鍵字await用于阻塞當前線程并 獲取目標線程的返回值,在方法體中使用await關鍵字后要求將方法聲明為async用來表示該方法是異步的,并且返回值必須為void或者將返回者封裝 在一個Task中
      當然,如果你不喜歡這種寫法,上面的action也可以寫為:

                  Task readTask = content.ReadAsOrDefaultAsync<JsonObject>().ContinueWith((task) => { jsonValue = task.Result; });
                  readTask.Wait();


      2.2從RequestMessage中獲取multipart表單數據
      將view頁面改寫為

      <form name="form" action="~/api/FormSubmit?key=11234" method="post" enctype="multipart/form-data" >
          <input type="text" name="key" id="txtKey" />
          <br />
          <input type="text" name="value" id="txtValue" />
          <br />
          <input type="file" name="file" id="upFile" />
          <br />
          <input type="submit" id="btnSubmit" value="Submit" />
      </form>

      此時捕獲到得請求是


      這里的文件內容被捕獲軟件解析成字符串,當然如果我上傳的是其他的非文本格式的文件,文件會被轉化為二進制數組
      這時如果我們不更改action,而直接調用,會發生錯誤,原因很明顯,這個HTTP的報文內容是無法被轉換為JSON的,這時我們需要將表單的報文解析成另外一種格式

                      IEnumerable<HttpContent> bodyparts = await content.ReadAsMultipartAsync();
                      foreach (var bodypart in bodyparts)
                      {
                          string name;
                          name = bodypart.Headers.ContentDisposition.Name;
                          sb.Append(name + ":");
                          if (bodypart.Headers.Contains("filename"))
                          {
                              Stream stream = await bodypart.ReadAsStreamAsync();
                              StreamReader reader = new StreamReader(stream);
                              sb.Append(reader.ReadToEnd());
                              sb.Append("----");
                          }
                          else
                          {
                              string val = await bodypart.ReadAsStringAsync();
                              sb.Append(val);
                              sb.Append("----");
                          }
                      }

      得到的處理后的sb值為:

      {"key":123----"value":123----"file":******{文件的內容}*****----}
      整合后的Action為

              [HttpPost]
              public async void submitForm()
              {
                  StringBuilder sb = new StringBuilder();
                  HttpContent content = Request.Content;
                  if (content.IsMimeMultipartContent())
                  {
                      IEnumerable<HttpContent> bodyparts = await content.ReadAsMultipartAsync();
                      foreach (var bodypart in bodyparts)
                      {
                          string name;
                          name = bodypart.Headers.ContentDisposition.Name;
                          sb.Append(name + ":");
                          if (bodypart.Headers.Contains("filename"))
                          {
                              Stream stream = await bodypart.ReadAsStreamAsync();
                              StreamReader reader = new StreamReader(stream);
                              sb.Append(reader.ReadToEnd());
                              sb.Append("----");
                          }
                          else
                          {
                              string val = await bodypart.ReadAsStringAsync();
                              sb.Append(val);
                              sb.Append("----");
                          }
                      }
                  }
                  else
                  {
                      JsonObject jsonValue = await content.ReadAsOrDefaultAsync<JsonObject>();
                      foreach (var x in jsonValue)
                      {
                          sb.Append(x.Key);
                          string va;
                          if (x.Value.TryReadAs<string>(out va))
                          {
                              sb.Append(va);
                          }
                      }
                  }
              }

       

      (三)WebAPI工作方式

      要想解決第二個問題就沒這么容易了,我們需要更深入的理解WebAPI的工作方式。
      其實對于WebAPI來說,它最初被設計為和WCF一樣的:客戶端、服務端兩套結構,我們到現在之所以還沒有提到客戶端,是因為我們的請求別的方式來封裝成HTTP請求或接收HTTP相應的,比如AJAX和Form表單提交。

      在這里先給出一個服務端的響應工作流,讓大家有個大體上的認識

      (不得已在圖片中加了水印,因為看到自己辛苦寫的東西被人直接拿走,也不給出原文鏈接,心里真的不好受..希望不會影響大家的閱讀...)
      由于圖片大小限制,所有的HttpRequestMessage被簡寫為HttpRequestMsg,HttpResponseMessage被簡寫了HttpResponseMsg

      大 家可以看到,HTTP的請求最先是被傳遞到HOST中的,如果WebAPI是被寄宿在IIS上的,這個HOST就是IIS上,HOST是沒有能力也沒有必 要進行請求的處理的,請求通過HOST被轉發給了HttPServer此時已經進入WebAPI的處理加工范圍,HttpServer是 System.Net.HTTP中的一個類,通過HttpServer,請求被封裝成了WebAPI中的請求承載 類:HttpRequestMessage,這個封裝后的請求可以經過一系列自定義的Handler來處理,這些handler串聯成一個 pipeline,最后請求會被傳遞給HttpControlDispather,這個類通過對路由表的檢索來確定請求將被轉發到的具體的 Controller中的Action。

      Client端的處理與服務端類似,直接上圖:

      其實根據微軟的說法,他們本身就被設計成類似但是可以獨立運行的結構

      ASP.NET Web API has a pipeline for processing HTTP messages on both the client and server. The client and server sides are designed to be symmetrical but independent; you can use each half by itself. Both sides are built on some common objects:

      • HttpRequestMessage represents the HTTP request.
      • HttpResponseMessage represents the HTTP response.
      • HttpMessageHandler objects process the request and response.

      直接看圖,在客戶端,Handlers pipeline最終是被傳遞到HttpClientHandler上的,由他負責HttpRequestMessage到HTTP請求的轉換。

      這里只說明一下Request,Response與其類似。

      (四)解決第二個問題

      由 此我們早就可以看出,想要解決第二個問題,可以直接在Handler PipeLine中進行,這種AOP風格的過濾器(攔截器)在REST的Webservice的安全驗證中應用很廣,一般大家比較樂于在HTTP頭或者在 HTTP請求的URL中加上身份驗證字段進行身份驗證,下面舉一個在Http頭中添加身份驗證信息的小例子

      4.1客戶端
      客戶端的customhandler用于將身份驗證信息添加入報頭

          class RequestUpHandler : DelegatingHandler
          {
              protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
              {
                  request.Headers.Add("key", "11234");
                  return base.SendAsync(request, cancellationToken);
              }
          }

      注:
      1.customhandler繼承自DelegatingHandler類,上面已經說過,WebAPI的客戶端和服務端被設計為相互對應的兩套結構,所以不論是在客戶端還是服務端,customhandler都是繼承自DelegatingHandler類
      2.DelegatingHandler的sendAsync方法便是處理請求和接受請求時會被調用的方法,該方法返回值是HttPResponseMessage,接收的值為HttpRequestMessage,符合我們的一般認知
      3.方法的最后,調用base.SendAsync是將Request繼續向該pipeline的其他customHandler傳遞,并獲取其返回值。由于該方法不包含Response的處理邏輯,只需直接將上一個CustomHandler的
      返回值直接返回
      客戶端主程序

              static void Main(string[] args)
              {
                  HttpClient client = new HttpClient(new RequestUpHandler() { InnerHandler = new HttpClientHandler() });
                  HttpResponseMessage response = client.GetAsync("http://localhost:60023/api/FormSubmit").Result;
                  response.Content.ReadAsAsync<string>().ContinueWith((str) => { Console.WriteLine(str.Result); });
                  Console.Read();
              }

      客 戶端的主程序創建了一個HttpClient,HttpClient可以接受一個參數,該參數就是CustomHandler,此處我們嵌入了我們定義的 RequestUpHandler,用于對Request報頭進行嵌入身份驗證碼的處理,CustomHandler通過InnerHandler屬性嵌 入其內置的下一個CustomHandler,此處,由于沒有下一個CustomerHandler,我們直接嵌入HttpClientHandler用 于將HttpRequestMessage轉化為HTTP 請求、將HTTP響應轉化為HttpResponseMessage

      4.2服務端
      服務端的customHandler用于解析HTTP報頭中的身份認證碼

              protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
              {
                  int matchHeaderCount = request.Headers.Count((item) =>
                  {
                      if ("key".Equals(item.Key))
                      {
                          foreach (var str in item.Value)
                          {
                              if ("11234".Equals(str))
                              {
                                  return true;
                              }
                          }
                      }
                      return false;
                  });
                  if (matchHeaderCount>0)
                  {
                      return base.SendAsync(request, cancellationToken);
                  }
                  return Task.Factory.StartNew<HttpResponseMessage>(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden); });
              }

      注:代碼的處理邏輯很簡單:如果身份驗證碼匹配成功,則通過base.SendAsync繼續將請求向下傳遞,否則返回直接中斷請求的傳遞,直接返回一個響應碼為403的響應,指示沒有權限。
      注意由于SendAsync的返回值需要封裝在Task之中,所以需要使用Task.Factory.StartNew將返回值包含在Task中

      將customHandler注入到HOST中
      本例中WebAPI HOST在IIS上,所以我們只需將我們定義的CustomHandler在Application_Start中定義即可

              protected void Application_Start()
              {
                  //省略其他邏輯代碼
      
                  GlobalConfiguration.Configuration.MessageHandlers.Add(new HttpUrlHandler());
              }

      由于WebAPI Host在IIS上,所以HttpServer和HttpControllerDispatcher不用我們手工處理

      在加上上面的處理后,如果沒有身份驗證碼的請求,會得到如下的響應

       

       ******************************************************************************
      作者:王坤
      出處:http://www.rzrgm.cn/wk1234
      本文版權歸 王坤和博客園共有,歡迎轉載,但請注明出處。

       

       

       

      posted @ 2012-05-07 09:02  wangking1029  閱讀(87009)  評論(21)    收藏  舉報
      主站蜘蛛池模板: 精品一区二区不卡无码AV| 国产精品一区二区三区激情| 强插少妇视频一区二区三区| 日本在线视频网站www色下载| 国产高清自产拍av在线| 精品人妻系列无码天堂| 亚洲精品久久久久久婷婷| 一本一道av无码中文字幕﹣百度| 好硬好湿好爽再深一点动态图视频| 亚洲最大成人av免费看| 韩国无码av片在线观看| 国产成人AV男人的天堂| 精品视频一区二区三区不卡| 久久99精品久久久大学生 | 狠狠色婷婷久久综合频道日韩| 亚洲中文字幕久久精品品| 日韩精品一区二区蜜臀av| 国产又黄又爽又不遮挡视频 | 国产精品日日摸夜夜添夜夜添无码 | 国产欧美日韩亚洲一区二区三区| 在线欧美精品一区二区三区| 久久久久成人精品无码中文字幕| 无码专区 人妻系列 在线| 亚洲av无码专区在线亚| 亚洲国产码专区在线观看| 97成人碰碰久久人人超级碰oo| 毛茸茸性xxxx毛茸茸毛茸茸| 人妻少妇无码精品视频区| 人妻少妇精品中文字幕| 艳妇臀荡乳欲伦69调教视频| 成人午夜av在线播放| 人妻中出无码中字在线| 无码免费大香伊蕉在人线国产| 亚洲大尺度无码无码专线| 插插无码视频大全不卡网站| 久久久久久综合网天天| 国产熟睡乱子伦午夜视频| 四虎亚洲国产成人久久精品| 最新精品露脸国产在线| 无码熟妇人妻av影音先锋| 久久天天躁狠狠躁夜夜躁|