URL重写及干掉ASP.NET试图状态的实现方法
程序员文章站
2024-03-07 16:23:57
1、url重写已经很普遍了,但基本上大部分的url重写都不支持页面的相对路径,所有如果想在已经开发好的项目中添加还是有压力的,第二就是例如微软的那个url重写是根据正则表达...
1、url重写已经很普遍了,但基本上大部分的url重写都不支持页面的相对路径,所有如果想在已经开发好的项目中添加还是有压力的,第二就是例如微软的那个url重写是根据正则表达式来处理的,那样是很好,但也有不足之处,就是不方便定位到某个页面只能有哪些参数。
我觉得要解决的问题有一下几个:
1、解决如图片js等不能使用相对路径的文件
2、解决某个页面能有几个参数和哪些参数是可选的
下面就是解决掉这些问题了
添加处理程序myhttpmodule,下面是我的一个简单的处理程序(我只是做了一个简单的,并没有考虑性能,而且我是写死的一个url重写就是重写成没有扩展名的)
using system;
using system.collections.generic;
using system.web;
using system.io;
using system.text;
namespace myclass
{
public class myhttpmodule : ihttpmodule
{
#region ihttpmodule 成员
///<summary>
/// 释放所有资源
///</summary>
public void dispose()
{
}
///<summary>
/// 初始化模块,并使其为处理请求做好准备
///</summary>
///<param name="context"> 一个 system.web.httpapplication,它提供对 asp.net 应用程序内所有应用程序对象的公用的方法、属性和事件的访问</param>
public void init(httpapplication context)
{
context.authorizerequest += new
eventhandler(this.basemodulerewriter_authorizerequest);
}
///<summary>
/// 当安全模块已验证用户授权时发生
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
protected virtual void basemodulerewriter_authorizerequest(
object sender, eventargs e)
{
system.web.httpapplication app = (system.web.httpapplication)sender;
rewrite(app.request.path, app);
}
///<summary>
/// 重写url
///</summary>
///<param name="requestedpath">url的虚拟路径</param>
///<param name="app"></param>
protected void rewrite(string requestedpath, system.web.httpapplication app)
{
list<string> qerystring;
string virtualpath;
string inputfile = getinputfile(app.context, out virtualpath, out qerystring);//获取到真实的文件信息
if (system.io.path.getextension(inputfile) == ".aspx")//如果是aspx文件 那么则把保留重写的url
{
app.context.rewritepath(requestedpath, string.empty, string.empty);//这里查询参数我没去处理了,也就是request.querystring的信息,如果取qerystring 然后去处理成一个字符串
return;
}
app.context.rewritepath(virtualpath, string.empty, app.context.request.querystring.tostring());//其它文件则使用找到的路径
}
///<summary>
/// 获取url对应的绝对路径和虚拟路径及查询参数
///</summary>
///<param name="context"></param>
///<param name="virtualpath">虚拟路径</param>
///<param name="qerystring">查询参数 如果为null请取httpcontext.request.querystring</param>
///<returns>url对应的绝对路径</returns>
public static string getinputfile(httpcontext context, out string virtualpath, out list<string> qerystring)
{
string executionfilepath = context.request.apprelativecurrentexecutionfilepath.remove(0, 2);//获取当前对应的虚拟路径并干掉“~/”
string inputfile = context.request.physicalpath;//获取当前url对于的绝对路径
virtualpath = context.request.apprelativecurrentexecutionfilepath;
qerystring = null;
list<string> qerylist = new list<string>();
if (!file.exists(inputfile))//判断文件是否存在,也就是没有被重写的url获取使用绝对路径的资源等等
{
bool b = false;
string filename;
string extension;
string applicationpath = context.request.physicalapplicationpath;//获取网站的跟目录
var temppath = getfileinfo(inputfile, out filename, out extension);
while (!b)//根据目录循环获取有效的文件目录
{
b = file.exists(temppath + "\\" + extension);//判断文件是否存在
if (temppath + "\\" == applicationpath)//如果查找到根目录还没有查找到那么则不需要在查了
{
break;
}
if (!string.isnullorwhitespace(filename))
{
qerylist.add(filename);//如果不存在那么这个就是参数 例如http://localhost:4688/webform1/2011/ (对应http://localhost:4688/webform1.aspx?xxx=2011)
}
temppath = getfileinfo(temppath, out filename, out extension);
}
if (b)//如果查找到了就把查找到的路径复制给输出或返回参数
{
inputfile = temppath + "\\" + extension;
virtualpath = "~/" + inputfile.replace(applicationpath, null);
}
if (path.getextension(extension) == ".aspx")//如果是asp.net那么则把list复制给输出参数 qerystring
{
qerystring = qerylist;
}
}
return inputfile;
}
///<summary>
/// 获取指定目录+文件是否有效
///</summary>
///<param name="inputfile">目录</param>
///<param name="filename"></param>
///<param name="extension"></param>
///<returns></returns>
private static string getfileinfo(string inputfile, out string filename, out string extension)
{
var temppath = directory.getparent(inputfile).fullname;//获取传进来目录的父目录
filename = inputfile.replace(temppath + "\\", null);//获取子目录名称
extension = path.getextension(inputfile);//获取扩展名
if (string.isnullorwhitespace(extension))//如果扩展名为null那么则认为是aspx文件
{
extension = filename + ".aspx";
}
else
{
extension = filename + extension;
}
return temppath;
}
#endregion
}
}
因为我在处理aspx页面时还是传入的重写后的路径,所有我们还有添加一个继承ihttphandlerfactory的类
代码如下:
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.web;
using system.io;
using system.web.ui;
namespace myclass
{
public class myhttphandlerfactory:ihttphandlerfactory
{
#region ihttphandlerfactory 成员
///<summary>
/// 返回实现 system.web.ihttphandler 接口的类的实例
///</summary>
///<param name="context">system.web.httpcontext 类的实例,它提供对用于为 http 请求提供服务的内部服务器对象(如 request、response、session和 server)的引用</param>
///<param name="requesttype">客户端使用的 http 数据传输方法(get 或 post)</param>
///<param name="url">所请求资源的 system.web.httprequest.rawurl</param>
///<param name="pathtranslated">所请求资源的 system.web.httprequest.physicalapplicationpath</param>
///<returns>处理请求的新的 system.web.ihttphandler 对象</returns>
public ihttphandler gethandler(httpcontext context, string requesttype, string url, string pathtranslated)
{
list<string> qerystring;
string virtualpath;
string inputfile =myhttpmodule.getinputfile(context, out virtualpath, out qerystring);//这里跟那里是一样的
object[] obj = new object[] { };
dictionary<string, string> qerystringdictionary = new dictionary<string, string>();
var receivemembers = system.web.compilation.buildmanager.getcompiledtype(virtualpath).getmember("receiveparameters");//获取访问当前页面的所有receiveparameters成员 (这个是我自己加的,就是想做成和mvc的那种模式,但可能不是很好)
system.reflection.methodinfo receiveparameters=null;
if (qerystring != null&&qerystring.count>0)//如果查找到没有参数则不去反射了
{
foreach (system.reflection.memberinfo receivemember in receivemembers)//遍历所有receiveparameters成员
{
if (receivemember.membertype == system.reflection.membertypes.method)//因为上面获取到的是成员 但我们要的是方法所有要判断下
{
system.reflection.methodinfo methodinfo = receivemember as system.reflection.methodinfo;
if (methodinfo != null)
{
var parameters = methodinfo.getparameters();//获取receiveparameters方法的所有参数
int optionalcount = parameters.count(i => i.isoptional);//获取receiveparameters参数里面有多少个可选参数
bool b = qerystring.count == parameters.length - optionalcount;
if (qerystring.count == parameters.length || b)//如果当前查询的参数或receiveparameters的所有参数-去可选择的查询参数相等
{
receiveparameters = methodinfo;//记录这个方法
int i = 0;
obj = new object[parameters.length];//记录参数值,到后面调用receiveparameters时用
for (; i < parameters.length; i++)
{
string name = parameters[i].name;//获取参数的名称
string value = string.empty;
if (qerystring.count > i)//如果receiveparameters参数没到可选参数那么则去查询的字符串
{
value = qerystring[i];
}
obj[i] = value;//把查询的字符串保存起来,到后面调用receiveparameters时用
qerystringdictionary.add(name, value);//添加到自定义的集合里面
}
break;
}
}
}
}
if (receiveparameters == null)//判断是否已经找到,如果没找到就把以前找的文件信息全部赋为重写的文件信息,也就是不存在的
{
virtualpath = context.request.path;
inputfile = context.request.physicalpath;
}
}
var temp= system.web.ui.pageparser.getcompiledpageinstance(virtualpath, inputfile, context);//编译页面
if (receiveparameters != null)//这个里面的内容其实应该写到releasehandler里面去的,但我写在这里了
{
system.web.ui.page page = (system.web.ui.page)temp;
page.init+=new eventhandler(page_init);//添加一个事件 ,//还有就是本来应该添加一个pagebase类的,那样就可以把真实的路径信息和查询参数放进去
sss = receiveparameters;
sssobj = obj;
//receiveparameters.invoke(temp, obj);
}
return temp;
}
public system.reflection.methodinfo sss { get; set; }
public object[] sssobj { get; set; }
protected void page_init(object sender, eventargs e)
{
sss.invoke(sender, sssobj);//当page执行到这里时就去调用receiveparameters方法 在这里还可以做其它的判断。。。 但不符合编程规范(我的理解)
}
///<summary>
/// 使工厂可以重用现有的处理程序实例
///</summary>
///<param name="handler">要重用的 system.web.ihttphandler 对象</param>
public void releasehandler(ihttphandler handler)
{
}
#endregion
}
}
页面代码就是多放几个方法
///<summary>
/// 一个参数的 如果需要多个则手动添加如public void receiveparameters(string name,string value)等等 这样页面编译后就会根据参数自动运行这个方法并转递参数值
///</summary>
///<param name="name">参数名称为name</param>
public void receiveparameters(string name)
{
var temp = request;
}
url的解决了,在来看看干掉试图的。。。
我只写了把事件的实体状态去掉了,然后手动去激发控件的事件,而且就是在url中写里面解决的 代码如下:
protected void page_init(object sender, eventargs e)
{
sss.invoke(sender, sssobj);
page page = (page)sender;
foreach (string name in page.request.form.allkeys)//查找form里面所有的字典 其实应该取__eventargument隐藏域的
{
try
{
system.web.ui.control control = page.findcontrol(page.page.request.form[name]);//查找这个控件
if (control != null)
{
string value = page.request.form[page.posteventsourceid];
ipostbackeventhandler ip = control as ipostbackeventhandler;
if (ip != null)//能转换成ipostbackeventhandler 那么就激发它
{
ip.raisepostbackevent(value);
break;
}
ipostbackdatahandler backdatahandler = control as ipostbackdatahandler;
if (backdatahandler != null)//能转换成ipostbackdatahandler 就把__eventtarget隐藏域的值传给控件 然后激发更改事件
{
system.collections.specialized.namevaluecollection namevaluecollection=new system.collections.specialized.namevaluecollection();
namevaluecollection.add(page.request.form[control.clientid],page.request.form[control.clientid]);
backdatahandler.loadpostdata(control.clientid, namevaluecollection);
backdatahandler.raisepostdatachangedevent();
}
}
break;
}
catch
{
}
}
}
这样简单的处理就完了,
我希望各位来帮我改进改进,因为我毕竟还不太了解asp.net的处理机制。。。
我觉得要解决的问题有一下几个:
1、解决如图片js等不能使用相对路径的文件
2、解决某个页面能有几个参数和哪些参数是可选的
下面就是解决掉这些问题了
添加处理程序myhttpmodule,下面是我的一个简单的处理程序(我只是做了一个简单的,并没有考虑性能,而且我是写死的一个url重写就是重写成没有扩展名的)
复制代码 代码如下:
using system;
using system.collections.generic;
using system.web;
using system.io;
using system.text;
namespace myclass
{
public class myhttpmodule : ihttpmodule
{
#region ihttpmodule 成员
///<summary>
/// 释放所有资源
///</summary>
public void dispose()
{
}
///<summary>
/// 初始化模块,并使其为处理请求做好准备
///</summary>
///<param name="context"> 一个 system.web.httpapplication,它提供对 asp.net 应用程序内所有应用程序对象的公用的方法、属性和事件的访问</param>
public void init(httpapplication context)
{
context.authorizerequest += new
eventhandler(this.basemodulerewriter_authorizerequest);
}
///<summary>
/// 当安全模块已验证用户授权时发生
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
protected virtual void basemodulerewriter_authorizerequest(
object sender, eventargs e)
{
system.web.httpapplication app = (system.web.httpapplication)sender;
rewrite(app.request.path, app);
}
///<summary>
/// 重写url
///</summary>
///<param name="requestedpath">url的虚拟路径</param>
///<param name="app"></param>
protected void rewrite(string requestedpath, system.web.httpapplication app)
{
list<string> qerystring;
string virtualpath;
string inputfile = getinputfile(app.context, out virtualpath, out qerystring);//获取到真实的文件信息
if (system.io.path.getextension(inputfile) == ".aspx")//如果是aspx文件 那么则把保留重写的url
{
app.context.rewritepath(requestedpath, string.empty, string.empty);//这里查询参数我没去处理了,也就是request.querystring的信息,如果取qerystring 然后去处理成一个字符串
return;
}
app.context.rewritepath(virtualpath, string.empty, app.context.request.querystring.tostring());//其它文件则使用找到的路径
}
///<summary>
/// 获取url对应的绝对路径和虚拟路径及查询参数
///</summary>
///<param name="context"></param>
///<param name="virtualpath">虚拟路径</param>
///<param name="qerystring">查询参数 如果为null请取httpcontext.request.querystring</param>
///<returns>url对应的绝对路径</returns>
public static string getinputfile(httpcontext context, out string virtualpath, out list<string> qerystring)
{
string executionfilepath = context.request.apprelativecurrentexecutionfilepath.remove(0, 2);//获取当前对应的虚拟路径并干掉“~/”
string inputfile = context.request.physicalpath;//获取当前url对于的绝对路径
virtualpath = context.request.apprelativecurrentexecutionfilepath;
qerystring = null;
list<string> qerylist = new list<string>();
if (!file.exists(inputfile))//判断文件是否存在,也就是没有被重写的url获取使用绝对路径的资源等等
{
bool b = false;
string filename;
string extension;
string applicationpath = context.request.physicalapplicationpath;//获取网站的跟目录
var temppath = getfileinfo(inputfile, out filename, out extension);
while (!b)//根据目录循环获取有效的文件目录
{
b = file.exists(temppath + "\\" + extension);//判断文件是否存在
if (temppath + "\\" == applicationpath)//如果查找到根目录还没有查找到那么则不需要在查了
{
break;
}
if (!string.isnullorwhitespace(filename))
{
qerylist.add(filename);//如果不存在那么这个就是参数 例如http://localhost:4688/webform1/2011/ (对应http://localhost:4688/webform1.aspx?xxx=2011)
}
temppath = getfileinfo(temppath, out filename, out extension);
}
if (b)//如果查找到了就把查找到的路径复制给输出或返回参数
{
inputfile = temppath + "\\" + extension;
virtualpath = "~/" + inputfile.replace(applicationpath, null);
}
if (path.getextension(extension) == ".aspx")//如果是asp.net那么则把list复制给输出参数 qerystring
{
qerystring = qerylist;
}
}
return inputfile;
}
///<summary>
/// 获取指定目录+文件是否有效
///</summary>
///<param name="inputfile">目录</param>
///<param name="filename"></param>
///<param name="extension"></param>
///<returns></returns>
private static string getfileinfo(string inputfile, out string filename, out string extension)
{
var temppath = directory.getparent(inputfile).fullname;//获取传进来目录的父目录
filename = inputfile.replace(temppath + "\\", null);//获取子目录名称
extension = path.getextension(inputfile);//获取扩展名
if (string.isnullorwhitespace(extension))//如果扩展名为null那么则认为是aspx文件
{
extension = filename + ".aspx";
}
else
{
extension = filename + extension;
}
return temppath;
}
#endregion
}
}
因为我在处理aspx页面时还是传入的重写后的路径,所有我们还有添加一个继承ihttphandlerfactory的类
代码如下:
复制代码 代码如下:
using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.web;
using system.io;
using system.web.ui;
namespace myclass
{
public class myhttphandlerfactory:ihttphandlerfactory
{
#region ihttphandlerfactory 成员
///<summary>
/// 返回实现 system.web.ihttphandler 接口的类的实例
///</summary>
///<param name="context">system.web.httpcontext 类的实例,它提供对用于为 http 请求提供服务的内部服务器对象(如 request、response、session和 server)的引用</param>
///<param name="requesttype">客户端使用的 http 数据传输方法(get 或 post)</param>
///<param name="url">所请求资源的 system.web.httprequest.rawurl</param>
///<param name="pathtranslated">所请求资源的 system.web.httprequest.physicalapplicationpath</param>
///<returns>处理请求的新的 system.web.ihttphandler 对象</returns>
public ihttphandler gethandler(httpcontext context, string requesttype, string url, string pathtranslated)
{
list<string> qerystring;
string virtualpath;
string inputfile =myhttpmodule.getinputfile(context, out virtualpath, out qerystring);//这里跟那里是一样的
object[] obj = new object[] { };
dictionary<string, string> qerystringdictionary = new dictionary<string, string>();
var receivemembers = system.web.compilation.buildmanager.getcompiledtype(virtualpath).getmember("receiveparameters");//获取访问当前页面的所有receiveparameters成员 (这个是我自己加的,就是想做成和mvc的那种模式,但可能不是很好)
system.reflection.methodinfo receiveparameters=null;
if (qerystring != null&&qerystring.count>0)//如果查找到没有参数则不去反射了
{
foreach (system.reflection.memberinfo receivemember in receivemembers)//遍历所有receiveparameters成员
{
if (receivemember.membertype == system.reflection.membertypes.method)//因为上面获取到的是成员 但我们要的是方法所有要判断下
{
system.reflection.methodinfo methodinfo = receivemember as system.reflection.methodinfo;
if (methodinfo != null)
{
var parameters = methodinfo.getparameters();//获取receiveparameters方法的所有参数
int optionalcount = parameters.count(i => i.isoptional);//获取receiveparameters参数里面有多少个可选参数
bool b = qerystring.count == parameters.length - optionalcount;
if (qerystring.count == parameters.length || b)//如果当前查询的参数或receiveparameters的所有参数-去可选择的查询参数相等
{
receiveparameters = methodinfo;//记录这个方法
int i = 0;
obj = new object[parameters.length];//记录参数值,到后面调用receiveparameters时用
for (; i < parameters.length; i++)
{
string name = parameters[i].name;//获取参数的名称
string value = string.empty;
if (qerystring.count > i)//如果receiveparameters参数没到可选参数那么则去查询的字符串
{
value = qerystring[i];
}
obj[i] = value;//把查询的字符串保存起来,到后面调用receiveparameters时用
qerystringdictionary.add(name, value);//添加到自定义的集合里面
}
break;
}
}
}
}
if (receiveparameters == null)//判断是否已经找到,如果没找到就把以前找的文件信息全部赋为重写的文件信息,也就是不存在的
{
virtualpath = context.request.path;
inputfile = context.request.physicalpath;
}
}
var temp= system.web.ui.pageparser.getcompiledpageinstance(virtualpath, inputfile, context);//编译页面
if (receiveparameters != null)//这个里面的内容其实应该写到releasehandler里面去的,但我写在这里了
{
system.web.ui.page page = (system.web.ui.page)temp;
page.init+=new eventhandler(page_init);//添加一个事件 ,//还有就是本来应该添加一个pagebase类的,那样就可以把真实的路径信息和查询参数放进去
sss = receiveparameters;
sssobj = obj;
//receiveparameters.invoke(temp, obj);
}
return temp;
}
public system.reflection.methodinfo sss { get; set; }
public object[] sssobj { get; set; }
protected void page_init(object sender, eventargs e)
{
sss.invoke(sender, sssobj);//当page执行到这里时就去调用receiveparameters方法 在这里还可以做其它的判断。。。 但不符合编程规范(我的理解)
}
///<summary>
/// 使工厂可以重用现有的处理程序实例
///</summary>
///<param name="handler">要重用的 system.web.ihttphandler 对象</param>
public void releasehandler(ihttphandler handler)
{
}
#endregion
}
}
页面代码就是多放几个方法
///<summary>
/// 一个参数的 如果需要多个则手动添加如public void receiveparameters(string name,string value)等等 这样页面编译后就会根据参数自动运行这个方法并转递参数值
///</summary>
///<param name="name">参数名称为name</param>
public void receiveparameters(string name)
{
var temp = request;
}
url的解决了,在来看看干掉试图的。。。
我只写了把事件的实体状态去掉了,然后手动去激发控件的事件,而且就是在url中写里面解决的 代码如下:
复制代码 代码如下:
protected void page_init(object sender, eventargs e)
{
sss.invoke(sender, sssobj);
page page = (page)sender;
foreach (string name in page.request.form.allkeys)//查找form里面所有的字典 其实应该取__eventargument隐藏域的
{
try
{
system.web.ui.control control = page.findcontrol(page.page.request.form[name]);//查找这个控件
if (control != null)
{
string value = page.request.form[page.posteventsourceid];
ipostbackeventhandler ip = control as ipostbackeventhandler;
if (ip != null)//能转换成ipostbackeventhandler 那么就激发它
{
ip.raisepostbackevent(value);
break;
}
ipostbackdatahandler backdatahandler = control as ipostbackdatahandler;
if (backdatahandler != null)//能转换成ipostbackdatahandler 就把__eventtarget隐藏域的值传给控件 然后激发更改事件
{
system.collections.specialized.namevaluecollection namevaluecollection=new system.collections.specialized.namevaluecollection();
namevaluecollection.add(page.request.form[control.clientid],page.request.form[control.clientid]);
backdatahandler.loadpostdata(control.clientid, namevaluecollection);
backdatahandler.raisepostdatachangedevent();
}
}
break;
}
catch
{
}
}
}
这样简单的处理就完了,
我希望各位来帮我改进改进,因为我毕竟还不太了解asp.net的处理机制。。。
下一篇: Android图片转换器代码分享