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 文件中查寻
本文涉及的知识点
- ActionFilter
- AuthorizationFilter
- DelegateHandler
- Different Web API routing 属性
- MediaTypeFormatter
- OWIN
- Self Hosting
- Web API 文档及可扩展功能
.Net 框架
- Async/Await
- .NET reflection
- Serialization
- ASP.NET Web API/MVC Error handling
- IIS ,HTTPS 及Certificate
- 设计准则及技术
前言
自从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
Web API 数据流使用的更多相关文章
- ASP.NET Web API 应用教程(一) ——数据流使用
相信已经有很多文章来介绍ASP.Net Web API 技术,本系列文章主要介绍如何使用数据流,HTTPS,以及可扩展的Web API 方面的技术,系列文章主要有三篇内容. 主要内容如下: I 数据 ...
- Web API与文件操作
前段时间,一直有练习ASP.NET MVC与Web API交互,接下来,Insus.NET再做一些相关的练习,Web API与文件操作,如POST文件至Web API,更新或是删除等. 不管怎样,先在 ...
- OAuth2 for asp.net web api
在上篇文章中我研究了OpenId及DotNetOpenAuth的相关应用,这一篇继续研究OAuth2. https://github.com/DotNetOpenAuth http://www.cnb ...
- ASP.NET MVC和Web API中的Angular2 - 第1部分
下载源码 - 903.5 KB 内容 第1部分:Visual Studio 2017中的Angular2设置,基本CRUD应用程序,第三方模态弹出控件 第2部分:使用Angular2管道进行过滤/搜索 ...
- .NET WEB API关键过程 思维导图
背景说明 近期在去面试的过程中,被问及有关WEB API的一些特性,一时竟不知该如何回答,故根据自己已知的知识,加上网上搜索的,详细列举了一下,期望对WEB API有一个比较开阔和全面的认知. 关键要 ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序
也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式.通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序 ...
- Hello Web API系列教程——Web API与国际化
软件国际化是在软件设计和文档开发过程中,使得功能和代码设计能处理多种语言和文化习俗,在创建不同语言版本时,不需要重新设计源程序代码的软件工程方法.这在很多成熟的软件开发平台中非常常见.对于.net开发 ...
- ASP.NET Web API 跨域访问(CORS)
一.客户端用JSONP请求数据 如果你想用JSONP来获得跨域的数据,WebAPI本身是不支持javascript的callback的,它返回的JSON是这样的: {"YourSignatu ...
随机推荐
- html练习(5)
这个练习主要简单的展示了据对定位和相对定位: 在此说下html的定位: 1.static定位 这个是默认的方式.对static而言.left和right是不生效的. 2.relative定位(相对定位 ...
- exit()和_exit()和return
exit()和return的差别: 依照ANSI C,在最初调用的main()中使用return和exit()的效果同样. 但要注意这里所说的是"最初调用".假设main()在一个 ...
- ubuntu配置jdk脚本以及导致开不了机的解决方案
关于在那个文件里配置jdk脚本,有些大牛总结了四个地方,大体就是ubuntu系统启动后会默认加载的四个地方.例如:/etcenvironment,/etc/profile这两个文件处于系统层面的,还有 ...
- RSD和wlwmanifest是什么
今天小博发现header.php文件里<?php wp_head(); ?>显示有以下的两句代码,因为小博的网站打开速度实在太慢了,换空间怕麻烦,那就只有优化代码了. <link r ...
- hdu2159(二维完全背包)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2159 题意:打怪,还有最后一级,忍耐度有限m,问在杀怪数量上限为s的情况下能否获取n经验而通关,且忍耐 ...
- Android -> 怎样避免Handler引起内存泄露
很多其它内容,可訪问个人博客www.liangfeizc.com 错误代码 假设在Activiy中通过内部类(Runnable)的方式定义了一个变量runnable, final Runnable r ...
- 2014ACM/ICPC亚洲区西安站 F题 color (组合数学,容斥原理)
题目链接:传送门 题意: n个格子排成一行.我们有m种颜色.能够给这些格子涂色,保证相邻的格子的颜色不同 问,最后恰好使用了k种颜色的方案数. 分析: 看完题目描写叙述之后立刻想到了一个公式 :C(m ...
- gem5 设定checkpiont以及从checkpoint开始运行
同spec2006中间bzip2一个例子,如何设置checkpoint .以及从checkpoint继续以启动运行.这样做的目的是为了,采纳automic运行N指令,然后detailed运行M指令. ...
- hdu4714(树形dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4714 题意:给你一棵树,,其中每去掉一条边或加一条边的代价均为1,让你求出将其变成一个圆的最小代价. ...
- java设计模式_单例
public class Singleton { public static void main(String[] args) throws Exception { System.out.printl ...