在上篇文章中和大家一起学习了建立基本的WebAPI应用,立刻就有人想到了一些问题:
1.客户端和WebService之间文件传输
2.客户端或者服务端的安全控制
要解决这些问题,要了解一下WebAPI的基本工作方式。
(一)WebAPI中工作的Class
在MVC中大家都知道,获取Request和Response使用HttpRequest和HttpResponse两个类,在WebAPI中使用两外两个类:
HttpRequestMessage 和HttpResponseMessage,分别用于封装Requset和Response。除了这两个类之外,还有一个常见的抽象 类:HttpMessageHandler,用于过滤和加工HttpRequestMessage和HttpResponseMessage
(二)解决第一个问题
其 实第一个问题之所以被提出来应该是和客户端有关,如果客户端的请求是我们手写提交的,比如使用HttpClient封装的请求,则要传递文件之前,我们一 般会进行一次序列化,转化为二进制数组之类的,在网络上传输。这样的话,在Controller中的Action参数里,我们只需要接收这个二进制数组类 型的对象就可以了。
但是如果客户端是Web Form呢,比如我们提交一个Form到指定的Controller的Action中,这个Action要接收什么类型的参数呢?
或者我们问另外一个问题,如果我将Web Form提交到一个WebAPI的Action中 ,我要怎么去取出这个表单中的数据呢?
其 实我们应该想到:我们的Action设置的参数之所以能够被赋值,是因为WebAPI的架构中在调用Action时将HTTP请求中的数据解析出来分别赋 值给Action中的参数,如果真是这样的话,我们只需要在Action中获取到HTTP请求,然后直接获取请求里面的数据,就能解决上面的问题。
这 种想法是正确的,只不过,此时的HTTP请求已经不是最原始的HTTP Request,而是已经被转化成了HttpRequestMessage,在Action中,我们可以直接调用base.Requet来得到这个 HttpRequestMessage实例,通过这个实例我们就可以随心所欲的取出HTTP请求中想要的数据。
2.1从RequestMessage中获取普通表单数据
这里的普通表单是指不包含File的表单,也就是说表单的enctype值不是multipart/form-data,这时,表单的数据默认情况下是以Json来传递的
如下页面
<form name="form" action="~/api/FormSubmit?key=11234" method="post">
<input type="text" name="key" id="txtKey" />
<br />
<input type="text" name="value" id="txtValue" />
<br />
<input type="submit" id="btnSubmit" value="Submit" />
</form>
捕获到的请求为
提交到对应的Action为:
[HttpPost]
public async void submitForm()
{
StringBuilder sb = new StringBuilder();
HttpContent content = Request.Content;
JsonObject jsonValue = await content.ReadAsOrDefaultAsync<JsonObject>();
foreach (var x in jsonValue)
{
sb.Append(x.Key);
string va ;
if (x.Value.TryReadAs<string>(out va))
{
sb.Append(va);
}
}
}
这样最后可以得到 Json的值:{"key":"123","value":"123"} sb处理后的值为:key123value123
注:在该action中使用到了关键字async和await,这些在4.5中新提出的关键字主要是用于进行多线程取值的,在MVCAPI的设计中,大部分的方法都被设计成类似于下面的方法
public static Task<T> ReadAsOrDefaultAsync<T>(this HttpContent content);
返 回值是一个Task,这种返回新线程的方法虽然可以提高系统的响应能力,但是多线程取值会给编码带来不便,所以新出的关键字await用于阻塞当前线程并 获取目标线程的返回值,在方法体中使用await关键字后要求将方法声明为async用来表示该方法是异步的,并且返回值必须为void或者将返回者封装 在一个Task中
当然,如果你不喜欢这种写法,上面的action也可以写为:
Task readTask = content.ReadAsOrDefaultAsync<JsonObject>().ContinueWith((task) => { jsonValue = task.Result; });
readTask.Wait();
2.2从RequestMessage中获取multipart表单数据
将view页面改写为
<form name="form" action="~/api/FormSubmit?key=11234" method="post" enctype="multipart/form-data" >
<input type="text" name="key" id="txtKey" />
<br />
<input type="text" name="value" id="txtValue" />
<br />
<input type="file" name="file" id="upFile" />
<br />
<input type="submit" id="btnSubmit" value="Submit" />
</form>
此时捕获到得请求是
这里的文件内容被捕获软件解析成字符串,当然如果我上传的是其他的非文本格式的文件,文件会被转化为二进制数组
这时如果我们不更改action,而直接调用,会发生错误,原因很明显,这个HTTP的报文内容是无法被转换为JSON的,这时我们需要将表单的报文解析成另外一种格式
IEnumerable<HttpContent> bodyparts = await content.ReadAsMultipartAsync();
foreach (var bodypart in bodyparts)
{
string name;
name = bodypart.Headers.ContentDisposition.Name;
sb.Append(name + ":");
if (bodypart.Headers.Contains("filename"))
{
Stream stream = await bodypart.ReadAsStreamAsync();
StreamReader reader = new StreamReader(stream);
sb.Append(reader.ReadToEnd());
sb.Append("----");
}
else
{
string val = await bodypart.ReadAsStringAsync();
sb.Append(val);
sb.Append("----");
}
}
得到的处理后的sb值为:
{"key":123----"value":123----"file":******{文件的内容}*****----}
整合后的Action为
[HttpPost]
public async void submitForm()
{
StringBuilder sb = new StringBuilder();
HttpContent content = Request.Content;
if (content.IsMimeMultipartContent())
{
IEnumerable<HttpContent> bodyparts = await content.ReadAsMultipartAsync();
foreach (var bodypart in bodyparts)
{
string name;
name = bodypart.Headers.ContentDisposition.Name;
sb.Append(name + ":");
if (bodypart.Headers.Contains("filename"))
{
Stream stream = await bodypart.ReadAsStreamAsync();
StreamReader reader = new StreamReader(stream);
sb.Append(reader.ReadToEnd());
sb.Append("----");
}
else
{
string val = await bodypart.ReadAsStringAsync();
sb.Append(val);
sb.Append("----");
}
}
}
else
{
JsonObject jsonValue = await content.ReadAsOrDefaultAsync<JsonObject>();
foreach (var x in jsonValue)
{
sb.Append(x.Key);
string va;
if (x.Value.TryReadAs<string>(out va))
{
sb.Append(va);
}
}
}
}
(三)WebAPI工作方式
要想解决第二个问题就没这么容易了,我们需要更深入的理解WebAPI的工作方式。
其实对于WebAPI来说,它最初被设计为和WCF一样的:客户端、服务端两套结构,我们到现在之所以还没有提到客户端,是因为我们的请求别的方式来封装成HTTP请求或接收HTTP相应的,比如AJAX和Form表单提交。
在这里先给出一个服务端的响应工作流,让大家有个大体上的认识
(不得已在图片中加了水印,因为看到自己辛苦写的东西被人直接拿走,也不给出原文链接,心里真的不好受..希望不会影响大家的阅读...)
由于图片大小限制,所有的HttpRequestMessage被简写为HttpRequestMsg,HttpResponseMessage被简写了HttpResponseMsg
大 家可以看到,HTTP的请求最先是被传递到HOST中的,如果WebAPI是被寄宿在IIS上的,这个HOST就是IIS上,HOST是没有能力也没有必 要进行请求的处理的,请求通过HOST被转发给了HttPServer此时已经进入WebAPI的处理加工范围,HttpServer是 System.Net.HTTP中的一个类,通过HttpServer,请求被封装成了WebAPI中的请求承载 类:HttpRequestMessage,这个封装后的请求可以经过一系列自定义的Handler来处理,这些handler串联成一个
pipeline,最后请求会被传递给HttpControlDispather,这个类通过对路由表的检索来确定请求将被转发到的具体的 Controller中的Action。
Client端的处理与服务端类似,直接上图:
其实根据微软的说法,他们本身就被设计成类似但是可以独立运行的结构
ASP.NET Web API has a pipeline for processing HTTP messages on both the client and server. The client and server sides are designed to be symmetrical but independent; you can use each half by itself.
Both sides are built on some common objects:
-
HttpRequestMessagerepresents the HTTP request.
-
HttpResponseMessagerepresents the HTTP response.
-
HttpMessageHandlerobjects process the request and response.
直接看图,在客户端,Handlers pipeline最终是被传递到HttpClientHandler上的,由他负责HttpRequestMessage到HTTP请求的转换。
这里只说明一下Request,Response与其类似。
(四)解决第二个问题
由 此我们早就可以看出,想要解决第二个问题,可以直接在Handler PipeLine中进行,这种AOP风格的过滤器(拦截器)在REST的Webservice的安全验证中应用很广,一般大家比较乐于在HTTP头或者在 HTTP请求的URL中加上身份验证字段进行身份验证,下面举一个在Http头中添加身份验证信息的小例子
4.1客户端
客户端的customhandler用于将身份验证信息添加入报头
class RequestUpHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
request.Headers.Add("key", "11234");
return base.SendAsync(request, cancellationToken);
}
}
注:
1.customhandler继承自DelegatingHandler类,上面已经说过,WebAPI的客户端和服务端被设计为相互对应的两套结构,所以不论是在客户端还是服务端,customhandler都是继承自DelegatingHandler类
2.DelegatingHandler的sendAsync方法便是处理请求和接受请求时会被调用的方法,该方法返回值是HttPResponseMessage,接收的值为HttpRequestMessage,符合我们的一般认知
3.方法的最后,调用base.SendAsync是将Request继续向该pipeline的其他customHandler传递,并获取其返回值。由于该方法不包含Response的处理逻辑,只需直接将上一个CustomHandler的
返回值直接返回
客户端主程序
static void Main(string[] args)
{
HttpClient client = new HttpClient(new RequestUpHandler() { InnerHandler = new HttpClientHandler() });
HttpResponseMessage response = client.GetAsync("http://localhost:60023/api/FormSubmit").Result;
response.Content.ReadAsAsync<string>().ContinueWith((str) => { Console.WriteLine(str.Result); });
Console.Read();
}
客 户端的主程序创建了一个HttpClient,HttpClient可以接受一个参数,该参数就是CustomHandler,此处我们嵌入了我们定义的 RequestUpHandler,用于对Request报头进行嵌入身份验证码的处理,CustomHandler通过InnerHandler属性嵌 入其内置的下一个CustomHandler,此处,由于没有下一个CustomerHandler,我们直接嵌入HttpClientHandler用 于将HttpRequestMessage转化为HTTP
请求、将HTTP响应转化为HttpResponseMessage
4.2服务端
服务端的customHandler用于解析HTTP报头中的身份认证码
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
int matchHeaderCount = request.Headers.Count((item) =>
{
if ("key".Equals(item.Key))
{
foreach (var str in item.Value)
{
if ("11234".Equals(str))
{
return true;
}
}
}
return false;
});
if (matchHeaderCount>0)
{
return base.SendAsync(request, cancellationToken);
}
return Task.Factory.StartNew<HttpResponseMessage>(() => { return new HttpResponseMessage(HttpStatusCode.Forbidden); });
}
注:代码的处理逻辑很简单:如果身份验证码匹配成功,则通过base.SendAsync继续将请求向下传递,否则返回直接中断请求的传递,直接返回一个响应码为403的响应,指示没有权限。
注意由于SendAsync的返回值需要封装在Task之中,所以需要使用Task.Factory.StartNew将返回值包含在Task中
将customHandler注入到HOST中
本例中WebAPI HOST在IIS上,所以我们只需将我们定义的CustomHandler在Application_Start中定义即可
protected void Application_Start()
{
//省略其他逻辑代码
GlobalConfiguration.Configuration.MessageHandlers.Add(new HttpUrlHandler());
}
由于WebAPI Host在IIS上,所以HttpServer和HttpControllerDispatcher不用我们手工处理
在加上上面的处理后,如果没有身份验证码的请求,会得到如下的响应
分享到:
相关推荐
综上所述,`WebApiDemo`项目中的`CustomAuthorizeAttribute`是一个定制化的安全层,它允许开发者控制未授权请求的响应方式,提高API的用户体验和安全性。通过这样的自定义,我们可以更好地集成API到现代的、无状态的...
总结来说,这个项目提供了一个全面的解决方案,集成了C# MVC的WEB API开发和两种主流的在线支付方式——微信支付和支付宝支付,对于需要构建具备支付功能的Web服务的开发者来说,这是一个非常有价值的资源。...
总结来说,.NET Web API结合.NET Web MVC提供了一种强大且灵活的方式,用于构建和发布面向HTTP的服务。在这个例子中,我们展示了如何创建一个简单的RESTful API,用于获取产品的列表。这只是一个基础示例,实际开发...
Spring MVC是Java Web中最受欢迎的MVC框架之一,提供了强大的依赖注入、AOP(面向切面编程)等功能。 五、Java EE容器 Java EE容器如Tomcat、Jetty、Glassfish等,它们负责管理Web应用的生命周期,提供Servlet和JSP...
《.NET版本Web API使用详解——全部实例讲解》 在当今的互联网开发中,Web API已经成为构建RESTful服务的重要工具,特别是在.NET框架下,.NET版本的Web API为我们提供了强大的功能,使得创建、管理和调用HTTP服务变...
**Web程序设计——JSP** Web程序设计是构建动态、交互式网站的关键技术,而Java Server Pages(JSP)是Java平台上的一个核心组件,用于创建动态网页。孙延鹏的《Web程序设计——JSP》教程是学习JSP的重要资源,它...
Java Web4j 框架——Fish 是一个用于构建企业级Web应用的开源框架,它为开发者提供了便捷的工具和组件,以简化Web应用程序的开发流程。Web4j旨在提高开发效率,降低维护成本,同时支持MVC(Model-View-Controller)...
Spring MVC 是一个强大的Java Web开发框架,用于构建高效、可维护的Web应用...通过阅读"实验4 Spring MVC——Web.pdf"和研究"qasystemraw"、"qasystem"的实际项目,你可以更全面地掌握Spring MVC在实际应用中的运用。
ASP.NET MVC4.0是微软开发的一个用于构建Web应用程序的框架,它基于Model-View-Controller(MVC)设计模式,提供了更为灵活和可测试的Web应用开发方式。本资源为中文版,对于中文使用者来说,能更好地理解和学习相关...
8. **MVC模式**:虽然在描述中未明确提及,但Java Web开发常常采用Model-View-Controller(MVC)设计模式。在这个模式中,`TestServlet`可能扮演Controller角色,负责处理用户请求,调用Model层进行业务逻辑处理,再...
Web Forms是ASP.NET最早引入的开发模型,它通过控件和事件驱动的方式进行编程,模拟了桌面应用程序的开发体验。开发者可以利用控件库创建交互式的网页,事件处理机制则使得用户操作的响应更为直接。 四、ASP.NET ...
【标题】"构建Web解决方案——应用ASP.NET和ADO.NET"主要涵盖了两个核心的Microsoft技术:ASP.NET和ADO.NET,它们是构建高效、动态Web应用程序的关键工具。ASP.NET是.NET框架的一部分,专门用于创建交互式的、数据...
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,它从 Spring 框架诞生之初就被包含其中。正式名称“Spring Web MVC”来源于其源模块 `spring-webmvc`,但在实际使用中更常见地被称为“Spring MVC”。 与 ...
《Java Web开发实践教程——从设计到实现(第2版)》是一本深入探讨Java Web技术的权威指南,尤其适合初学者和有一定基础的开发者。本教程通过PPT的形式,以直观、易懂的方式讲解了从项目规划到实际开发的全过程。在...
在这个《动态Web数据库技术——基于JSP和XML技术实现电子教案》中,作者王红可能会详细讲解以下几个关键知识点: 1. JSP基础:包括JSP的生命周期、指令、脚本元素以及EL(Expression Language)和JSTL(JavaServer ...
在"实验5 Spring MVC——API.pdf"中,可能详细介绍了如何创建和测试一个Spring MVC API的步骤。这个文档可能包括了设置控制器、配置URL映射、处理数据绑定、异常处理等方面的内容。通过阅读这份文档,开发者能更好地...
ASP.NET是微软公司推出的一种强大的Web应用程序开发框架,它基于.NET Framework,为开发者提供了一种高效、安全且易于维护的方式来创建动态网站、Web应用程序和Web服务。本篇将深入探讨Web程序设计中的ASP.NET网站...
这两本书——《ASP.NET MVC4开发指南》和《ASP.NET MVC 4 in Action》都是围绕这个框架进行深入讲解的资源,旨在帮助读者掌握ASP.NET MVC 4的核心概念和技术。 《ASP.NET MVC4开发指南》可能会详细解释以下内容: ...