zoukankan      html  css  js  c++  java
  • Web API 数据流使用

    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:
    ophoto.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("
    Upload completed @ {0}, {1} time ",
      76:                      DateTime.Now.ToLongDateString(),
      77:                      DateTime.Now.ToLongTimeString());
      78:          Console.ForegroundColor = ConsoleColor.Green;
      79:          Console.WriteLine(string.Format("{0}
    Upload Message : 
    {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

  • 相关阅读:
    二分查找法
    AES算法工具类
    SHA加密算法工具类
    使用SQL创建唯一索引
    springboot 启动类CommandLineRunner(转载)
    MD5加密算法工具类
    Android 通过Socket 和服务器通讯
    android 网络连接判断
    android 文件上传,中文utf-8编码
    github打不开问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5036201.html
Copyright © 2011-2022 走看看