ASP.NET Web API 應用教程(一) ——資料流使用

weixin_34391854發表於2015-12-08

 

相信已經有很多文章來介紹ASP.Net Web API 技術,本系列文章主要介紹如何使用資料流,HTTPS,以及可擴充套件的Web API 方面的技術,系列文章主要有三篇內容。

主要內容如下:

I  資料流

II 使用HTTPS

III 可擴充套件的Web API 文件

 

專案環境要求

  • VS 2012(SP4)及以上,
  • .Net 框架4.5.1
  • Nuget包,可在packages.config 檔案中查尋

本文涉及的知識點

  1. ActionFilter
  2. AuthorizationFilter
  3. DelegateHandler
  4. Different Web API routing 屬性
  5. MediaTypeFormatter
  6. OWIN
  7. Self Hosting
  8. Web API 文件及可擴充套件功能

.Net 框架

  1. Async/Await
  2. .NET reflection
  3. Serialization
  4. ASP.NET Web API/MVC Error handling
  5. IIS ,HTTPS 及Certificate
  6. 設計準則及技術

前言

 

自從ASP.NET MVC 4之後.Net 框架開始支援ASP.NET Web API ,ASP.NET Web API 基於HTTP 協議建立的,是構建 RESTful 服務和處理資料的理想平臺,旨在使用HTTP 技術實現對多平臺的支援。

ASP.NET Web API 以request-response 的訊息轉換模式為主,客戶端向伺服器傳送請求,伺服器端響應客戶端請求。響應可同步或非同步。

 個人認為使用Web API建立應用需要注意的三個關鍵點:

  • 採用服務及方法滿足的目標
  • 每個方法的輸入,如請求
  • 每個方法的輸出,如響應

通常情況下,Asp.Net Web API 定義method語法與HTTP方法一一對應的,如自定義方法名 GetPysicians(),則與HTTP中Get 方法匹配。下圖是常用匹配表。

 


但是此方法在很多情況下,並不實用,假如你想在單個API controller 類中定義多個Get 或Post 方法,在這種情況下,需要定義包含action 的路徑,將Action 作為URI 的一部分。以下是配置程式碼:

   1:  public static void Register(HttpConfiguration config)
   2:  {
   3:      // Web API configuration and services
   4:      // Web API routes
   5:       config.MapHttpAttributeRoutes();
   6:      
   7:       config.Routes.MapHttpRoute(name: "PhysicianApi",
   8:                  routeTemplate: "{controller}/{action}/{id}",
   9:                  defaults: new { id = RouteParameter.Optional });
  10:  }

但是此方法不足以應對所有情況,如果想實現從中央倉庫刪除檔案,並且想呼叫同一個方法來獲取檔案,這種情況下,Web API 框架需要偽裝Get 及Delete對應的HTTP 方法屬性。如圖所示:

RemoveFile 方法可被Delete(HttpDelete) 或 Get(HttpGet)方法同時呼叫,從某種程度來說,HTTP 方法使開發人員命名 API“方法”變得簡單而標準。

Web API框架也提供了一些其他功能來處理路徑方面的問題,與MVC 的路徑處理方法相似。因此可定義不同型別的Action方法。 

資料流

網路App 最常見的執行操作就是獲取資料流。ASP.NET Web API 能夠處理客戶端與伺服器端傳輸的重量級的資料流,資料流可來源於目錄檔案,也可是資料庫中的二進位制檔案。本文主要介紹兩種方法“Download”和“Upload”實現資料流相關的功能,Download是從伺服器下載資料操作,而Upload則是上傳資料到伺服器。

相關專案

  • WebAPIDataStreaming
  • WebAPIClient
  • POCOLibrary

在對程式碼解釋之前,首先來了解如何配置IIS(7.5)和Web API 服務Web.Config 檔案。

1. 保證Downloads/Uploads 涉及的檔案具有讀寫許可權。

2. 保證有足夠容量的內容或因公安空間處理大檔案。

3. 如果檔案較大

a. 配置Web.Config 檔案時,保證 maxRequestLength 時響應時間 executionTimeout 合理。具體的值主要依賴於資料大小,允許一次性上傳的最大資料為2 GB

