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

77rou發表於2015-12-09

 

相信已經有很多文章來介紹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 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 開發專案”

下載原始碼

原文連結:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28298702/viewspace-1869010/,如需轉載,請註明出處,否則將追究法律責任。

相關文章