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 ...
随机推荐
- ZOJ 1610 间隔染色段树
要长8000仪表板.间染色的范围,问:最后,能看到的颜色,而且颜色一共有段出现 覆盖段 数据对比水 水可太暴力 段树: #include "stdio.h" #include ...
- Java流的理解
最近做了一下Socket编程,其中有socket.getInputStream和socket.getOutputStream的问题. 想传输文件,感觉应该用FileInputStream和FileOu ...
- A Game of Thrones(1) - Bran
The morning had dawned clear and cold, with a crispness(易碎:清新) that hinted(暗示:示意) at the end of summ ...
- css 背景色渐变
background:rgba(0, 0, 0, 0) linear-gradient(to bottom, #eff5ff 0px, #e0ecff 20%) repeat-x scroll 0 0 ...
- vim ctl+v批量添加/删除
vim编辑器---批量注释与反注释 在使用vim编写代码的时候,经常需要用到批量注释与反注释一段代码.下面简要介绍其操作. 方法一 块选择模式 插入注释: 用v进入virtual模式 用上下键选中需要 ...
- iOS 多线程开发之OperationQueue(二)NSOperation VS GCD
原创Blog.转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS SDK具体解释专栏 http://blog.csdn.net/column/details/huang ...
- java整合flex
java+flex项目整合 评论0 字号:大中小 订阅 第一种:javaproject和flexproject独立 这样的方式也是非常多人使用的方式.flex程序猿和java程序猿相互独立的工作. ...
- 两种Data Table参数化设置的区别
首先介绍Data Table的语法: 1.DataTable.value(ParameterID, SheetID) 2.DataTable(ParameterID, SheetID) 以上2种方法的 ...
- Python3.2官方文件翻译--课堂笔记和异常是阶级
6.7备注 有时喜欢Pasca在"录"和C中"数据体"的数据类型很实用.集合一些数据项. 一个空类定义能够清楚地显示: class Employee: pass ...
- openGL点精灵PointSprite具体解释: 纹理映射,旋转,缩放,移动
第一,什么是点精灵 openGL的图形由顶点构成,以后利用顶点进行纹理的映射.点精灵就是,一个顶点被当作一个精灵来处理.特别之处就是,一个顶点也可进行纹理贴出.比如,原来是个顶点构成的一个矩形,如今一 ...