b. 保證 maxAllowedContentLength 在requestFiltering部分配置下正確設定,預設值為30MB,最大值4GB

一旦完成預先配置,那麼建立資料流服務就非常簡單了,首先 需要定義檔案流“ApiController”,如下:

   1:  /// <summary>
   2:  /// File streaming API
   3:  /// </summary>
   4:  [RoutePrefix("filestreaming")]
   5:  [RequestModelValidator]
   6:  public class StreamFilesController : ApiController
   7:  {
   8:      /// <summary>
   9:      /// Get File meta data
  10:      /// </summary>
  11:      /// <param name="fileName">FileName value</param>
  12:      /// <returns>FileMeta data response.</returns>
  13:      [Route("getfilemetadata")]
  14:      public HttpResponseMessage GetFileMetaData(string fileName)
  15:      {
  16:          // .........................................
  17:          // Full code available in the source control
  18:          // .........................................
  19:   
  20:      }
  21:   
  22:      /// <summary>
  23:      /// Search file and return its meta data in all download directories
  24:      /// </summary>
  25:      /// <param name="fileName">FileName value</param>
  26:      /// <returns>List of file meta datas response</returns>
  27:      [HttpGet]
  28:      [Route("searchfileindownloaddirectory")]
  29:      public HttpResponseMessage SearchFileInDownloadDirectory(string fileName)
  30:      {
  31:          // .........................................
  32:          // Full code available in the source control
  33:          // .........................................
  34:      }
  35:   
  36:      /// <summary>
  37:      /// Asynchronous Download file
  38:      /// </summary>
  39:      /// <param name="fileName">FileName value</param>
  40:      /// <returns>Tasked File stream response</returns>
  41:      [Route("downloadasync")]
  42:      [HttpGet]
  43:      public async Task<HttpResponseMessage> DownloadFileAsync(string fileName)
  44:      {
  45:          // .........................................
  46:          // Full code available in the source control
  47:          // .........................................
  48:      }
  49:   
  50:      /// <summary>
  51:      /// Download file
  52:      /// </summary>
  53:      /// <param name="fileName">FileName value</param>
  54:      /// <returns>File stream response</returns>
  55:      [Route("download")]
  56:      [HttpGet]
  57:      public HttpResponseMessage DownloadFile(string fileName)
  58:      {
  59:          // .........................................
  60:          // Full code available in the source control
  61:          // .........................................
  62:      }
  63:   
  64:      /// <summary>
  65:      /// Upload file(s)
  66:      /// </summary>
  67:      /// <param name="overWrite">An indicator to overwrite a file if it exist in the server</param>
  68:      /// <returns>Message response</returns>
  69:      [Route("upload")]
  70:      [HttpPost]
  71:      public HttpResponseMessage UploadFile(bool overWrite)
  72:      {
  73:          // .........................................
  74:          // Full code available in the source control
  75:          // .........................................
  76:      }
  77:   
  78:      /// <summary>
  79:      /// Asynchronous Upload file
  80:      /// </summary>
  81:      /// <param name="overWrite">An indicator to overwrite a file if it exist in the server</param>
  82:      /// <returns>Tasked Message response</returns>
  83:      [Route("uploadasync")]
  84:      [HttpPost]
  85:      public async Task<HttpResponseMessage> UploadFileAsync(bool overWrite)
  86:      {
  87:          // .........................................
  88:          // Full code available in the source control
  89:          // .........................................
  90:      }
  91:  }

