HttpModule与HttpHandler详解(2)
接上篇(1):
2、身份检查
大家在作登录时,登录成功后,一般要把用户名放在Session中保存,在其它每一个页面的Page_Load事件中都检查Session中是否存在用户名,如果不存在就说明用户未登录,就不让其访问其中的内容。
在比较大的程序中,这种做法实在是太笨拙,因为你几乎要在每一个页面中都加入检测Session的代码,导致难以开发和维护。下面我们看看如何使用HttpModule来减少我们的工作量
由于在这里我们要用到Session中的内容,我们只能在AcquireRequestState和PreRequestHandlerExecute事件中编写代码,因为在HttpModule中只有这两事件中可以访问Session。这里我们选择PreRequestHandlerExecute事件编写代码。
第一步:创建一个类库ClassLibrary831。
第二步:编写一个类实现IHttpModule接口
class
TestModule:IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication
context)
{
}
}
第三步:在Init事件中注册PreRequestHandlerExecute事件,并实现事件处理方法
class
AuthenticModule:IHttpModule
{
public void Dispose(){}
public void Init(HttpApplication
context)
{
context.PreRequestHandlerExecute += new
EventHandler(context_PreRequestHandlerExecute);
}
void context_PreRequestHandlerExecute(object
sender, EventArgs e)
{
HttpApplication ha =
(HttpApplication)sender;
string path =
ha.Context.Request.Url.ToString();
int n =
path.ToLower().IndexOf("Login.aspx");
if (n == -1)
//是否是登录页面,不是登录页面的话则进入{}
{
if (ha.Context.Session["user"] == null)
//是否Session中有用户名,若是空的话,转向登录页。
{
ha.Context.Response.Redirect("Login.aspx?source=" +
path);
}
}
}
}
第四步:在Login.aspx页面的“登录”按钮中加入下面代码
protected
void Button1_Click(object sender, EventArgs e)
{
if(true) //判断用户名密码是否正确
{
if (Request.QueryString["source"] !=
null)
{
string s =
Request.QueryString["source"].ToLower().ToString();
//取出从哪个页面转来的
Session["user"] =
txtUID.Text;
Response.Redirect(s);
//转到用户想去的页面
}
else
{
Response.Redirect("main.aspx"); //默认转向main.aspx
}
}
}
第五步:在Web.Conofig中注册一下这个HttpModule模块
<httpModules>
<add name="TestModule"
type="ClassLibrary831.TestModule,ClassLibrary831"></add>
</httpModules>
3、多模块的操作
如果定义了多个HttpModule,在web.config文件中引入自定义HttpModule的顺序就决定了多个自定义HttpModule在处理一个HTTP请求的接管顺序。
HttpHandler
HttpHandler是HTTP请求的处理中心,真正地对客户端请求的服务器页面做出编译和执行,并将处理过后的信息附加在HTTP请求信息流中再次返回到HttpModule中。
HttpHandler与HttpModule不同,一旦定义了自己的HttpHandler类,那么它对系统的HttpHandler的关系将是“覆盖”关系。
IHttpHandler接口声明
public interface IHttpHandler
{
bool IsReusable { get; }
public void
ProcessRequest(HttpContext context); //请求处理函数
}
示例:把硬盘上的图片以流的方式写在页面上
class TestHandler :
IHttpHandler
{
public void
ProcessRequest(HttpContext context)
{
FileStream fs = new FileStream(context.Server.MapPath("worm.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>
Verb属性:指定了处理程序支持的HTTP动作。*-支持所有的HTTP动作;“GET”-支持Get操作;“POST”-支持Post操作;“GET,
POST”-支持两种操作。
Path属性:指定了需要调用处理程序的路径和文件名(可以包含通配符)。“*”、“*.aspx”、“showImage.aspx”、“test1.aspx,test2.aspx”
Type属性:用名字空间、类名称和程序集名称的组合形式指定处理程序或处理程序工厂的实际类型。ASP.NET运行时首先搜索bin目录中的DLL,接着在GAC中搜索。
这样程序运行的效果是该网站的任何一个页面都会显示worm.jpg图片。如何只让一个页面(default21.aspx)执行HttpHandler
中的ProcessRequest方法呢?最简单的办法是在Web.Config文件中把path配置信息设为default21.aspx。
根据这个例子大家可以考虑一下如何编写“验证码”了。
IHttpHandler工厂
IHttpHandlerFactory的作用是对IHttpHandler进行管理。工厂的作用请见http://hi.baidu.com/grayworm/blog/item/4a832160f8c9de46eaf8f8c1.html"
IHttpHandlerFactory接口的声明:
public interface
IHttpHandlerFactory
{
IHttpHandler GetHandler
(HttpContext context,string requestType,string url,string
pathTranslated);
void ReleaseHandler (IHttpHandler
handler);
}
GetHandler返回实现IHttpHandler接口的类的实例,ReleaseHandler使工厂可以重用现有的处理程序实例。
示例:两个用IHttpHandlerFactory来实现对不同HttpHandler的调用。
有两个HttpHandler:将图片显示在页面上的HttpHandler和生成验证码的Handler
//将图片显示在页面上的Handler
class TestHandler :
IHttpHandler
{
public void ProcessRequest(HttpContext
context)
{
FileStream fs = new
FileStream(context.Server.MapPath("worm.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;
}
}
}
//生成验证码的Handler
class CodeHandler:IHttpHandler
{
public bool IsReusable
{
get
{
return true;
}
}
public void ProcessRequest(HttpContext
context)
{
Image b = new
Bitmap(50,20);
Graphics g =
Graphics.FromImage(b);
SolidBrush sb = new
SolidBrush(Color.White);
Font f = new Font("宋体",
12);
string str = "";
Random r = new
Random();
for (int i = 0; i < 4; i++)
{
str += r.Next(10);
}
g.DrawString(str,f,sb,0,0);
b.Save(context.Response.OutputStream,
System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
IHttpHandler工厂
class
TestHandlerFactory : IHttpHandlerFactory
{
public
IHttpHandler GetHandler(HttpContext context, string requestType, string url,
string pathTranslated)
{
string fname = url.Substring(url.IndexOf('/') + 1);
while
(fname.IndexOf('/') != -1)
fname =
fname.Substring(fname.IndexOf('/') + 1);
string cname =
fname.Substring(0, fname.IndexOf('.'));
string className
="";
className =
"ClassLibrary831.CodeHandler";
object h =
null;
try
{
//h =
new TestHandler();
h =
Activator.CreateInstance(Type.GetType(className));
}
catch (Exception e)
{
throw new HttpException("工厂不能为类型" + cname + "创建实例。",
e);
}
return
(IHttpHandler)h;
}
public void
ReleaseHandler(IHttpHandler handler)
{
}
}(车延禄)
配置文件
<httpHandlers>
<add verb="*"
path="default21.aspx,default22.aspx"
type="ClassLibrary831.TestHandlerFactory,ClassLibrary831"></add>
</httpHandlers>
这样TestHandlerFactory就会根据请求的不同页面执行不同的HttpHandler处理程序了。
HttpHandler使用会话
如果要在处理程序中使用Session,那必须把该HttpHandler实现IRequiresSessionState接口,,IRequiresSessionState接口是个空接口,它没有抽象方法,只是一个标记。此处就不作例子验证了。
浙公网安备 33010602011771号