主题 | 概要 |
---|---|
Asp.net | Http请求处理程序 |
编辑 | 时间 |
新建 | 20171113 |
序号 | 参考资料 |
1 | Asp.net本质论 |
2 | https://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html |
前面从介绍HttpApplication对像可知,HttpApplication有19个标准事件,在第8个事件PostMapRequestHandler(
https://msdn.microsoft.com/en-us/library/system.web.httpapplication.postmaprequesthandler(v=vs.110).aspx)触发的时候,标志着已经获取到了处理当前请求的处理程序对象,在第11个事件PreRequestHandlerExecute之后,HttpApplicaton将执行这个处理程序。那么,HttpApplicaton选择处理程序的依据是什么?什么对象可以作为处理程序?HttpApplication如何得到这个处理程序对象?
处理程序概述
前面知道,HttpApplication是在请求到达后,HttpRuntime通过应用程序对象池创建的一个用于处理请求的对象。
但是真正的处理请求是在处理程序这个环节,那么HttpApplication的作用是什么呢?我们可以将它看作请求到达处理程序和离开处理程序的一个管道,这个管道提供了统一处理所有请求的机制,使得我们可以在请求被真正处理之前和处理之后进行预处理和处理后的工作。而处理程序则负责完成实际的请求处理工作,对于网站开发程序员来说,大多数的开发工作是围绕着处理程序展开的。处理程序在不同的网站开发技术中,有着不同的名称,在ASP.NET中,称为HttpHandler。
在Asp.net中,所有处理程序类必须实现IHttpHandler或者IHttpAsyncHandler接口。
IHttpHandler的说明:
https://msdn.microsoft.com/zh-cn/library/system.web.ihttphandler(v=vs.110).aspx
定义如下:
public interface IHttpHandler
{
// 其他Request是否可以使用IHttpHandler
bool IsReusable { get; }
// 处理HttpRequest
void ProcessRequest(HttpContext context);
}
RrocessRequest是这个接口的主要方法,接收一个HttpContext类型的上下文对象,通过这个对象,处理程序可以得到关于处理请求所需的信息,通过这个参数的Response属性,可以得到管理回应的对象,用户向客户端返回服务器处理的结果。
IsReuseable属性表示这个处理程序对象在使用之后,是否还可以被缓存起来,在以后的请求处理程序中再次使用。
处理程序工厂
实现了处理程序接口的类就可以被用来创建处理程序对象直接使用,如果再配合一个处理程序工厂,就可以实现处理程序对象的管理。比如说,创建一个处理程序对象池,就可以不用在每次使用处理程序的时候创建一个新的对象,而是从池中取一个现有的对象直接使用,以提高效率。
处理程序工厂类必须实现接口,定义如下:
https://msdn.microsoft.com/zh-cn/library/system.web.ihttphandlerfactory(v=vs.110).aspx
其中,GetHandler方法用来通过这个处理程序工厂获取一个处理程序对象,ReleaseHandler方法用来释放一个处理程序对象。
常见的处理程序工厂:
注册处理程序
每一种处理程序用来处理一类的请求,不同的请求类别通过不同的扩展名来进行区分,处理程序与网站之间的匹配关系在网站的配置文件web.config中通过配置参数进行设置。
System.web配置元素的子元素httpHandlers用来配置网站所使用的处理程序。
一般处理程序
Asp.net自带的实现了IHttpHandler的处理程序,扩展名为*.ashx,这个处理程序已经定义在系统配置文件的web.config中,对于.net4.0的路径:
(C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config),
所以浏览器可以直接请求扩展名为ashx的地址:
通过配置文件,我们可以看到,对于扩展名为ashx的请求是通过定义在命名空间System.Web.UI下的SimpleHandlerFactory处理程序工厂来完成的。
当请求一个ashx扩展名的服务器资源时,SimpleHandlerFactory将找到对应的ashx文件,通过这个文件找到对应的处理程序。最后,SimpleHandlerFactory通过反射创建一个此类型的处理程序对象实例,并通过GetHandler方法返回HttpApplication,完成最终的请求处理过程。
页面处理程序
扩展名为aspx的请求,将由PageHandlerFactory这个处理程序工厂进行处理。
Web服务处理程序
对于Web服务来说,标准的方式是使用SOAP协议,在SOAP协议中,请求和响应的数据通过XML格式进行描述。在Asp.net 4.0下,对于Web服务来说,还可以选择支持Ajax访问。为了同时支持这两种类型的请求处理,在Asp.net 4.0下,处理程序工厂采用了两级结构。首先,通过标准的处理程序工厂来取得服务的处理程序;其次,在内部根据请求的内容来取得实际的处理程序工厂;最终,取得处理请求的处理程序。
在ScriptHandlerFactory内部,定义了两个处理程序工厂,RestHandlerFactory和WebServiceHandlerFactory,伪代码如下:
在ScriptHandlerFactory内部,通过GetHandler方法获取一个处理程序对象实例的时候,将首先判断请求是否是一个REST请求,根据判断的结果来决定当前实际使用的处理程序工厂。
MVC处理程序
在asp.net MVC2下,引入了Controller的概念,请求将被首先路由到Controller进行处理,然后由Controller分配到相应的Action完成实际的处理工作。处理的数据结果就是所谓的Modal。然后,这个Modal被传递给View转换成显示的界面元素,最终发送到客户端完成处理工作。
在Asp.net中,起着路由作用的处理程序不是一个普通的处理程序,因为对于MVC的请求来说根本就不是通过请求的扩展名来判断的,所以,这个处理程序通过一个类型为UrlRouting Module的Module配置在网站中,通过HttpApplication的事件管道得到这个处理程序。
在系统的配置文件中,可以看到确实配置了这个模块:
那么这个模块是什么时机起的作用呢,或者是从哪里开始拦截正常的Http请求的呢?
反编译dll(注:我并没有去反编译源码,Reflactor一直安装有问题,参考了
https://www.cnblogs.com/RobbinHan/archive/2011/12/05/2270707.html):
protected virtual void Init(HttpApplication application)
{
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
}
可以看到,在PostResolveRequestCache,PostMapRequestHandler事件时进行了拦截。
拦截后,主要做了什么工作?
看OnApplicationPostResolveRequestCache这个方法:
public class UrlRoutingModule : IHttpModule
{
protected virtual void Init(HttpApplication application)
{
application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
}
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context);
PostResolveRequestCache(context);
}
public virtual void PostResolveRequestCache(HttpContextBase context)
{
// Match the incoming URL against the route table
RouteData routeData = RouteCollection.GetRouteData(context);
// Do nothing if no route found
if (routeData == null)
{
return;
}
// If a route was found, get an IHttpHandler from the route's RouteHandler
IRouteHandler= routeData.RouteHandler;
RequestContext requestContext = new RequestContext(context, routeData);
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
// Remap IIS7 to our handler
context.RemapHandler(httpHandler);
}
}
总结起来,步骤是:
1)UrlRoutingModule在PostResolveRequestChche事件中做的第一件事就是包装当前的HttpContext为HttpContextWrapper。
(2)接着把HttpContextWrapper传给RouteTable,RouteTable根据参数返回一个匹配的路由对象:RouteData。
(3)如果UrlRoutingModule成功获取到RouteData,则会创建HttpContext对象和RouteData的RouteContext路由对象。
(4)然后把RouteContext路由对象传递给Handler的构造函数,实例化出一个新的HttpHandler;同时把此路由对象RouteContext通过HttpContext对象的Items集合保存起来,以便在PostMapRequestHandler中使用。
控制器的工厂接口IControllerFactory
前面通过UrlRoutingModule(实现IHttpModule接口)通过注册的路由表匹配得到了一个MvcRouteHandler(实现IRouteHandler接口),通过MvcRouteHandler创建一个MvcHandler(实现IHttpHandler接口)对象。而MvcHandler则要通过IControllerFactory对象,实现控制器的对象实例。
public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
void ReleaseController(IController controller);
}
MVC请求的处理过程