Download 服務方法首先需要確認請求的檔案是否存在,如果未找到,則返回錯誤提示“file is not found”,如果找到此檔案,內容則轉換為位元組附加到響應物件,為“application/octet-stream” MIMI 內容型別。

   1:  /// <summary>
   2:  /// Download file
   3:  /// </summary>
   4:  /// <param name="fileName">FileName value<param>
   5:  /// <returns>File stream response<returns>
   6:  [Route("download")]
   7:  [HttpGet]
   8:  public HttpResponseMessage DownloadFile(string fileName)
   9:  {
  10:      HttpResponseMessage response = Request.CreateResponse();
  11:      FileMetaData metaData = new FileMetaData();
  12:      try
  13:      {
  14:          string filePath = Path.Combine(this.GetDownloadPath(), @"\", fileName);
  15:          FileInfo fileInfo = new FileInfo(filePath);
  16:   
  17:          if (!fileInfo.Exists)
  18:          {
  19:              metaData.FileResponseMessage.IsExists = false;
  20:              metaData.FileResponseMessage.Content = string.Format("{0} file is not found !", fileName);
  21:              response = Request.CreateResponse(HttpStatusCode.NotFound, metaData, new MediaTypeHeaderValue("text/json"));
  22:          }
  23:          else
  24:          {
  25:              response.Headers.AcceptRanges.Add("bytes");
  26:              response.StatusCode = HttpStatusCode.OK;
  27:              response.Content = new StreamContent(fileInfo.ReadStream());
  28:              response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
  29:              response.Content.Headers.ContentDisposition.FileName = fileName;
  30:              response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
  31:              response.Content.Headers.ContentLength = fileInfo.Length;
  32:          }
  33:      }
  34:      catch (Exception exception)
  35:      {
  36:          // Log exception and return gracefully
  37:          metaData = new FileMetaData();
  38:          metaData.FileResponseMessage.Content = ProcessException(exception);
  39:          response = Request.CreateResponse(HttpStatusCode.InternalServerError, metaData, new MediaTypeHeaderValue("text/json"));
  40:      }
  41:      return response;
  42:  }

Upload服務方法則會在multipart/form-data MIMI 內容型別執行,首先會檢測HTTP 請求的內容型別是否是多主體,如果是,則對比內容長度是否超過最大尺寸,如果沒有超過,則開始上傳內容,當操作完成之後,則提示相應的資訊。

程式碼片段如下:

   1:  /// <summary>
   2:  /// Upload file(s)
   3:  /// </summary>
   4:  /// <param name="overWrite">An indicator to overwrite a file if it exist in the server.</param>
   5:  /// <returns>Message response</returns>
   6:  [Route("upload")]
   7:  [HttpPost]
   8:  public HttpResponseMessage UploadFile(bool overWrite)
   9:  {
  10:      HttpResponseMessage response = Request.CreateResponse();
  11:      List<FileResponseMessage> fileResponseMessages = new List<FileResponseMessage>();
  12:      FileResponseMessage fileResponseMessage = new FileResponseMessage { IsExists = false };
  13:   
  14:      try
  15:      {
  16:          if (!Request.Content.IsMimeMultipartContent())
  17:          {
  18:              fileResponseMessage.Content = "Upload data request is not valid !";
  19:              fileResponseMessages.Add(fileResponseMessage);
  20:              response = Request.CreateResponse(HttpStatusCode.UnsupportedMediaType, fileResponseMessages, new MediaTypeHeaderValue("text/json"));
  21:          }
  22:   
  23:          else
  24:          {
  25:              response = ProcessUploadRequest(overWrite);
  26:          }
  27:      }
  28:      catch (Exception exception)
  29:      {
  30:          // Log exception and return gracefully
  31:          fileResponseMessage = new FileResponseMessage { IsExists = false };
  32:          fileResponseMessage.Content = ProcessException(exception);
  33:          fileResponseMessages.Add(fileResponseMessage);
  34:          response = Request.CreateResponse(HttpStatusCode.InternalServerError, fileResponseMessages, new MediaTypeHeaderValue("text/json"));
  35:   
  36:      }
  37:      return response;
  38:  }
  39:   
  40:  /// <summary>
  41:  /// Asynchronous Upload file
  42:  /// </summary>
  43:  /// <param name="overWrite">An indicator to overwrite a file if it exist in the server.<param>
  44:  /// <returns>Tasked Message response</returns>
  45:  [Route("uploadasync")]
  46:  [HttpPost]
  47:  public async Task<HttpResponseMessage> UploadFileAsync(bool overWrite)
  48:  {
  49:      return await new TaskFactory().StartNew(
  50:         () =>
  51:         {
  52:             return UploadFile(overWrite);
  53:         });
  54:  }
  55:   
  56:  /// <summary>
  57:  /// Process upload request in the server
  58:  /// </summary> 
  59:  /// <param name="overWrite">An indicator to overwrite a file if it exist in the server.</param>
  60:  /// </returns>List of message object</returns>
  61:  private HttpResponseMessage ProcessUploadRequest(bool overWrite)
  62:  {
  63:      // .........................................
  64:      // Full code available in the source control
  65:      // .........................................
  66:  }

呼叫download 及 upload 檔案方法是控制檯應用,App 假定檔案流服務通過HttpClient和相關類。基本下載檔案程式碼,建立下載HTTP 請求物件。

   1:  /// <summary>
   2:  /// Download file
   3:  /// </summary>
   4:  /// <returns>Awaitable Task object</returns>
   5:  private static async Task DownloadFile()
   6:  {
   7:      Console.ForegroundColor = ConsoleColor.Green;
   8:      Console.WriteLine("Please specify file name  with extension and Press Enter :- ");
   9:      string fileName = Console.ReadLine();
  10:      string localDownloadPath = string.Concat(@"c:\", fileName); // the path can be configurable
  11:      bool overWrite = true;
  12:      string actionURL = string.Concat("downloadasync?fileName=", fileName);
  13:   
  14:      try
  15:      {
  16:          Console.WriteLine(string.Format("Start downloading @ {0}, {1} time ",
  17:              DateTime.Now.ToLongDateString(),
  18:              DateTime.Now.ToLongTimeString()));
  19:   
  20:   
  21:          using (HttpClient httpClient = new HttpClient())
  22:          {
  23:              httpClient.BaseAddress = baseStreamingURL;
  24:              HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, actionURL);
  25:   
  26:              await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).
  27:                  ContinueWith((response)
  28:                      =>
  29:                  {
  30:                      Console.WriteLine();
  31:                      try
  32:                      {
  33:                          ProcessDownloadResponse(localDownloadPath, overWrite, response);
  34:                      }
  35:                      catch (AggregateException aggregateException)
  36:                      {
  37:                          Console.ForegroundColor = ConsoleColor.Red;
  38:                          Console.WriteLine(string.Format("Exception : ", aggregateException));
  39:                      }
  40:                  });
  41:          }
  42:      }
  43:      catch (Exception ex)
  44:      {
  45:          Console.ForegroundColor = ConsoleColor.Red;
  46:          Console.WriteLine(ex.Message);
  47:      }
  48:  }
  49:   
  50:   
  51:  /// <summary>
  52:  /// Process download response object
  53:  /// </summary>
  54:  /// <param name="localDownloadFilePath">Local download file path</param>
  55:  /// <param name="overWrite">An indicator to overwrite a file if it exist in the client.</param>
  56:  /// <param name="response">Awaitable HttpResponseMessage task value</param>
  57:  private static void ProcessDownloadResponse(string localDownloadFilePath, bool overWrite,
  58:      Task<HttpResponseMessage> response)
  59:  {
  60:      if (response.Result.IsSuccessStatusCode)
  61:      {
  62:          response.Result.Content.DownloadFile(localDownloadFilePath, overWrite).
  63:              ContinueWith((downloadmessage)
  64:                  =>
  65:              {
  66:                  Console.ForegroundColor = ConsoleColor.Green;
  67:                  Console.WriteLine(downloadmessage.TryResult());
  68:              });
  69:      }
  70:      else
  71:      {
  72:          ProcessFailResponse(response);
  73:      }
  74:  }

 

