The urlMappings in asp.net2.0 is very easy to use, but unfortunately it does not support regular expressions. However, fortunately, if you use IHttpModule,
no matter what kind of request, it will go through IHttpModule first, which provides a good opportunity for URL rewriting. :
The following is an IHttpModule I wrote:
using System;
using System.Web;
public class ReWriteModule:IHttpModule
{
public ReWriteModule()
{
}
public override string ToString()
{
return this.GetType().ToString();
}
void IHttpModule.Dispose()
{
}
private static System.Xml.XmlDocument ruleDoc = null;
private static System.Xml.XmlDocument GetRuleConfig(System.Web.HttpContext app)
{
if (ruleDoc == null)
{
ruleDoc = new System.Xml.XmlDocument();
ruleDoc.Load(app.Server.MapPath("~/rule.xml"));
}
return ruleDoc;
}
public static string GetUrl(System.Web.HttpContext cxt,string path)
{
System.Xml.XmlDocument doc = GetRuleConfig(cxt);
System.Xml.XmlNodeList lst= doc.GetElementsByTagName("RewriterRule");
string pat="";
foreach (System.Xml.XmlNode nd in lst)
{
System.Xml.XmlNodeList sub = nd.ChildNodes[0].ChildNodes;
foreach(System.Xml.XmlNode chk in sub)
{
pat = "^" + chk.InnerText+"$";
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(pat, System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if(reg.IsMatch(path))
{
return reg.Replace(path, nd.ChildNodes[1].InnerText);
}
}
}
return null;
}
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += delegate(object sender, EventArgs e)
{
System.Web.HttpContext cxt = context.Context;
if (cxt.Request.ContentType != "image/pjpeg")
{
string type = cxt.Request.ContentType.ToLower();
string path = cxt.Request.Path;
string apppath = cxt.Request.ApplicationPath;
path = path.Remove(0, apppath.Length);
path = "~" + path;
string newUrl = GetUrl(cxt, path.TrimEnd().TrimStart());
if (newUrl != null)
{
cxt.Response.Filter = new ResponseFilter(cxt.Response.Filter,cxt.Request.Path);
cxt.Response.Write("Requested path:" + path);
cxt.Response.Write("<BR>");
cxt.Response.Write("Redirected destination URL: " + newUrl);
cxt.Response.Write("<BR>");
cxt.RewritePath(newUrl);
} // Used if required to handle all requests
//else
//{
// cxt.Response.Write(cxt.Request.Path + "<BR>");
// cxt.Response.Write("The resource you requested does not exist or you have no permission to access!");
// cxt.Response.Flush();
// cxt.Response.End();
//}
}
};
}
}
Since once the URL is rewritten, the Action in the original WEBFORM will change, which can easily cause: the requested resource does not exist
. What specifically? It will be clear if you DX take a look at it! ! !
All we have is this ResponseFilter, which is implemented as follows,
public class ResponseFilter:System.IO.Stream
{
public ResponseFilter(System.IO.Stream sink,string _str)
{
_sink = sink;
//
// TODO: add constructor logic here
//
this.str = _str;
}
private string str = "";
private System.IO.Stream _sink;
private long _position;
private System.Text.Encoding end=System.Text.Encoding.GetEncoding("GB18030");
private System.Text.StringBuilder oOutput = new System.Text.StringBuilder();
// The following members of Stream must be overriden.
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override long length
{
get { return 0; }
}
public override long Position
{
get { return _position; }
set { _position = value; }
}
public override long Seek(long offset, System.IO.SeekOrigin direction)
{
return _sink.Seek(offset, direction);
}
public override void SetLength(long length)
{
_sink.SetLength(length);
}
public override void Close()
{
_sink.Close();
}
public override void Flush()
{
_sink.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _sink.Read(buffer, offset, count);
}
// The Write method actually does the filtering.
public override void Write(byte[] buffer, int offset, int count)
{
string szBuffer = System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count);
string ap="action="";
int pos=-1;
if ((pos=szBuffer.IndexOf(ap) )!= -1)
{
int epos = szBuffer.IndexOf(""", pos + ap.Length+1);
if (epos != -1)
{
szBuffer= szBuffer.Remove(pos + ap.Length, epos - pos - ap.Length);
}
szBuffer = szBuffer.Insert(pos + ap.Length, this.str);
byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(szBuffer);
_sink.Write(data, 0, data.Length);
}
else
{
oOutput.Append(szBuffer);
}
//The following paragraph can be used to modify the content between <Head></head>;
//Regex oEndFile = new Regex("</head>", RegexOptions.IgnoreCase|RegexOptions.Compiled);
//if (oEndFile.IsMatch(szBuffer))
//{
// //Append the last buffer of data
// //Append the last part of the data in the buffer
// oOutput.Append(szBuffer);
// //Get back the complete response for the client
// //Transmit complete client return data
// string szCompleteBuffer = oOutput.ToString().ToLower();
// int ipos = szCompleteBuffer.IndexOf("<title>");
// int epos = szCompleteBuffer.IndexOf("</title>",ipos+7);
// string sp = szCompleteBuffer.Substring(ipos+7, epos - ipos );
// szCompleteBuffer = szCompleteBuffer.Remove(ipos+7,sp.Length-7);
// szCompleteBuffer = szCompleteBuffer.Insert(ipos + 7, "dhz");
// // szCompleteBuffer = szCompleteBuffer.Replace(sp, "dhz");
// //No match, so write out original data
// //No match, so source code is written
// byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(szCompleteBuffer);
// _sink.Write(data, 0, data.Length);
//}
//else
//{
// oOutput.Append(szBuffer);
//}
}
}
//////The waiting rules are configured as follows using xml files;
of course, you can also do it through a custom configuration section in web.config
<?xml version="1.0" encoding="utf-8" ? >
<Rules>
<RewriterRule>
<LookFors>
<LookFor>~/(d{4})/(d{2}).html</LookFor>
<LookFor>~/(d{4})/(d{2})/</LookFor>
<LookFor>~/(d{4})/(d{2})</LookFor>
<LookFor>~/(d{4})/(d{2})/index.html</LookFor>
</LookFors>
<SendTo>~/Pro.aspx?year=$1&month=$2</SendTo>
</RewriterRule>
<RewriterRule>
<LookFors>
<LookFor>~/pc</LookFor>
</LookFors>
<SendTo>~/Test2.aspx</SendTo>
</RewriterRule>
</Rules>
//This rule is not well written. For example, the first one can be done with a regular expression. But I don’t know how to write it properly at the moment. It seems that I need to use some concept of anti-capture group. I’m thinking about this stuff! !