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

ASP.NET Web API 应用教程(一) ——数据流使用的更多相关文章

  1. ASP.NET Web API系列教程目录

    ASP.NET Web API系列教程目录 Introduction:What's This New Web API?引子:新的Web API是什么? Chapter 1: Getting Start ...

  2. ASP.NET Web API系列教程(目录)(转)

    注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP.NET Web API.这是一个用来在.NET平台上建立HTTP服务的Web API框架,是微软的又一项令人振奋的技术.目前,国内 ...

  3. [转]ASP.NET Web API系列教程(目录)

    本文转自:http://www.cnblogs.com/r01cn/archive/2012/11/11/2765432.html 注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP ...

  4. 【转】ASP.NET WEB API系列教程

    from: 西瓜小强 http://www.cnblogs.com/risk/category/406988.html ASP.NET Web API教程(六) 安全与身份认证 摘要: 在实际的项目应 ...

  5. 【ASP.NET Web API教程】6.1 媒体格式化器

    http://www.cnblogs.com/r01cn/archive/2013/05/17/3083400.html 6.1 Media Formatters6.1 媒体格式化器 本文引自:htt ...

  6. 【ASP.NET Web API教程】3.2 通过.NET客户端调用Web API(C#)

    原文:[ASP.NET Web API教程]3.2 通过.NET客户端调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

  7. 【ASP.NET Web API教程】2.4 创建Web API的帮助页面

    原文:[ASP.NET Web API教程]2.4 创建Web API的帮助页面 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. 2.4 ...

  8. 【ASP.NET Web API教程】2.3.7 创建首页

    原文:[ASP.NET Web API教程]2.3.7 创建首页 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. Part 7: Crea ...

  9. 【ASP.NET Web API教程】2.3.6 创建产品和订单控制器

    原文:[ASP.NET Web API教程]2.3.6 创建产品和订单控制器 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的内容. Part 6 ...

随机推荐

  1. 网站内容禁止复制和粘贴、另存为的js代码(转)

    1.使右键和复制失效 方法1: 在网页中加入以下代码: 代码如下: <script language="Javascript"> document.oncontextm ...

  2. c# 筛选进程命令行,得其ProcessId(唯一标示符,简称pid),再通过pid结束进程

    不说别的,上代码 部分using: using System.Diagnostics; using System.Management; 其中要引用System.Management 1.通过筛选Co ...

  3. Ubuntu android 开发配置

    1. 安装 Java SDK 1.1下载  java-sun-1.7.1.tar.gz 到Downlowd 目录下 1.2 创建java目录 sudo makrdir  /usr/java 1.3解压 ...

  4. Nessus导入Cookie进行Web应用安全扫描

    在不导入Cookie使用Nessus进行扫描的时候,扫描的结果是比较简单的,很多深层的问题无法被扫描出来. 需要我们手动导入Cookie,带着Cookie的状态扫描的结果会更详细更深入,以下是操作步骤 ...

  5. Win7 64位 VS2013环境cuda_7.5.18的一些坑

    thrust库的sort算法,在x86平台使用就崩溃,x64就没问题,搜了下好像是很早的版本,4开始就有这样的问题了,原因不明. http://stackoverflow.com/questions/ ...

  6. Python生成器的经典程序

    import random def get_data(): """返回0到9之间的3个随机数""" return random.sample ...

  7. Oracle 服务手动启动关闭

    在windows7中安装完Oracle11g之后会出现一下七种服务:可通过运行->services.msc查看. 其中各个服务名称中的ORCL或orcl为SID即System IDentifie ...

  8. <Oracle Database>数据库启动与关闭

    启动和关闭Oracle数据库 要启动和关闭数据库,必须要以具有Oracle 管理员权限的用户登陆,通常也就是以具有SYSDBA权限的用户登陆.一般我们常用INTERNAL用户来启动和关闭数据库(INT ...

  9. wpf 报错: 在 AddNew 或 EditItem 事务过程中不允许“DeferRefresh”。

    今天修改Bug的时候遇到一个问题: datagrid 设置了双击事件,双击弹出一个窗口,在多次点击后报错:在 AddNew 或 EditItem 事务过程中不允许“DeferRefresh” 网上查了 ...

  10. Python成长笔记 - 基础篇 (十一)----RabbitMQ、Redis 、线程queue

    本节内容: 1.RabbitMQ 消息队列 2.Redis 3.Mysql   PY 中的线程queue(threading Queue):用于多个线程之间进行数据交换,不能在进程间进行通信 进程qu ...