注意上述程式碼中HttpClient 物件傳送請求,並等待響應傳送Header內容(HttpCompletionOption.ResponseHeadersRead )。而不是傳送全部的響應內容檔案。一旦Response header 被讀,則執行驗證,一旦驗證成功,則執行下載方法。

以下程式碼呼叫upload 檔案流,與下載方法類似,建立多主體表單資料,併傳送給伺服器端。

   1:  /// <summary>
   2:  /// Upload file
   3:  /// </summary>
   4:  /// <returns>Awaitable task object</returns>
   5:  private static async Task UploadFile()
   6:  {
   7:      try
   8:      {
   9:          string uploadRequestURI = "uploadasync?overWrite=true";
  10:   
  11:          MultipartFormDataContent formDataContent = new MultipartFormDataContent();
  12:   
  13:          // Validate the file and add to MultipartFormDataContent object
  14:          formDataContent.AddUploadFile(@"c:\nophoto.png");
  15:          formDataContent.AddUploadFile(@"c:\ReadMe.txt");
  16:   
  17:          if (!formDataContent.HasContent()) // No files found to be uploaded
  18:          {
  19:              Console.ForegroundColor = ConsoleColor.Red;
  20:              Console.Write(formDataContent.GetUploadFileErrorMesage());
  21:              return;
  22:          }
  23:          else
  24:          {
  25:              string uploadErrorMessage = formDataContent.GetUploadFileErrorMesage();
  26:              if (!string.IsNullOrWhiteSpace(uploadErrorMessage)) // Some files couldn't be found
  27:              {
  28:                  Console.ForegroundColor = ConsoleColor.Red;
  29:                  Console.Write(uploadErrorMessage);
  30:              }
  31:   
  32:              HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uploadRequestURI);
  33:              request.Content = formDataContent;
  34:   
  35:              using (HttpClient httpClient = new HttpClient())
  36:              {
  37:                  Console.ForegroundColor = ConsoleColor.Green;
  38:                  Console.WriteLine(string.Format("Start uploading @ {0}, {1} time ",
  39:                  DateTime.Now.ToLongDateString(),
  40:                  DateTime.Now.ToLongTimeString()));
  41:   
  42:                  httpClient.BaseAddress = baseStreamingURL;
  43:                  await httpClient.SendAsync(request).
  44:                        ContinueWith((response)
  45:                            =>
  46:                            {
  47:                                try
  48:                                {
  49:                                    ProcessUploadResponse(response);
  50:                                }
  51:                                catch (AggregateException aggregateException)
  52:                                {
  53:                                    Console.ForegroundColor = ConsoleColor.Red;
  54:                                    Console.WriteLine(string.Format("Exception : ", aggregateException));
  55:                                }
  56:                            });
  57:              }
  58:          }
  59:      }
  60:      catch (Exception ex)
  61:      {
  62:          Console.ForegroundColor = ConsoleColor.Red;
  63:          Console.WriteLine(ex.Message);
  64:      }
  65:  } 
  66:   
  67:  /// <summary>
  68:  /// Process download response object
  69:  /// </summary>
  70:  /// <param name="response">Awaitable HttpResponseMessage task value</param>
  71:  private static void ProcessUploadResponse(Task<HttpResponseMessage> response)
  72:  {
  73:      if (response.Result.IsSuccessStatusCode)
  74:      {
  75:          string uploadMessage = string.Format("\nUpload completed @ {0}, {1} time ",
  76:                      DateTime.Now.ToLongDateString(),
  77:                      DateTime.Now.ToLongTimeString());
  78:          Console.ForegroundColor = ConsoleColor.Green;
  79:          Console.WriteLine(string.Format("{0}\nUpload Message : \n{1}", uploadMessage,
  80:              JsonConvert.SerializeObject(response.Result.Content.ReadAsAsync<List<FileResponseMessage>>().TryResult(), Formatting.Indented)));
  81:      }
  82:      else
  83:      {
  84:          ProcessFailResponse(response);
  85:      }
  86:  }

 

資料流專案由可擴充套件類和方法組成,本文就不再詳述。下篇文章中將介紹“使用HTTPS 開發專案”

資料流是資料傳輸中的重要部分,學習了本節內容有助於大家更好地進行ASP.NET的開發。當然,還可以藉助一些開發工具來助力開發過程。ComponentOne Studio for ASP.NET 提供了一整套完備的開發工具包,用於在各種瀏覽器中建立和設計具有現代風格的Web應用程式。

 

下載原始碼

原文連結:http://www.codeproject.com/Articles/838274/Web-API-Thoughts-of-Data-Streaming#Hist

 

相關文章