HttpHandler是HTTP请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。HttpHandler与HttpModule不同,一旦定义了自己的HttpHandler类,那么它对系统的HttpHandler的关系将是“覆盖”关系. HttpHandler功能的实现通过实现IHttpHandler接口来达到.
当一个HTTP请求经同HttpModule容器传递到HttpHandler容器中时,ASP.NET Framework会调用HttpHandler的ProcessRequest成员方法来对这个HTTP请求进行真正的处理。以一个ASPX页面为例,正是在这里一个ASPX页面才被系统处理解析,并将处理完成的结果继续经由HttpModule传递下去,直至到达客户端。
对于ASPX页面,ASP.NET Framework在默认情况下是交给System.Web.UI.PageHandlerFactory这个HttpHandlerFactory来处理的。所谓一个HttpHandlerFactory,所谓一个HttpHandlerFactory,是指当一个HTTP请求到达这个HttpHandler Factory时,HttpHandlerFactory会提供出一个HttpHandler容器,交由这个HttpHandler容器来处理这个HTTP请求。
一个HTTP请求都是最终交给一个HttpHandler容器中的ProcessRequest方法来处理的。
IHttpHandler接口声明:
public
interface
IHttpHandler
{
bool
IsReusable {
get
; }
public
void
ProcessRequest(HttpContext context);
//
请求处理函数
}
v2.0.50727 下的machine.config中httpHandlers结点是这样的:<httpHandlers />,并没有给出详细的处理程序,在Web.config中才能看到。
Code
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type=""/>
<add verb="*" path="*_AppService.axd" validate="false" type=""/>
<add verb="GET,HEAD" path="ScriptResource.axd" type=""/>
<add verb="GET" path="CrystalImageHandler.aspx" type=""/>
</httpHandlers>
可以看到,在<httpHandlers>结点中将不同的文件类型映射给不同的Handler去处理. 上面的type部分为了好显示我去掉了,其实你打开web.config,可以看到里面的内容的.
Verb属性:指定了处理程序支持的HTTP动作。*-支持所有的HTTP动作;“GET”-支持Get操作;“POST”-支持Post操作;“GET, POST”-支持两种操作
Path属性:指定了需要调用处理程序的路径和文件名(可以包含通配符).“*”、“*.aspx”
Type属性:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型. ASP.NET运行时首先搜索bin目录中的DLL,接着在GAC中搜索
IHttpHandler工厂
IHttpHandlerFactory的作用是对IHttpHandler进行管理。接口如下:
public
interface
IHttpHandlerFactory
{
IHttpHandler GetHandler (HttpContext context,
string
requestType,
string
url,
string
pathTranslated);
void
ReleaseHandler (IHttpHandler handler);
}
GetHandler返回实现IHttpHandler接口的类的实例
ReleaseHandler使工厂可以重用现有的处理程序实例
HttpHandler的示例:
把硬盘上的图片以流的方式写在页面上
class
TestHandler : IHttpHandler
{
public
void
ProcessRequest(HttpContext context)
{
FileStream fs
=
new
FileStream(context.Server.MapPath(
"
test.jpg
"
), FileMode.Open);
byte
[] b
=
new
byte
[fs.Length];
fs.Read(b,
0
, (
int
)fs.Length);
fs.Close();
context.Response.OutputStream.Write(b,
0
, b.Length);
}
public
bool
IsReusable
{
get
{
return
true
;
}
}
}
Web.Config配置文件
<httpHandlers>
<add verb="*" path="*" type="ClassLibrary831.TestHandler,ClassLibrary831"></add>
</httpHandlers>
使用HttpHandler实现图片防盗链
namespace
CustomHandler
{
public
class
JpgHandler : IHttpHandler
{
public
void
ProcessRequest(HttpContext context)
{
//
获取文件服务器端物理路径
string
FileName
=
context.Server.MapPath(context.Request.FilePath);
//
如果UrlReferrer为空,则显示一张默认的禁止盗链的图片
if
(context.Request.UrlReferrer.Host
==
null
)
{
context.Response.ContentType
=
"
image/JPEG
"
;
context.Response.WriteFile(
"
/error.jpg
"
);
}
else
{
//
如果 UrlReferrer中不包含自己站点主机域名,则显示一张默认的禁止盗链的图片
if
(context.Request.UrlReferrer.Host.IndexOf(
"
yourdomain.com
"
)
>
0
)
{
context.Response.ContentType
=
"
image/JPEG
"
;
context.Response.WriteFile(FileName);
}
else
{
context.Response.ContentType
=
"
image/JPEG
"
;
context.Response.WriteFile(
"
/error.jpg
"
);
}
}
}
public
bool
IsReusable{
get
{
return
true
; }
}
}
}
编译后复制DLL文件到Bin文件夹下,然后在web.config注册
<httpHandlers>
<add path="*.jpg" verb="*" type="CustomHandler.JpgHandler, CustomHandler" />
</httpHandlers>
通过IhttpHandler实现图片验证码
ashx是什么文件呢? 其实该文件用于写web handler. 在web里面添加一个这样的文件,可以看到下列的代码:
<%
@ WebHandler Language
=
"
C#
"
Class
=
"
Handler
"
%>
using
System;
using
System.Web;
public
class
Handler : IHttpHandler
{
public
void
ProcessRequest (HttpContext context)
{
context.Response.ContentType
=
"
text/plain
"
;
context.Response.Write(
"
Hello World
"
);
}
public
bool
IsReusable {
get
{
return
false
;
}
}
}
在上面的基础上修改我们需要的代码如下:
<%
@ WebHandler Language
=
"
C#
"
Class
=
"
Handler
"
%>
using
System;
using
System.Drawing;
using
System.Drawing.Imaging;
using
System.Text;
using
System.Web;
using
System.Web.SessionState;
public
class
Handler : IHttpHandler, IRequiresSessionState {
public
void
ProcessRequest(HttpContext context) {
context.Response.ContentType
=
"
image/gif
"
;
//
建立Bitmap对象,绘图
Bitmap basemap
=
new
Bitmap(
200
,
60
);
Graphics graph
=
Graphics.FromImage(basemap);
graph.FillRectangle(
new
SolidBrush(Color.White),
0
,
0
,
200
,
60
);
Font font
=
new
Font(FontFamily.GenericSerif,
48
, FontStyle.Bold, GraphicsUnit.Pixel);
Random r
=
new
Random();
string
letters
=
"
ABCDEFGHIJKLMNPQRSTUVWXYZ
"
;
string
letter;
StringBuilder s
=
new
StringBuilder();
//
添加随机的五个字母
for
(
int
x
=
0
; x
<
5
; x
++
) {
letter
=
letters.Substring(r.Next(
0
, letters.Length
-
1
),
1
);
s.Append(letter);
graph.DrawString(letter, font,
new
SolidBrush(Color.Black), x
*
38
, r.Next(
0
,
15
));
}
//
混淆背景
Pen linePen
=
new
Pen(
new
SolidBrush(Color.Black),
2
);
for
(
int
x
=
0
; x
<
6
; x
++
)
graph.DrawLine(linePen,
new
Point(r.Next(
0
,
199
), r.Next(
0
,
59
)),
new
Point(r.Next(
0
,
199
), r.Next(
0
,
59
)));
//
将图片保存到输出流中
basemap.Save(context.Response.OutputStream, ImageFormat.Gif);
context.Session[
"
CheckCode
"
]
=
s.ToString();
//
如果没有实现IRequiresSessionState,则这里会出错,也无法生成图片
context.Response.End();
}
public
bool
IsReusable {
get
{
return
true
; }
}
}
最后的就是需要在页面上引用这个验证码了. 创建一个新的aspx页面.
<img src="Handler.ashx" alt="图片验证码" />
需要注意的就是我们继承了一个IRequiresSessionState接口:
public interface IRequiresSessionState{}
Handler类不仅需要实现 IHttpHandler接口,为了在这个Handler类中使用SessionState,还需要实现IRequiresSessionState接口,对于这个接口,MSDN的解释是这样的:Specifies that the target HTTP handler requires read and write access to session-state values. This is a marker interface and has no methods.(中文大概的意思:指定当前Http Handler需要对SessionState值的读写访问权。这是一个标记接口,没有任何方法)。这个接口没有任何需要实现的方法或属性,大家只要记得:如果想在HttpHandler中使用SessionState,必须实现这个接口,实际上也就是在类的标头将这个接口加进去。
收集整理与自己见解总结, 且当回忆, 下次再讨论一下HttpModule的理论与方法.