一.Httphandler:
在《模型一》的demo中,新建一个webform文件,MyWebForm.aspx,内容和简单,就是一行文字。
-
<body>
-
<form id="form1" runat="server">
-
<div>
-
This is MyWebForm
-
</div>
-
</form>
-
</body>
运行结果:
再看MyWebForm.aspx.cs文件代码:
-
public partial class MyWebForm : System.Web.UI.Page
-
{
-
protected void Page_Load(object sender, EventArgs e)
-
{
-
-
}
-
}
没有别的代码,只是实现了System.Web.UI.Page,Page又是什么呢?F12跟进去:
所以,在Webform中,我们申明的东西,写的页面,实现的业务逻辑,其实就是一个HttpHandler,而它是在PreRequestHandlerExecute 和 PostRequestHandlerExecute 这两个Event之间执行的。
这里有一个很重要的事件,MapRequestHandler,负责制定具体的handler处理请求:
它是在哪里配置的呢?
全局的webconfig中,(C:WindowsMicrosoft.NETFramework64v4.0.30319Configweb.config)
我们先用反编译工具,看看System.Web.UI.PageHandlerFactory:
这个核心方法 GetHandlerHelper干什么的呢?它就是传入的参数,创建一个Page。
其实从名字就能看的出来,PageHandlerFactory,PageHandler的工厂,当然输出Page。
还有web.config文件不能访问,就是因为已经配置了HttpForbiddenHandler,禁止访问。
强行访问web.config,就报错了
为什么禁止访问?HttpForbiddenHandler里面做了什么?反编译工具中看:
既然知道了怎么给不同后缀名的请求配置不同处理的HttpHandler,那我们能不能自定义呢?可以的。例如自定义一个图片验证码请求的HttpHandler。步骤如下:
1.webconfig下,在handlers节点配置一个处理verify后缀的HttpHandler。
2.建立VerifyCode类,并继承IHttpHandler,这个 是必须的。
3.在浏览器中访问 http://localhost:8090/handler/1.verify ,报错
错误很明显,是不存在handler控制器,那么在路由里面将它忽略掉吧。
4.忽略路由:
5.浏览器再次访问 http://localhost:8090/handler/1.verify ,显示了正确的验证码图片。
问题来了,这样自定义HttpHandler有什么价值?举个例子,可以做防盗链。
比如网上有很多图片“本图片仅限百度用户内部交流”,这就是百度图片做了防盗链,怎么做到的呢?就是用到了自定义的HttpHandler。步骤如下:
1.在web.config文件的 节点,针对图片类型,注册新的HttpHandler。
2.在PipeController控制器中加一个Handler()方法。
-
public ViewResult Handler()
-
{
-
IHttpHandler handler = base.HttpContext.Handler;
-
ViewBag.HandlerName = handler.GetType().ToString();
-
ViewBag.Url = Request.Url.AbsoluteUri;
-
return View();
-
}
同时,Handler.cshtml中也添加代码。
-
@{
-
ViewBag.Title = "Handler";
-
}
-
-
<h2>Handler</h2>
-
<p>@ViewBag.HandlerName</p>
-
<p>@ViewBag.Url</p>
-
<p>@Html.Image("/Content/Image/liuyan.jpg", "liuyan图片", "柳岩", new { id = "image" })</p>
运行一下,显示如图:
3.F12打开浏览器的调试工具,看到了原图地址:
4.既然已经找到了原图地址,在浏览器的新标签页打开,结果是这个样子:
由此可见,直接输入图片地址是打不开的,也达到了防盗链的效果。下面讲一下是如何做到的
看看上面定义的ImageHandler类的ProcessRequest方法:
-
public void ProcessRequest(HttpContext context)
-
{
-
// 如果UrlReferrer为空,则显示一张默认的禁止盗链的图片
-
if (context.Request.UrlReferrer == null || context.Request.UrlReferrer.Host == null)
-
{
-
context.Response.ContentType = "image/JPEG";
-
context.Response.WriteFile("/Content/Image/Forbidden.jpg");
-
}
-
else
-
{
-
// 如果 UrlReferrer中不包含自己站点主机域名,则显示一张默认的禁止盗链的图片
-
if (context.Request.UrlReferrer.Host.Contains("localhost"))
-
{
-
// 获取文件服务器端物理路径
-
string FileName = context.Server.MapPath(context.Request.FilePath);
-
context.Response.ContentType = "image/JPEG";
-
context.Response.WriteFile(FileName);
-
}
-
else
-
{
-
context.Response.ContentType = "image/JPEG";
-
context.Response.WriteFile("/Content/Image/Forbidden.jpg");
-
}
-
}
-
}
解释一下,如果请求上下文的UrlReferrer或者Host都是null,那么返回一个404的图片,如果请求上下文的UrlReferrer或者Host不是null,但host不是我们自己的网站(这里因为是本地调试,所以网址是localhost),同样返回一个404的图片。
我们比较一下,先监控一下能获得正常图片的请求:
再监控一下不能正常访问,只显示404图片:
当然,Http请求是可以模拟的,只要模拟的到位,一样可以得到正确的图片。
二. Webform请求的HttpApplication众多事件流程图
三.HttpModule和HttpHandler的区别
HttpModule:是任何一个Http请求都必须执行的东西,是附着在事件(Event)上的动作。
HttpHandler:任何一个请求一定有一个HttpHandler在处理,这个HttpHandler要么是aspx,要么是ashx,要么是mvc。
那么,什么场景用HttpModule,什么场景用HttpHandler?
1.session处理 : HttpModule
2.身份认证:HttpModule
3.权限检测:HttpModule
4.trace.axd查看追踪信息:HttpHandler
5.Outputcach输出缓存:HttpModule
6.config禁止下载:HttpHandler
7.cs代码不允许访问:HttpHandler
HttpModule适合处理全局的,因为任何请求都要经过它,任何请求都需要session,都需要身份认证,都需要权限检测,都需要缓存处理。
HttpHandler适合处理单一请求,查看追踪信息,config禁止下载,cs代码不允许访问这些都属于单一请求。
其实所有的东西HttpModule都可以做到,只是不适合而已。
四.路由、HttpHandler映射,哪个先执行?
用上面的,verify的例子,http://localhost:8090/handler/1.verify ,我们先在web.config中做了映射,所以它可以执行我们自定义的HttpHandler,输出显示验证码的图片。
如果url改成 http://localhost:8090/1.verify呢,找不到控制器。
这个图说明,虽然我们给verify做了映射,但是路由在HttpHandler之前就执行了,即还没有到MapRequestHandler,就被拦截了,谁拦截的?PostResolveRequestCache。
怎么做到的?要从UrlRoutingModule说起,用反编译工具打开UrlRoutingModule
再看一下PostResolveRequestCache方法里面干了什么。
那么MapRoute又是什么呢?用反编译工具看:
回到刚才的 PostResolveRequestCache方法。
RemapHandle是干什么的呢?解释一下
五.MVC路由以及和Webform的区别
那为什么aspx可以顺序执行下去,而MVC跳过去了呢?看看上面说到的GetRouteData方法,就是在这个方法里面,webform和mvc产生了分支。
再看一个MVC总的流程图:
B2:用MapRoute来增加路由。
并且把route放入RouteCollection中。route是什么?route的构造函数,参数分别是url和MvcRouteHandler,这里讲到B3了。
MvcRouteHandler里面又是什么?
B4:MvcRouteHandler可以通过GetHttpHandler方法得到一个HttpHandler,所以也可以说MvcRouteHandler负责生产IHttpHandler,如果不好理解,可以把MvcRouteHandler看成是HttpHandler。所以route的构造函数,就是传入了一个url和一个处理这个url的HttpHandler。这就和我们在webconfig文件中自定义的HttpHandler映射是一个道理,那里是什么后缀对应什么HttpHandler来处理(webform),这里是什么url对应什么HttpHandler来处理(mvc)。
url和处理这个url的HttpHandler,它们二者的对应关系是怎么对应的?就是在RouteConfig中建立的:
B5:所以RouteCollection里面有很多这样的route。
现在可以宏观的看这张mvc流程图了,先启动网站A1,然后进入Application_start(B1),执行Global(B2)中的RouteConfig,用routes.MapRoute注册了很多route,这些route保存了url和处理这些url的MvcRouteHandler(B3),然后这些MvcRouteHandler生产出HttpHandler(B4),将这些route放入RouteCollection(B5),好了 ,路由规则做好了,进入A2,22个事件依次执行(A3),执行到PostResolveRequestCache的时候,扩展了一个UrlRoutingModule(A4),UrlRoutingModule拿着url去在B5中已经准备好了的RouteCollection中去做匹配,找处理该url的MvcRouteHandler(A5),找到了MvcRouteHandler,然后把这个MvcRouteHandler生产的HttpHandler(mvc中就是MvcHandler)给context。
回头再看看PostResolveRequestCache方法: