在上一篇中,介绍了
AcceptVerbs,ActionName,NonAction,OutputCache,ValidateInput,HandleError这几个修饰标签。它们的父类有所不同。例如:
AcceptVerbs 标签(类)从ActionMethodSelectorAttribute类派生;NonAction从ActionMethodSelectorAttribute派生。
然后,如HandleError它的定义是这样的:
public
class
HandleErrorAttribute : FilterAttribute, IExceptionFilter
{
//
Fields
private
const
string
_defaultView
=
"
Error
"
;
private
Type _exceptionType;
private
string
_master;
private
string
_view;
//
Methods
public
HandleErrorAttribute();
public
virtual
void
OnException(ExceptionContext filterContext);
//
Properties
public
Type ExceptionType {
get
;
set
; }
public
string
Master {
get
;
set
; }
public
string
View {
get
;
set
; }
}
它继承了FilterAttribute类,且实现了IExceptionFilter 接口。
asp.net MVC框架支持以下几类过滤器:
·AuthorizeAttribute用于实现用户验证及对动作访问的授权。
·Action过滤器
·Result过滤器
·Exception过滤器
并提供以下几个接口:
public
interface
IActionFilter
{
//
Methods
void
OnActionExecuted(ActionExecutedContext filterContext);
void
OnActionExecuting(ActionExecutingContext filterContext);
}
public
interface
IResultFilter
{
//
Methods
void
OnResultExecuted(ResultExecutedContext filterContext);
void
OnResultExecuting(ResultExecutingContext filterContext);
}
public
interface
IExceptionFilter
{
//
Methods
void
OnException(ExceptionContext filterContext);
}
public
interface
IAuthorizationFilter
{
//
Methods
void
OnAuthorization(AuthorizationContext filterContext);
}
另外还有一个标签:
[AttributeUsage(AttributeTargets.Method
|
AttributeTargets.Class,
Inherited
=
true
, AllowMultiple
=
false
)]
public
abstract
class
FilterAttribute : Attribute
{
private
int
_order;
protected
FilterAttribute();
public
int
Order {
get
;
set
; }
}
如果要实现一个自定义过滤器,须实现这个标签和一个以上的接口。MVC还提供了一个ActionFilterAttributen属性(类),它已经实现了IActionFilter, IresultFilter这两个接口,然后可以重载它们的方法来自定义过滤器。
(一)日志记录
现在做一个自定义的日志过滤器,用于记录日志。实现的目标很简单:当访问此页时,记录当前的时间。这个直接从ActionFilterAttribute 派生。
在实现之前,得说下执行顺序:
OnActionExecuting – 在Action执行之前调用。
OnActionExecuted – 在Action执行之后调用。
OnResultExecuting – 在Result产生之前调用。
OnResultExecuted
现在要做的是在ActionExecuting事件中记录时间,在ResultExecuted事件中记录时间,即在自定义过滤器中重载其中两个方法即可:
public
class
DateLogFilter : ActionFilterAttribute
{
public
override
void
OnActionExecuting(ActionExecutingContext filterContext)
{
Log(
"
ActionExecuting
"
+
DateTime.Now.ToString());
}
public
override
void
OnResultExecuted(ResultExecutedContext filterContext)
{
Log(
"
ResultExecuted
"
+
DateTime.Now.ToString());
}
private
void
Log(
string
strMessage)
{
strMessage.WriteFile(
@"
K:\logs\
"
,
"
log.txt
"
);
}
}
其中的Log方法用于记录内容。
用的时候就是以修饰标签的方式声明即可:
[DateLogFilter]
public
ActionResult NewsList()
{
……
}
这个简单的日志记录过滤器就完成了。这个日志过滤器的定义是没有多大意义的,它只用来演示自定义过滤器。
(二)实现图片防盗
图片防盗的原理是利用Http头中的Referer键来实现的。对于资源文件来说,它记录资源文件的引用地址:
请见:
http://www.cnblogs.com/jams742003/archive/
2010/02/01
/1660917.html
例如:http://192.168.1.105:8029/images/a1.gif
这个地址有一个a1.gif图片资源文件。而在另一个站中引用时:
<img src="http://192.168.1.105:8029/images/a1.gif" />
此时的Http头中截取部分键对:
·(Request-Line):GET /images/a1.gif HTTP/1.1
·Host:192.168.1.105:8029
·Referer:http://localhost:20372/web2/Test.aspx
其中的
Host
为资源的地址,而
Referer
为引用图片的地址。这两个地址不同,所以可以进行防盗处理了。
在WebForm模型中,通过自定义HttpHandler来实现防盗处理,而HttpHandler是Http请求的最终处理中心。例如,通过ashx资源文件进行防盗,因为这个类从IhttpHandler派生的。或者直接以HttpHandler来进行全局资源防盗处理。
(1)实现IhttpHandler接口
实现public void ProcessRequest(HttpContext context) 这个方法。通过Http上下文来判断Http头中的Host与Referer键值:context.Request.UrlReferrer.Host
(2)注册Handler
<
httpHandlers
>
<
add
path
="*.gif"
verb
="*"
type
="GifHandler"
/>
</
httpHandlers
>
(3)在IIS中添加Gif文件映射
以上三项完成后就可以了。
在MVC中实现防盗原理也是这样的。
现在还通过以前介绍的方法来实现并通过测试。
现在先看一个图片访问地址:
http://192.168.1.105:8196/Contents/images/a1.gif
这个地址是一张图片,然后,在MVC中,按路由来分析一下它的情况:
当访问这个地址时(URL),会路由到Contents控制器,并执行images动作,且向这个动作传递了一个a1.gif的字串参数。于是分别创建:
(01)控制器Contents
public
ActionResult images(
string
strFilename)
{
return
File(
"
http://www.cnblogs.com/content/images/
"
+
strFilename,
"
image/gif
"
);
}
这张图片放在content文件夹中的images文件夹中
这个动作就是返回图片文件。其中File方法是控制器的一个方法,用于返回ActionResult类型对象。
(02)过滤器
自定义过滤器,可以直接从ActionFilterAttribute类派生,重载它的方法。这里只重载OnActionExecuting这个方法。
实现为:
public
override
void
OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContextBase httpContext
=
filterContext.HttpContext;
if
(
null
!=
httpContext.Request.UrlReferrer)
{
string
serverDomain
=
httpContext.Request.Url.Host;
string
refDomain
=
httpContext.Request.UrlReferrer.Host;
if
(serverDomain.Contains(refDomain))
{
return
;
//
如果根域名相同就返回
}
ContentResult cr
=
new
ContentResult();
if
(FileType
==
FileType.Image)
{
cr.ContentType
=
"
image/jpeg
"
;
FileInfo fi
=
new
FileInfo(httpContext.Server.MapPath(
"
~/Content/images/z1.gif
"
));
httpContext.Response.WriteFile(fi.FullName);
}
filterContext.Result
=
cr;
}
}
现在分析一下这个方法:
原理很简单,在Http请求时,资源文件和html是分步进行,例如,一张html中带有一张图片,那么,它会先请求html,然后请求图片,这个在
http://www.cnblogs.com/jams742003/archive/
2010/02/01
/1660917.html
中已经分析过。在第一次请求中,在http头中host和referer两个键值并不是全有的,也就是在第二次请求中(请求图片)时,两个值才全有。那么,如果第一次请求时(请求html),那么要判断:
if (null != httpContext.Request.UrlReferrer)
如果不为无(一般我把null叫做无,而不叫空),那么才确定是资源请求。
然后:
string
serverDomain
=
httpContext.Request.Url.Host;
string
refDomain
=
httpContext.Request.UrlReferrer.Host;
这两句用于得到Host和Referer键值
于是,如果是本网站访问的,那么两个值应该相等(这两个值要分析,它们的主体部分应该相同),这里粗略的判断:
if
(serverDomain.Contains(refDomain))
{
return
;
//
如果根域名相同就返回
}
如果两部分相同,那么直接返回即可,不再做处理。
如果不相同,说明是别的网站访问的,那么:
if
(FileType
==
FileType.Image)
{
cr.ContentType
=
"
image/jpeg
"
;
FileInfo fi
=
new
FileInfo(httpContext.Server.MapPath(
"
~/Content/images/z1.gif
"
));
httpContext.Response.WriteFile(fi.FullName);
}
如果是图片的话,返回防盗图片给他即可。这里的contenttype并不严格。
这个过滤器是以修饰标签形式声明。
(03)为动作添加修饰
[SelfFilter(FileType.Image)]
public
ActionResult images(
string
strFilename)
{
return
File(
"
http://www.cnblogs.com/content/images/
"
+
strFilename,
"
image/gif
"
);
}
现在就是这种情况,其中SelfFilter是自定义的过滤器(一个从ActionFilterAttribute派生的属性类),
为这个属性类还要添加一个一个参数的构造器,它是FileType枚举类型。
public
SelfFilter(FileType fileType)
{
this
.FileType
=
fileType;
}
(04)测试
在一个别的网站中添加文件,文件内容为:
<
form
id
="form1"
runat
="server"
>
<
div
>
<
img
src
="http://192.168.1.105:8196/Contents/images/a1.gif"
/>
</
div
>
</
form
>
然后会发现,显示的是一张防盗图片。