ASP.NET Web API 应用教程(一) ——数据流使用

简介:

 

相信已经有很多文章来介绍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 开发项目”

下载源代码

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

 

相信已经有很多文章来介绍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: }

本文转自ITPUB博客77rou的博客,原文链接:ASP.NET Web API 应用教程(一) ——数据流使用,如需转载请自行联系原博主。

相关文章
|
开发框架 前端开发 JavaScript
ASP.NET Web Pages - 教程
ASP.NET Web Pages 是一种用于创建动态网页的开发模式,采用HTML、CSS、JavaScript 和服务器脚本。本教程聚焦于Web Pages,介绍如何使用Razor语法结合服务器端代码与前端技术,以及利用WebMatrix工具进行开发。适合初学者入门ASP.NET。
|
10月前
|
JSON 编解码 API
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
|
10月前
|
自然语言处理 算法 API
阿里云增值税发票识别NET Rest API调用示例
本文介绍了使用NET代码调用阿里云增值税发票识别API的实现方式。通过示例代码,详细展示了如何构造请求、设置签名以及发送HTTP请求的具体步骤。代码中涵盖了请求参数的处理、签名生成逻辑(如HMAC-SHA256算法)以及调用API后的结果处理。此外,还提供了运行结果的截图和参考文档链接,帮助开发者更好地理解和应用该接口。
1288 4
|
C# Android开发 iOS开发
2025年全面的.NET跨平台应用框架推荐
2025年全面的.NET跨平台应用框架推荐
732 23
|
开发框架 数据可视化 .NET
.NET 中管理 Web API 文档的两种方式
.NET 中管理 Web API 文档的两种方式
317 14
|
开发框架 .NET 程序员
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
Autofac 是一个轻量级的依赖注入框架,专门为 .NET 应用程序量身定做,它就像是你代码中的 "魔法师",用它来管理对象的生命周期,让你的代码更加模块化、易于测试和维护
663 4
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
362 5
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
611 2
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
407 9
|
存储 开发框架 .NET
.NET 8 实现无实体库表 API 部署服务
【10月更文挑战第12天】在.NET 8中,可通过以下步骤实现无实体库表的API部署:首先安装.NET 8 SDK及开发工具,并选用轻量级Web API框架如ASP.NET Core;接着创建新项目并设计API,利用内存数据结构模拟数据存储;最后配置项目设置并进行测试与部署。此方法适用于小型项目或临时解决方案,但对于大规模应用仍需考虑持久化存储以确保数据可靠性与可扩展性。
215 3