<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>jonescheng</title>
    <description></description>
    <link>http://jonescheng.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>孙鑫的Java无难事视频学习笔记(一)</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183616" style="color:red;">http://jonescheng.javaeye.com/blog/183616</a>&nbsp;
          发表时间: 2008年04月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <font face="Verdana">
<p><font face="Verdana"><span style="font-size: 10pt"><font face="Verdana" style="font-size: 14pt"><strong>byte 是一个单字节整数，8位的有符号整数，也就是-127-127之间<br />
</strong></font></span>&nbsp;&nbsp;&nbsp;byte b;<br />
&nbsp;&nbsp; b=3; <br />
&nbsp;&nbsp; b=b*3 <span style="color: red">//这里JAVA自动进行了一次向上的类型转换，将相X的结果转换成了int类型，而将一个int赋给byte出现精度丢失<br />
</span>这里有个知识点（类型自动转换）b=b*3这里，JAVA在编译时为了保证精度，会自动对byte进行向int的</font><font face="Verdana">转换，以保证结果的精度，所以实际上b*3之后的结果是一个int类型</font><font face="Verdana">，而将一个int类型赋值给一个byte会导致精度丢失，因此这里会编译</font><font face="Verdana">不通过。</font><font face="Verdana">==&gt;因此必须b=byte(b*3)</font></p>
<p><font face="Verdana"><strong style="font-size: 14pt">short 2字节的整数16位 -32768-32767 有符号的两字节整数</strong></font></p>
<p><font face="Verdana"><strong><span style="font-size: 14pt"><font face="Verdana">int&nbsp; 4字节的有符号整数<br />
</font></span><span style="font-size: 14pt">long 8字节的有符号的整数</span></strong></font></p>
<p><font face="Verdana"><strong style="font-size: 14pt">char 两字节，无符号的0-65535 存储的时候以ASSC II码来存储</strong></font></p>
<p><font face="Verdana"><strong style="font-size: 14pt">float&nbsp;4字节的无符号浮点数</strong><br />
一般的小数常量都是double类型，比如1.3所以准确表示是1.3f</font> ,<font face="Verdana">来表示他是float类型 4字节和int的一样的长度一共32位,第一位表示</font><font face="Verdana">符号,2-9表示指数,后面23位表示小数部分。<br />
<br />
</font><font face="Verdana"><strong style="font-size: 14pt">double 和float一样，只是8位的，对应于long</strong></font></p>
<p><font face="Verdana"><strong style="font-size: 14pt">boolean 只有两个值 true与false</strong></font></p>
<p><font face="Verdana">数组&nbsp; 数组在定义的时候是不能分配空间的,所以不能写int num[3]这样的类似于C的写法。<br />
</font><font face="Verdana">这是错误的&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;int num[] = new int[3]; <span style="color: red">//这样是错误的，下面有正确的写法<br />
</span>建议的方式是<br />
&nbsp;&nbsp;&nbsp;&nbsp;int[] num;<br />
定义的时候初始化数组<br />
&nbsp;&nbsp;&nbsp;&nbsp;int[] num = {1,2,3} ;//<span style="color: red"><span style="color: #339966"><font face="Verdana">这种方式定义的初始化，只能在定义的时候使</font><font face="Verdana">用<br />
</font></span></span></font><font face="Verdana"><span style="color: #ff0000"><font face="Verdana">而下面这种方式初始化是错误的<br />
&nbsp;&nbsp;&nbsp;&nbsp;int[] num;<br />
&nbsp;&nbsp;&nbsp;&nbsp;num = {1,2,3 };&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;如果你先定义在，然后在按上面的方法进行初始化是错误的。&nbsp;&nbsp;</font></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
还有另外一种定义数组&nbsp;<br />
</font><font face="Verdana">&nbsp;&nbsp;&nbsp;&nbsp;int[] num = new int[]{1,2,3};//定义的时候new<br />
<br />
非常常用定义方法<br />
&nbsp;&nbsp;&nbsp;&nbsp;int[] num;<br />
&nbsp;&nbsp;&nbsp;&nbsp;num = new int[3]//分配三个空间<br />
&nbsp;&nbsp;&nbsp;&nbsp;num[0] = 1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;num[1] = 2;<br />
&nbsp;&nbsp;&nbsp;&nbsp;num[3] = 3; //用索引去赋值</font></p>
<p><font face="Verdana">二维数组<br />
int[][] num;<br />
num = new int[3][4];//访问方式同C操作</font></p>
</font>

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183616#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 16 Apr 2008 16:08:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183616</link>
        <guid>http://jonescheng.javaeye.com/blog/183616</guid>
      </item>
      <item>
        <title>Java开发中经常遇到ClassNoFound的异常问题</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183617" style="color:red;">http://jonescheng.javaeye.com/blog/183617</a>&nbsp;
          发表时间: 2008年04月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          1.编译源代码时，发生的问题<br />
&nbsp;&nbsp; 首先使用java -version查看，如果可以正确显示版本，表示你的jdk的路径是正确的。也就是说你的Path是正确的。<br />
&nbsp;&nbsp; 而ClassPath的设定是错误的。<br />
&nbsp;&nbsp; Path和ClassPath到底有什么区别呢？其时这个非常简单，但是也非常重要。<br />
&nbsp;&nbsp; Paht其时是一个公用的环境变量，他的作用就是为在DOS环境下执行一些命令提路径。比如：<br />
&nbsp;&nbsp; 你上面执行的java这个命令，他是在你jdk的安装目标中。。如果你指定好Path那么你只要在命令行环境下输入java，那根据你设定好的PATH，会自动定位到这个java命令。意思就是当你输入java后，系统会自动的搜索所有PATH中的目录，一旦在一个目录下找到，就运行该命令。<br />
<br />
&nbsp;&nbsp; 而ClASSPATH则是Java专用的路径，他也就是JAVA环境中一些常用的JAR文件的存放地，相当于我们开发C或者C++链接DLL存放的目录。。也就是指JAVA开发环境中常用的一些库的存放地（而JAVA会将一些库打包成JAR）<br />
<br />
&nbsp; 所以当你能执行java -version表明你的PATH设定的正确的，但是JAVA编译和执行时的一些常用库他找不到，也就是说CLASSPATH设定不正确，以致于JAVA的环境无法找到相应的库（也就是相当于你执行一个WINDOW程序时经常碰到的提示XX.dll找不到这样的，但是JAVA他不提示，他就直接给你一个异常。）<br />
<br />
2.连接数据时的一些问题<br />
&nbsp; 常见的是代码都写好了，编译也OK，结果执行还是会发生ClassNoFound的问题，其时这也是ClassPATH那里在做怪。<br />
&nbsp; 所以你不管连接那个数据库，数据库的厂商都会提供一个JDBC的JAR，来帮助你使用JAVA来连接数据库。而常发生的ClassNoFound这种异常就是因为没找到这个JAR文件，怎么办，你其时只要把他加到CLASSPATH，让JAVA在运行时能找到他就好了，加到那里其时并不象那些网上文章中说的，什么jdk里也放阿，什么TOMCAT那里也放阿什么的。。<br />
你明白原理就好了，那就是能让他出现在CLASSPATH中，比如你CLASSPATH中指定了一个目标，那么只要你把他拷贝进去就OK了，但是一般来说，这个也是有些常用的规范问题的，具体你可以参考公司的规范文档。。<br />
&nbsp; <font face="Verdana">String url = "jdbc:oracle:thin:@localhost(实例所在的机器名）:1521:oracle9i(实例名)";<br />
&nbsp;//一般都用think方式，一般Oracle和WEB服务器肯定是不在同一机器上，所有WEB服务器上肯定没安装ORACLE客户端<br />
&nbsp;&nbsp;try {<br />
&nbsp;&nbsp;&nbsp;Class.forName("oracle.jdbc.OracleDriver");<br />
&nbsp;&nbsp;&nbsp;conn = DriverManager.getConnection(url, 用户名, 密码);</font><br />
<br />
继续补充中。。。。。 

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183617#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 16 Apr 2008 13:38:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183617</link>
        <guid>http://jonescheng.javaeye.com/blog/183617</guid>
      </item>
      <item>
        <title>关于Java栈与堆的思考 (收藏于天极网）</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183618" style="color:red;">http://jonescheng.javaeye.com/blog/183618</a>&nbsp;
          发表时间: 2008年04月16日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span class="a14c" id="zoom">&nbsp;
<p style="text-indent: 2em">　　1. 栈(stack)与堆(heap)都是<a href="http://dev.21tx.com/java/" target="_blank">Java</a>用来在Ram中存放数据的地方。与<a href="http://dev.21tx.com/language/c/" target="_blank">C++</a>不同，Java自动管理栈和堆，程序员不能直接地设置栈或堆。 <br />
<br />
　　2. 栈的优势是，存取速度比堆要快，仅次于直接位于CPU中的寄存器。但缺点是，存在栈中的数据大小与生存期必须是确定的，缺乏灵活性。另外，栈数据可以共享，详见第3点。堆的优势是可以动态地分配内存大小，生存期也不必事先告诉编译器，Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是，由于要在运行时动态分配内存，存取速度较慢。<br />
<br />
　　3. Java中的数据类型有两种。<br />
<br />
　　一种是基本类型(primitive types), 共有8种，即int, short, long, byte, float, double, boolean, char(注意，并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的，称为自动变量。值得注意的是，自动变量存的是字面值，不是类的实例，即不是类的引用，这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用，指向3这个字面值。这些字面值的数据，由于大小可知，生存期可知(这些字面值固定定义在某个程序块里面，程序块退出后，字段值就消失了)，出于追求速度的原因，就存在于栈中。<br />
<br />
　　另外，栈有一个很重要的特殊性，就是存在栈中的数据可以共享。假设我们同时定义：<br />
<br />
</p><p style="text-indent: 2em">
<table class="txcode" cellspacing="0" border="0" align="center" cellpadding="0">
    <p style="text-indent: 2em">
    <tbody>
        <p style="text-indent: 2em">
        <tr>
            <p style="text-indent: 2em">
            <td>int a = 3; <br />
            int b = 3；</td>
            </p>
        </tr>
        </p>
    </tbody>
    </p>
</table>
<br />
　　编译器先处理int a = 3；首先它会在栈中创建一个变量为a的引用，然后查找有没有字面值为3的地址，没找到，就开辟一个存放3这个字面值的地址，然后将a指向3的地址。接着处理int b = 3；在创建完b的引用变量后，由于在栈中已经有3这个字面值，便将b直接指向3的地址。这样，就出现了a与b同时均指向3的情况。<br />
<br />
　　特别注意的是，这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象，如果一个对象引用变量修改了这个对象的内部状态，那么另一个对象引用变量也即刻反映出这个变化。相反，通过字面值的引用来修改其值，不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例，我们定义完a与b的值后，再令a=4；那么，b不会等于4，还是等于3。在编译器内部，遇到a=4；时，它就会重新搜索栈中是否有4的字面值，如果没有，重新开辟地址存放4的值；如果已经有了，则直接将a指向这个地址。因此a值的改变不会影响到b的值。<br />
<br />
　　另一种是包装类数据，如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中，Java用new()语句来显示地告诉编译器，在运行时才根据需要动态创建，因此比较灵活，但缺点是要占用更多的时间。 4. String是一个特殊的包装类数据。即可以用String str = new String("abc");的形式来创建，也可以用String str = "abc"；的形式来创建(作为对比，在<a href="http://dev.21tx.com/java/base/jdk/" target="_blank">JDK</a> 5.0之前，你从未见过Integer i = 3;的表达式，因为类与字面值是不能通用的，除了String。而在JDK 5.0中，这种表达式是可以的！因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程，即在Java中，一切都是对象，而对象是类的实例，全部通过new()的形式来创建。Java中的有些类，如DateFormat类，可以通过该类的getInstance()方法来返回一个新创建的类，似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例，只不过这个实例是在该类内部通过new()来创建的，而getInstance()向外部隐藏了此细节。那为什么在String str = "abc"；中，并没有通过new()来创建实例，是不是违反了上述原则？其实没有。<br />
<br />
　　5. 关于String str = "abc"的内部工作。Java内部将此语句转化为以下几个步骤：<br />
<br />
　　(1)先定义一个名为str的对String类的对象引用变量：String str；<br />
<br />
　　(2)在栈中查找有没有存放值为"abc"的地址，如果没有，则开辟一个存放字面值为"abc"的地址，接着创建一个新的String类的对象o，并将o的字符串值指向这个地址，而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址，则查找对象o，并返回o的地址。<br />
<br />
　　(3)将str指向对象o的地址。<br />
<br />
　　值得注意的是，一般String类中字符串值都是直接存值的。但像String str = "abc"；这种场合下，其字符串值却是保存了一个指向存在栈中数据的引用！<br />
<br />
　　为了更好地说明这个问题，我们可以通过以下的几个代码进行验证。<br />
<br />
</p><p style="text-indent: 2em">
<table class="txcode" cellspacing="0" border="0" align="center" cellpadding="0">
    <p style="text-indent: 2em">
    <tbody>
        <p style="text-indent: 2em">
        <tr>
            <p style="text-indent: 2em">
            <td>String str1 = "abc";<br />
            String str2 = "abc";<br />
            System.out.println(str1==str2); //true</td>
            </p>
        </tr>
        </p>
    </tbody>
    </p>
</table>
<br />
　　注意，我们这里并不用str1.equals(str2)；的方式，因为这将比较两个字符串的值是否相等。==号，根据JDK的说明，只有在两个引用都指向了同一个对象时才返回真值。而我们在这里要看的是，str1与str2是否都指向了同一个对象。<br />
结果说明，JVM创建了两个引用str1和str2，但只创建了一个对象，而且两个引用都指向了这个对象。<br />
<br />
　　我们再来更进一步，将以上代码改成：<br />
<br />
</p><p style="text-indent: 2em">
<table class="txcode" cellspacing="0" border="0" align="center" cellpadding="0">
    <p style="text-indent: 2em">
    <tbody>
        <p style="text-indent: 2em">
        <tr>
            <p style="text-indent: 2em">
            <td>String str1 = "abc";<br />
            String str2 = "abc";<br />
            str1 = "bcd";<br />
            System.out.println(str1 + "," + str2); //bcd, abc<br />
            System.out.println(str1==str2); //false</td>
            </p>
        </tr>
        </p>
    </tbody>
    </p>
</table>
<br />
　　这就是说，赋值的变化导致了类对象引用的变化，str1指向了另外一个新对象！而str2仍旧指向原来的对象。上例中，当我们将str1的值改为"bcd"时，JVM发现在栈中没有存放该值的地址，便开辟了这个地址，并创建了一个新的对象，其字符串的值指向这个地址。<br />
<br />
　　事实上，String类被设计成为不可改变(immutable)的类。如果你要改变其值，可以，但JVM在运行时根据新值悄悄创建了一个新对象，然后将这个对象的地址返回给原来类的引用。这个创建过程虽说是完全自动进行的，但它毕竟占用了更多的时间。在对时间要求比较敏感的环境中，会带有一定的不良影响。<br />
<br />
　　再修改原来代码：<br />
<br />
</p><p style="text-indent: 2em">
<table class="txcode" cellspacing="0" border="0" align="center" cellpadding="0">
    <p style="text-indent: 2em">
    <tbody>
        <p style="text-indent: 2em">
        <tr>
            <p style="text-indent: 2em">
            <td>String str1 = "abc";<br />
            String str2 = "abc";<br />
            <br />
            str1 = "bcd";<br />
            <br />
            String str3 = str1;<br />
            System.out.println(str3); //bcd<br />
            <br />
            String str4 = "bcd";<br />
            System.out.println(str1 == str4); //true</td>
            </p>
        </tr>
        </p>
    </tbody>
    </p>
</table>
<br />
　　str3这个对象的引用直接指向str1所指向的对象(注意，str3并没有创建新对象)。当str1改完其值后，再创建一个String的引用str4，并指向因str1修改值而创建的新的对象。可以发现，这回str4也没有创建新的对象，从而再次实现栈中数据的共享。<br />
<br />
　　我们再接着看以下的代码。<br />
<br />
</p><p style="text-indent: 2em">
<table class="txcode" cellspacing="0" border="0" align="center" cellpadding="0">
    <p style="text-indent: 2em">
    <tbody>
        <p style="text-indent: 2em">
        <tr>
            <p style="text-indent: 2em">
            <td>String str1 = new String("abc");<br />
            String str2 = "abc";<br />
            System.out.println(str1==str2); //false</td>
            </p>
        </tr>
        </p>
    </tbody>
    </p>
</table>
<br />
　　创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。<br />
<br />
</p><p style="text-indent: 2em">
<table class="txcode" cellspacing="0" border="0" align="center" cellpadding="0">
    <p style="text-indent: 2em">
    <tbody>
        <p style="text-indent: 2em">
        <tr>
            <p style="text-indent: 2em">
            <td>String str1 = "abc";<br />
            String str2 = new String("abc");<br />
            System.out.println(str1==str2); //false</td>
            </p>
        </tr>
        </p>
    </tbody>
    </p>
</table>
<br />
　　创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。<br />
<br />
　　以上两段代码说明，只要是用new()来新建对象的，都会在堆中创建，而且其字符串是单独存值的，即使与栈中的数据相同，也不会与栈中的数据共享。<br />
<br />
　　6. 数据类型包装类的值不可修改。不仅仅是String类的值不可修改，所有的数据类型包装类都不能更改其内部的值。 7. 结论与建议：<br />
<br />
　　(1)我们在使用诸如String str = "abc"；的格式定义类时，总是想当然地认为，我们创建了String类的对象str。担心陷阱！对象可能并没有被创建！唯一可以肯定的是，指向String类的引用被创建了。至于这个引用到底是否指向了一个新的对象，必须根据上下文来考虑，除非你通过new()方法来显要地创建一个新的对象。因此，更为准确的说法是，我们创建了一个指向String类的对象的引用变量str，这个对象引用变量指向了某个值为"abc"的String类。清醒地认识到这一点对排除程序中难以发现的bug是很有帮助的。<br />
<br />
　　(2)使用String str = "abc"；的方式，可以在一定程度上提高程序的运行速度，因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc")；的代码，则一概在堆中创建新对象，而不管其字符串值是否相等，是否有必要创建新对象，从而加重了程序的负担。这个思想应该是享元模式的思想，但JDK的内部在这里实现是否应用了这个模式，不得而知。<br />
<br />
　　(3)当比较包装类里面的数值是否相等时，用equals()方法；当测试两个包装类的引用是否指向同一个对象时，用==。<br />
<br />
　　(4)由于String类的immutable性质，当String变量需要经常变换其值时，应该考虑使用StringBuffer类，以提高程序效率。<br />
</p></span>

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183618#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 16 Apr 2008 12:40:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183618</link>
        <guid>http://jonescheng.javaeye.com/blog/183618</guid>
      </item>
      <item>
        <title>VS2003中自定义代码模板</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183619" style="color:red;">http://jonescheng.javaeye.com/blog/183619</a>&nbsp;
          发表时间: 2008年04月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <font face="Verdana">VS2003中的模板文件存放在X:<font face="Verdana">Microsoft Visual Studio .NET 2003\VC#\VC#Wizards和X<font face="Verdana">:\Microsoft Visual Studio .NET 2003\VC#\DesignerTemplates\2052<br />
</font></font>在这X:<font face="Verdana">Microsoft Visual Studio .NET 2003\VC#</font>有所有的模板文件，文件名很清楚的表明了他的用途<br />
<br />
/************************************************************<br />
&nbsp; Copyright (C) 2008&nbsp;MyCompany Software Design Center<br />
&nbsp; Program Name&nbsp;: $classname$.cs<br />
&nbsp; Create by&nbsp;&nbsp;: Andrew Cheng<br />
&nbsp; Create Date&nbsp;: MM/DD/YYYY<br />
&nbsp; Description&nbsp;: 在这里填写类描术<br />
&nbsp; Maintain Log&nbsp;: // 历史修改记录<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;author&gt;&nbsp;&nbsp;&lt;time&gt;&nbsp;&nbsp; &lt;version&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;desc&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; andrew&nbsp;&nbsp;&nbsp; MM/DD/YYYY&nbsp;&nbsp; 1.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; build this program<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
************************************************************/</font>
<p><font face="Verdana"></font>&nbsp;</p>

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183619#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Apr 2008 14:36:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183619</link>
        <guid>http://jonescheng.javaeye.com/blog/183619</guid>
      </item>
      <item>
        <title>Oracle 动态SQL学习笔记</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183620" style="color:red;">http://jonescheng.javaeye.com/blog/183620</a>&nbsp;
          发表时间: 2008年04月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          function open_cursor:打开一个动态游标,并返回一个整型;<br />
<br />
procedure close_cursor(c in out integer);关闭一个动态游标,参数为open_cursor所打开的游标;<br />
<br />
procedure parse(c in integer, statement in varchar2, language_flag in integer):对动态游标所提供的sql语句进行解析,参数C表示游标,statement为sql语句,language-flag为解析sql语句所用oracle版本,一般有V6,V7跟native(在不明白所连database版本时,使用native);<br />
<br />
procedure define_column(c in integer, position in integer, column any datatype, [column_size in integer]):定义动态游标所能得到的对应值,其中c为动态游标,positon为对应动态sql中的位置(从1开始),column为该值所对应的变量,可以为任何类型,column_size只有在column为定义长度的类型中使用如VARCHAR2,CHAR等(该过程有很多种情况,此处只对一般使用到的类型进行表述);<br />
<br />
function execute(c in integer):执行游标,并返回处理一个整型,代表处理结果(对insert,delete,update才有意义,而对select语句而言可以忽略);<br />
<br />
function fetch_rows(c in integer):对游标进行循环取数据,并返回一个整数,为0时表示已经取到游标末端;<br />
<br />
procedure column_value(c in integer, position in integer, value):将所取得的游标数据赋值到相应的变量,c为游标,position为位置,value则为对应的变量;<br />
<br />
procedure bind_variable(c in integer, name in varchar2, value):定义动态sql语句(DML)中所对应字段的值,c为游标,name为字段名称,value为字段的值;<br />
<br />
以上是在程序中经常使用到的几个函数及过程,其他函数及过程请参照oracle所提供定义语句dbmssql.sql<br />
<br />
(二)一般过程<br />
对于一般的select操作,如果使用动态的sql语句则需要进行以下几个步骤:<br />
open cursor---&gt;parse---&gt;define column---&gt;excute---&gt;fetch rows---&gt;close cursor;<br />
而对于dml操作(insert,update)则需要进行以下几个步骤:<br />
open cursor---&gt;parse---&gt;bind variable---&gt;execute---&gt;close cursor;<br />
对于delete操作只需要进行以下几个步骤:<br />
open cursor---&gt;parse---&gt;execute---&gt;close cursor;<br />
<br />
<br />
实例分析<br />
<strong><span style="font-size: 8pt; color: black; font-family: 'Courier New'">create</span></strong><span style="font-size: 8pt; color: black; font-family: 'Courier New'"> <strong>or</strong> <strong>replace</strong> <strong>procedure</strong> p_oneproduc <strong>is<br />
--变量定义<br />
<span style="font-size: 8pt; color: black; font-family: 'Courier New'">iid <strong>int</strong>;<br />
icount <strong>int</strong>;<br />
strtblname <strong>varchar2</strong>(</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">100</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">);<br />
ssql <strong>varchar2</strong>(</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">1024</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">);<br />
serrmsg <strong>varchar2</strong>(</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">1024</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">);<br />
cid1 <strong>number</strong>;--动态游标光标编号<br />
cid2 number;--动态光标编号<br />
iipno <strong>number</strong>;--从动态光标中取出值的存放变量<br />
idateno <strong>number</strong>;--从动态光标中取出值的存放变量<br />
i <strong>number</strong>;<br />
<strong>cursor</strong> cur1 <strong>is</strong> <strong>select</strong> id,table_name <strong>from</strong> userisit <strong>where</strong> status=</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">2</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">;--定义一个光标<br />
--定义结束<br />
<strong><span style="font-size: 8pt; color: black; font-family: 'Courier New'">begin</span></strong><span style="font-size: 8pt; color: black; font-family: 'Courier New'"><br />
&nbsp;<strong>select</strong> <strong>count</strong>(*) <strong>into</strong> icount <strong>from</strong> uservisit <strong>where</strong> status=</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">2</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">;//查询用户访问表将记录汇总统计存入icount中<br />
&nbsp;<strong>if</strong> icount&gt;</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">0</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'"> <strong>then</strong>&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;<strong>open</strong> cur1;//打开cur1光标<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>fetch</strong> cur1 <strong>into</strong> iid,strtblname; //取出光标的值并赋值给iid,strtblname，相应的变量和select变量同序<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>exit</strong> <strong>when</strong> cur1%<strong>notfound</strong>; //如果记录为空时退出&nbsp;cur%notfound是cur的一个变量，当记录为空时为true<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">--重头戏，，动态SQL开始(具体可以参考前面的方法说明)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 8pt; color: black; font-family: 'Courier New'">ssql:=</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">'select distinct ipno,dateno from '</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">||strtblname; --定义一个SQL语句，后面用动态SQL进行执行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cid1:=dbms_sql.open_cursor; --定义一个动态光标用来执行前面定义的SQL语句<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_sql.Parse(cid1,ssql,dbms_sql.v7); --分析SQL语句<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_sql.Define_Column(cid1,</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">1</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">,iipno); --定义要取出的字段值,1表示第一个字段要取出，即Select语句的ipno<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_sql.Define_Column(cid1,</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">2</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">,idateno);--同上说明<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; icount:=dbms_sql.<strong>execute</strong>(cid1); --执行Sql语句，这里icount得到一个执行的结果<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; --执行SQL语句可以和普通光标一样进行取值操作<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 8pt; color: black; font-family: 'Courier New'">i:=</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">0</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>loop</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>If</strong> dbms_sql.fetch_rows(cid1) &gt; </span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">0</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'"> <strong>then&nbsp;--如果动态光标的记录数大于0，则进行取值操作</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>begin</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_sql.column_value(cid1,</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">1</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">,iipno); --取出值<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dbms_sql.column_value(cid1,</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">2</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">,idateno);--同上<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>select</strong> <strong>count</strong>(*) <strong>into</strong> icount <strong>from</strong> pmhtmpunchkcpc@unionbill <strong>where</strong> ipno=iipno <strong>and</strong> dateno=idateno;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>if</strong> icount&gt;</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">0</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'"> <strong>then</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ssql:=</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">'update FIRST<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#67;&#68;&#65;&#89;&#95;&#39;&#124;&#124;&#105;&#100;&#97;&#116;&#101;&#110;&#111;&#124;&#124;&#39;&#64;&#117;&#110;&#105;&#111;&#110;&#98;&#105;&#108;&#108;">CDAY_'</a></span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">||idateno||</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">'@unionbill set isreach=1 where ipno='</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">||iipno;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>execute</strong> <strong>immediate</strong> ssql;//动态立即执行一个SQL语句<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end if<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end if<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end loop<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'"><strong>exception</strong> <strong>when</strong> <strong>others</strong> <strong>then</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>null</strong>;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>end</strong>;<br />
&nbsp;&nbsp; --运用动态光标执行一组插入操作 （纯碎是为了记录动态插入中的赋值的用法，无逻辑可言）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 8pt; color: black; font-family: 'Courier New'">ssql:= 'insert uservisit(</span><span style="font-size: 8pt; color: #0000f0; font-family: 'Courier New'">ipno,dateno) values(:iipno,:idateno)'</span><span style="font-size: 8pt; color: black; font-family: 'Courier New'">; --定义一个SQL语句，后面用动态SQL进行执行<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cid2:=dbms_sql.open_cursor; --定义一个动态光标用来执行前面定义的SQL语句<br />
&nbsp;&nbsp;&nbsp;&nbsp; dbms_sql.Parse(cid2,ssql,v7);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for j in 1..999 loop<br />
&nbsp; &nbsp;&nbsp; &nbsp;dbms_sql.bind_variable(cid2, 'ipno', j);<br />
&nbsp; &nbsp;&nbsp; &nbsp;dbms_sql.bind_variable(cid2, 'dateno', 2);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;icount := dbms_sql.execute(cursor2);--插入数据<br />
&nbsp; &nbsp; end loop;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
</span>&nbsp;&nbsp;<br />
</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--关闭光标的不要忘记了<br />
&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 8pt; color: black; font-family: 'Courier New'">&nbsp;<strong>if</strong>(dbms_sql.is_open(cid1)) <strong>then --如果动态光标仍然是开的</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dbms_sql.close_cursor(cid1); --关闭<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>end</strong> <strong>if</strong>;<br />
&nbsp;&nbsp;&nbsp;&nbsp; ---记得关掉第二个动态光标<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>if</strong> cur1%<strong>isopen</strong> <strong>then</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>close</strong> cur1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>end</strong> <strong>if</strong>;<br />
<br />
</span><br />
<br />
</span><br />
</span><br />
<br />
<br />
<br />
<br />
</span><br />
</strong><br />
<br />
</span><br />

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183620#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Apr 2008 11:30:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183620</link>
        <guid>http://jonescheng.javaeye.com/blog/183620</guid>
      </item>
      <item>
        <title>在 ASP.NET 中执行 URL 重写(收藏于MSN）</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183621" style="color:red;">http://jonescheng.javaeye.com/blog/183621</a>&nbsp;
          发表时间: 2008年04月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div xmlns:msxsl="urn:schemas-microsoft-com:xslt" class="title">在 ASP.NET 中执行 URL 重写 </div>
<!--content type: DocStudio. Transform: psdk2mtps.xslt.-->
<div id="mainSection">
<div id="mainBody">
<div>
<div>发布日期 : 8/23/2004<span> | </span>更新日期 : 8/23/2004</div>
<p>Scott Mitchell</p>
<p>4GuysFromRolla.com</p>
<p>适用范围：</p>
<p>Microsoft&#174; ASP.NET</p>
<p><strong>摘要</strong>：介绍如何使用 Microsoft ASP.NET 执行动态 URL 重写。URL 重写是截取传入 Web 请求并自动将请求重定向到其他 URL 的过程。讨论实现 URL 重写的各种技术，并介绍执行 URL 重写的一些实际情况。</p>
<p><a href="http://download.microsoft.com/download/0/4/6/0463611e-a3f9-490d-a08c-877a83b797cf/MSDNURLRewriting.msi" id="ctl00_rs1_mainContentContainer_ctl01" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl01',this);">下载本文的源代码</a>。</p>
<h5>本页内容</h5>
<p><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection122121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="引言" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection122121120120">引言</a> <br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection123121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="URL 重写的常见用法" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection123121120120">URL 重写的常见用法</a> <br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection124121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="请求到达 IIS 时将会发生什么情况" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection124121120120">请求到达 IIS 时将会发生什么情况</a> <br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection125121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="实现 URL 重写 " /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection125121120120">实现 URL 重写 </a><br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection126121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="构建 URL 重写引擎" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection126121120120">构建 URL 重写引擎</a> <br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection127121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="使用 URL 重写引擎执行简单的 URL 重写" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection127121120120">使用 URL 重写引擎执行简单的 URL 重写</a> <br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection128121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="创建真正&#8220;可删节&#8221;的 URL" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection128121120120">创建真正&#8220;可删节&#8221;的 URL</a> <br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection129121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="结论" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection129121120120">结论</a> <br />
<a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection130121120120"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_down(zh-cn,MSDN.10).gif" border="0" alt="参考资料" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#XSLTsection130121120120">参考资料</a> <br />
</p>
<h3 id="XSLTsection122121120120">引言</h3>
<p>让我们花点时间来看一下网站上的一些 URL。您是否发现一些类似于 http://yoursite.com/info/dispEmployeeInfo.aspx?EmpID=459-099&amp;type=summary 的 URL？或者，您可能将一系列网页从一个目录或网站移动到另一个目录或网站，结果导致已将旧 URL 用作书签的访问者断开链接。在本文中，我们将了解如何通过将 http://yoursite.com/info/dispEmployeeInfo.aspx?EmpID=459-099&amp;type=summary 替换为类似于 http://yoursite.com/people/sales/chuck.smith 的网址，使用 URL 重写将那些冗长的 URL 缩写为富有意义且容易记忆的 URL。我们还将了解如何将 URL 重写用于创建智能 404 错误。</p>
<p>URL 重写是截取传入 Web 请求并自动将请求重定向到其他资源的过程。执行 URL 重写时，通常会检查被请求的 URL，并基于 URL 的值将请求重定向到其他 URL。例如，在进行网站重组而将 /people/ 目录下的所有网页移动到 /info/employees/ 目录中时，您可能希望使用 URL 重写来检查 Web 请求是否指向了 /people/ 目录中的文件。如果请求指向 /people/ 目录中的文件，您可能希望自动将请求重定向到 /info/employees/ 目录中的同一文件。</p>
<p>使用传统的 ASP，应用 URL 重写的唯一方法是编写 ISAPI 筛选器，或者购买提供 URL 重写功能的第三方产品。但是，使用 Microsoft&#174; ASP.NET，您可以通过很多方法来轻松地创建您自己的 URL 重写软件。本文讨论了可供 ASP.NET 开发人员实现 URL 重写的各种技术，然后讨论了 URL 重写的一些实际使用情况。在深入讨论 URL 重写的技术细节之前，让我们先看一些可以使用 URL 重写的日常情景。</p>
<div><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_up(zh-cn,MSDN.10).gif" border="0" alt="" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection">返回页首</a> <br />
</div>
<h3 id="XSLTsection123121120120">URL 重写的常见用法</h3>
<p>创建数据驱动的 ASP.NET 网站时，通常会产生一个单个的网页，该网页基于查询字符串参数显示数据库数据的子集。例如，在设计电子商务站点时，您的任务之一便是允许用户浏览待售产品。为此，您可以创建一个名为 displayCategory.aspx 的页面，该页面将显示给定类别的产品。可以通过查询字符串参数来指定要查看的该类别的产品。也就是说，如果用户要浏览待售的 Widget 产品，并且所有 Widget 产品的 CategoryID 均为 5，则用户可以访问以下网址：http://yousite.com/displayCategory.aspx?CategoryID=5。</p>
<p>创建具有此类 URL 的网站有两点不足：首先，从最终用户的角度考虑，URL http://yousite.com/displayCategory.aspx?CategoryID=5 比较杂乱。可用性专家 <a href="http://useit.com/" id="ctl00_rs1_mainContentContainer_ctl02" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl02',this);">Jakob Neilsen</a> <a href="http://www.useit.com/alertbox/990321.html" id="ctl00_rs1_mainContentContainer_ctl03" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl03',this);">建议</a>遵循以下标准来选择 URL： </p>
<ul>
    <li>
    <p>简短。 </p>
    </li><li>
    <p>易于键入。 </p>
    </li><li>
    <p>可以看出站点的结构。 </p>
    <li>
    <p>&#8220;可删节&#8221;，允许用户通过删除 URL 的组成部分来浏览站点。 </p>
    </li>
</li></ul>
<p>我还要增加一条标准，即，URL 应该便于记忆。URL http://yousite.com/displayCategory.aspx?CategoryID=5 不符合 Neilsen 的任何标准，也不容易记住。要求用户键入查询字符串值将使 URL 的键入变得非常困难，并且只有了解查询字符串参数的用途及其名称/值对结构的富有经验的 Web 开发人员才能够对 URL 进行&#8220;删节&#8221;。 </p>
<p>较好的方法是允许使用切合实际且容易记忆的 URL，如 http://yoursite.com/products/Widgets。只要看一眼 URL，您便可以推断出将要显示的内容 -- 有关 Widget 的信息。此 URL 也很容易记住和共享。我可以告诉我的同事&#8220;请查看 yoursite.com/products/Widgets，&#8221;，她可能无需再次问我 URL 是什么即可打开该页面。（尝试一下，您只需说出&#8220;Amazon.com 页面&#8221;即可！）此 URL 还将显示出来，并且应该是&#8220;可删节&#8221;的。也就是说，如果用户删去 URL 的末端，键入 http://yoursite.com/products，他们应该看到所有产品的列表，或者至少应该看到他们可以查看的所有类别的产品列表。</p>
<p><strong>注意</strong>：要获得&#8220;可删节&#8221;URL 的最好示例，可考虑使用由许多 blog 引擎生成的 URL。要查看 2004 年 1 月 28 日的帖子，用户可以访问诸如 http://someblog.com/2004/01/28 的 URL。如果该 URL 被删节为 http://someblog.com/2004/01，用户将看到 2004 年 1 月的所有帖子。将该 URL 进一步删节为 http://someblog.com/2004 将显示 2004 年的所有帖子。</p>
<p>除了简化 URL 之外，URL 重写还经常用于处理网站重组，以免导致大量链接断开或书签过期。</p>
<div><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_up(zh-cn,MSDN.10).gif" border="0" alt="" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection">返回页首</a> <br />
</div>
<h3 id="XSLTsection124121120120">请求到达 IIS 时将会发生什么情况</h3>
<p>在正式研究 URL 如何实现重写之前，应首先了解 Microsoft&#174; Internet Information Services (IIS) 如何处理传入请求，这一点非常重要。当请求到达 IIS Web 服务器时，IIS 检查被请求文件的扩展名以确定如何处理该请求。IIS 可以自行处理请求（如 HTML 页面、图像以及其他静态内容），或者将请求路由到 ISAPI 扩展。（ISAPI 扩展是一个处理传入 Web 请求的非托管编译类。其任务是生成被请求资源的内容。）</p>
<p>例如，当传入针对 Info.asp 网页的请求时，IIS 会将此消息路由到 asp.dll ISAPI 扩展。然后，该 ISAPI 扩展将加载被请求的 ASP 页面，执行该页面，并将所呈现的 HTML 返回给 IIS，然后，IIS 将该 HTML 发送回请求客户端。对于 ASP.NET 页面，IIS 会将此消息路由到 aspnet_isapi.dll ISAPI 扩展。然后，aspnet_isapi.dll ISAPI 扩展将处理操作传递给托管的 ASP.NET 辅助进程，该辅助程序将处理请求，并返回 ASP.NET 网页的呈现 HTML。</p>
<p>您可以自定义 IIS，以指定扩展名与 ISAPI 扩展的映射关系。图 1 显示了 Internet Information Services 管理工具的&#8220;应用程序配置&#8221;对话框。请注意，与 ASP.NET 有关的扩展名（.aspx、ascx、config、asmx、rem、cs、vb 及其他）均已映射到 aspnet_isapi.dll ISAPI 扩展。</p>
<p><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.urlrewriting_fig01(zh-cn,MSDN.10).gif" alt="" /> </p>
<div><strong>图</strong> <strong>1.</strong> 已配置的文件扩展名映射</div>
<p>讨论 IIS 如何管理传入请求稍稍超出了本文范围。但是可以在 Michele Leroux Bustamante 的文章 <a href="http://www.theserverside.net/articles/showarticle.tss?id=IIS_ASP" id="ctl00_rs1_mainContentContainer_ctl04" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl04',this);">Inside IIS and ASP.NET</a> 中找到对此内容的深入讨论。ASP.NET 引擎仅处理那些扩展名已明确映射至 IIS 中的 aspnet_isapi.dll 的传入 Web 请求，了解这一点非常重要。</p>
<h4>使用 ISAPI 筛选器检查请求</h4>
<p>IIS 除了可以将传入 Web 请求的文件扩展名映射到相应的 ISAPI 扩展之外，还将执行许多其他任务。例如，IIS 将尝试对发出请求的用户进行身份验证，并确定通过身份验证的用户是否有权限访问被请求的文件。在处理请求的有效期内，IIS 将经历几个状态。在每个状态下，IIS 都将引发可以使用 ISAPI 筛选器以编程方式进行处理的事件。</p>
<p>与 ISAPI 扩展一样，ISAPI 筛选器是在 Web 服务器上安装的非托管代码块。ISAPI 扩展被设计为可以响应针对特定文件类型的请求。另一方面，ISAPI 筛选器还包含可以对 IIS 引发的事件进行响应的代码。ISAPI 筛选器可以截取甚至修改传入和传出的数据。ISAPI 筛选器可以应用于很多方面，包括： </p>
<ul>
    <li>
    <p>身份验证和授权。 </p>
    </li><li>
    <p>记录和监视。 </p>
    </li><li>
    <p>HTTP 压缩。 </p>
    <li>
    <p>URL 重写。 </p>
    </li>
</li></ul>
<p>虽然 ISAPI 筛选器可用于执行 URL 重写，但本文将讨论如何使用 ASP.NET 实现 URL 重写。不过，我们将对使用 ISAPI 筛选器与使用 ASP.NET 中的技术实现 URL 重写进行权衡。</p>
<h4>请求进入 ASP.NET 引擎时将会发生什么情况</h4>
<p>在 ASP.NET 之前，需要使用 ISAPI 筛选器来实现 IIS Web 服务器上的 URL 重写。由于 ASP.NET 引擎与 IIS 非常相似，因此可以使用 ASP.NET 进行 URL 重写。存在相似之处的原因在于 ASP.NET 引擎可以实现以下功能： </p>
<ul>
    <li>
    <p>在处理请求时可以引发事件。 </p>
    </li><li>
    <p>允许任意数量的 HTTP 模块处理所引发的事件，这与 IIS 的 ISAPI 筛选器相似。 </p>
    <li>
    <p>将呈现被请求资源这项任务委托给 HTTP 处理程序，该处理程序与 IIS 的 ISAPI 扩展相似。 </p>
    </li>
</li></ul>
<p>与 IIS 一样，ASP.NET 引擎在请求的有效期内将会触发事件，通过发信号来表示其处理过程从一个状态改变为了另一个状态。例如，当 ASP.NET 引擎首次响应请求时，<strong>BeginRequest</strong> 事件将被触发。接下来触发的是 <strong>AuthenticateRequest</strong> 事件，该事件在已建立用户标识时出现。（此外，还有大量的其他事件：<strong>AuthorizeRequest</strong>、<strong>ResolveRequestCache</strong> 和 <strong>EndRequest</strong>，等等。这些事件属于 <strong>System.Web.HttpApplication</strong> 类；有关详细信息，请参阅位于以下网址的技术文档：<a href="http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemWebHttpApplicationClassTopic.asp" id="ctl00_rs1_mainContentContainer_ctl05" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl05',this);">HttpApplication Class Overview</a>。）</p>
<p>正如上一部分所讨论的，可以创建 ISAPI 筛选器以响应 IIS 引发的事件。同样，ASP.NET 提供了 HTTP 模块，该模块可以响应由 ASP.NET 引擎引发的事件。可以将 ASP.NET Web 应用程序配置为具有多个 HTTP 模块。对于由 ASP.NET 引擎处理的每个请求，将初始化每个已配置的 HTTP 模块，并允许将事件处理程序绑定到处理请求期间所引发的事件。请注意，对每个请求均使用了许多内置 HTTP 模块。其中的一个内置 HTTP 模块是 <strong>FormsAuthenticationModule</strong>，该模块首先检查是否使用了窗体身份验证，如果使用，将检查是否对用户进行了身份验证。如果没有使用，会自动将用户重定向到指定的登录页面。</p>
<p>如上所述，通过使用 IIS，传入请求将最终发送给 ISAPI 扩展，而 ISAPI 扩展的任务是返回特定请求的数据。例如，在请求传统的 ASP 网页时，IIS 将请求传递给 asp.dll ISAPI 扩展，该扩展的任务是返回被请求的 ASP 页面的 HTML 标记。ASP.NET 引擎使用相似的方法。初始化 HTTP 模块后，ASP.NET 引擎的下一项任务是确定应由哪个 HTTP 处理程序来处理请求。 </p>
<p>所有通过 ASP.NET 引擎传递的请求最终都将到达 HTTP 处理程序或 HTTP 处理程序工厂（HTTP 处理程序工厂仅返回 HTTP 处理程序的实例，然后使用该实例来处理请求）。最终的 HTTP 处理程序将返回响应，即呈现被请求的资源。此响应将被发送回 IIS，然后 IIS 将响应返回给提出请求的用户。</p>
<p>ASP.NET 包括许多内置的 HTTP 处理程序。例如，<strong>PageHandlerFactory</strong> 用于呈现 ASP.NET 网页。<strong>WebServiceHandlerFactory</strong> 用于呈现 ASP.NET Web 服务的响应 SOAP 信封。<strong>TraceHandler</strong> 将向 <strong>trace.axd</strong> 呈现请求的 HTML 标记。</p>
<p>图 2 描述了如何处理对 ASP.NET 资源的请求。首先，IIS 接收到请求，并将请求调度给 aspnet_isapi.dll。接下来，ASP.NET 引擎对已配置的 HTTP 模块进行初始化。最后将调用正确的 HTTP 处理程序，并呈现被请求的资源，将所生成的标记返回给 IIS 和请求客户端。</p>
<p><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.urlrewriting_fig02(zh-cn,MSDN.10).gif" alt="" /> </p>
<div><strong>图</strong> <strong>2. </strong>IIS 和 ASP.NET 正在处理请求</div>
<h4>创建和注册自定义 HTTP 模块和 HTTP 处理程序</h4>
<p>创建自定义 HTTP 模块和 HTTP 处理程序是相对简单的任务，包括创建实现正确接口的托管类。HTTP 模块必须实现 <strong>System.Web.IHttpModule</strong> 接口，而 HTTP 处理程序和 HTTP 处理程序工厂必须分别实现 <strong>System.Web.IHttpHandler</strong> 接口和 <strong>System.Web.IHttpHandlerFactory</strong> 接口。创建 HTTP 处理程序和 HTTP 模块的细节超出了本文的范围。要获得详细的背景知识，请阅读 Mansoor Ahmed Siddiqui 的文章 <a href="http://www.15seconds.com/issue/020417.htm" id="ctl00_rs1_mainContentContainer_ctl06" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl06',this);">HTTP Handlers and HTTP Modules in ASP.NET</a>。</p>
<p>创建了自定义 HTTP 模块或 HTTP 处理程序之后，必须将其注册到 Web 应用程序。为整个 Web 服务器注册 HTTP 模块和 HTTP 处理程序仅需在 machine.config 文件中进行简单的添加即可；而为特定 Web 应用程序注册 HTTP 模块或 HTTP 处理程序包括向应用程序的 Web.config 文件中添加几行 XML。</p>
<p>特别要说明的是，要将 HTTP 模块添加到 Web 应用程序，应在 Web.config 的 configuration/system.web 部分添加以下几行：</p>
<pre>&lt;httpModules&gt;
&lt;add type="type" name="name" /&gt;
&lt;/httpModules&gt;
</pre>
<p><em>type</em> 值提供了 HTTP 模块的程序集和类名称，而 <em>name</em> 值提供了友好名称，可以在 Global.asax 文件中使用此友好名称来引用 HTTP 模块。</p>
<p>Web.config 的 configuration/system.web 部分中的 &lt;httpHandlers&gt; 标记对 HTTP 处理程序和 HTTP 处理程序工厂进行了配置，如下所示：</p>
<pre>&lt;httpHandlers&gt;
&lt;add verb="verb" path="path" type="type" /&gt;
&lt;/httpHandlers&gt;
</pre>
<p>如上所述，对于每个传入请求，ASP.NET 引擎将确定应使用哪个 HTTP 处理程序来呈现请求。此决定是基于传入请求的动词和路径做出的。动词将指定所作出的 HTTP 请求的类型（GET 或 POST），而路径将指定被请求文件的位置和文件名。因此，如果我们希望 HTTP 处理程序处理对扩展名为 .scott 的文件的所有请求（GET 或 POST），可以在 Web.config 文件中添加下面几行：</p>
<pre>&lt;httpHandlers&gt;
&lt;add verb="*" path="*.scott" type="type" /&gt;
&lt;/httpHandlers&gt;
</pre>
<p>其中，type 是 HTTP 处理程序的类型。</p>
<p><strong>注意</strong>：注册 HTTP 处理程序时，应确保 HTTP 处理程序使用的扩展名已从 IIS 映射到 ASP.NET 引擎，这一点非常重要。也就是说，在本 .scott 示例中，如果 .scott 扩展名没有从 IIS 映射到 aspnet_isapi.dll ISAPI 扩展，则对文件 foo.scott 的请求将导致 IIS 试图返回文件 foo.scott 的内容。为了使 HTTP 处理程序可以处理此请求，必须将 .scott 扩展名映射到 ASP.NET 引擎。然后，ASP.NET 引擎将把请求正确地路由到相应的 HTTP 处理程序。</p>
<p>有关注册 HTTP 模块和 HTTP 处理程序的详细信息，请务必参考 <a href="http://msdn.microsoft.com/library/en-us/cpgenref/html/gngrfHttpmodulesSection.asp" id="ctl00_rs1_mainContentContainer_ctl07" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl07',this);">&lt;httpModules&gt; element documentation</a> 和 <a href="http://msdn.microsoft.com/library/en-us/cpgenref/html/gngrfHttphandlersSection.asp" id="ctl00_rs1_mainContentContainer_ctl08" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl08',this);">&lt;httpHandlers&gt; element documentation</a>。</p>
<div><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_up(zh-cn,MSDN.10).gif" border="0" alt="" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection">返回页首</a> <br />
</div>
<h3 id="XSLTsection125121120120">实现 URL 重写 </h3>
<p>可以使用 ISAPI 筛选器在 IIS Web 服务器级别实现 URL 重写，也可以使用 HTTP 模块或 HTTP 处理程序在 ASP.NET 级别实现 URL 重写。本文重点介绍如何使用 ASP.NET 实现 URL 重写，因此我们将不对使用 ISAPI 筛选器实现 URL 重写的细节进行深入探讨。但是，有大量的第三方 ISAPI 筛选器可用于 URL 重写，例如： </p>
<ul>
    <li>
    <p><a href="http://www.isapirewrite.com/" id="ctl00_rs1_mainContentContainer_ctl09" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl09',this);">ISAPI Rewrite </a></p>
    </li><li>
    <p><a href="http://www.qwerksoft.com/products/iisrewrite/" id="ctl00_rs1_mainContentContainer_ctl10" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl10',this);">IIS Rewrite </a></p>
    </li><li>
    <p><a href="http://port80software.com/products/pagexchanger/" id="ctl00_rs1_mainContentContainer_ctl11" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl11',this);">PageXChanger </a></p>
    <li>
    <p>还有许多其他的筛选器！ </p>
    </li>
</li></ul>
<p>通过 <strong>System.Web.HttpContext</strong> 类的 <strong>RewritePath()</strong> 方法，可以在 ASP.NET 级别实现 URL 重写。<strong>HttpContext</strong> 类包含有关特定 HTTP 请求的 HTTP 特定信息。对于 ASP.NET 引擎收到的每个请求，均为该请求创建一个 <strong>HttpContext</strong> 实例。此类具有如下属性：<strong>Request</strong> 和 <strong>Response</strong>，提供对传入请求和传出响应的访问；<strong>Application</strong> 和 <strong>Session</strong>，提供对应用程序和会话变量的访问；<strong>User</strong>，提供有关通过了身份验证的用户的信息；其他相关属性。 </p>
<p>使用 Microsoft&#174; .NET Framework Version 1.0，<strong>RewritePath()</strong> 方法可以接受单个字符串作为要使用的新路径。<strong>HttpContext</strong> 类的 <strong>RewritePath(string)</strong> 方法在内部对 <strong>Request</strong> 对象的 <strong>Path</strong> 属性和 <strong>QueryString</strong> 属性进行更新。除了 <strong>RewritePath(string)</strong>，.NET Framework 1.1 还包括另一种形式的 <strong>RewritePath() </strong>方法，此方法可以接受三个字符串输入参数。此备用重载形式不仅要设置 <strong>Request</strong> 对象的 <strong>Path</strong> 属性和 <strong>QueryString</strong> 属性，还要设置内部成员变量，这些变量用于计算 <strong>Request</strong> 对象的 <strong>PhysicalPath</strong>、<strong>PathInfo</strong> 和 <strong>FilePath</strong> 属性值。</p>
<p>要在 ASP.NET 中实现 URL 重写，需要创建 HTTP 模块或 HTTP 处理程序，以便完成以下操作： </p>
<ol>
    <li>
    <p>检查被请求的路径，以确定 URL 是否需要重写。 </p>
    <li>
    <p>如果需要重写，通过调用<strong> RewritePath() </strong>方法来重写路径。 </p>
    </li>
</li></ol>
<p>例如，假设我们的网站中包含每个员工通过 /info/employee.aspx?empID=employeeID 均可访问的信息。为了使 URL 可以更多地被&#8220;删节&#8221;，我们可以决定通过以下地址来访问员工页面：/people/EmployeeName.aspx。这就是我们要使用 URL 重写的一个例子。也就是说，在请求 /people/ScottMitchell.aspx 页面时，我们要重写该 URL，以便使用 /info/employee.aspx?empID=1001 页面。</p>
<h4>使用 HTTP 模块执行 URL 重写</h4>
<p>在 ASP.NET 级别执行 URL 重写时，可以使用 HTTP 模块或 HTTP 处理程序来执行重写。使用 HTTP 模块时，必须决定在请求有效期内的哪个时间点上来检查 URL 是否需要重写。乍一看，这似乎可以任意选择，但决定会以一种明显而微妙的方式对应用程序产生影响。由于内置 ASP.NET HTTP 模块使用 <strong>Request</strong> 对象的属性执行任务，因此选择在何处执行重写非常重要。（如上所述，重写路径将改变 Request 对象的属性值。）下面列出了这些密切相关的内置 HTTP 模块及其捆绑到的事件：</p>
<table>
    <tbody>
        <tr>
            <th>
            <p>HTTP 模块</p>
            </th>
            <th>
            <p>事件</p>
            </th>
            <th>
            <p>说明</p>
            </th>
        </tr>
        <tr>
            <td>
            <p><strong>FormsAuthenticationModule</strong> </p>
            </td>
            <td>
            <p><strong>AuthenticateRequest</strong> </p>
            </td>
            <td>
            <p>确定用户是否通过了窗体身份验证。如果没有，用户将被自动重定向到指定的登录页面。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p><strong>FileAuthorizationMoudle</strong> </p>
            </td>
            <td>
            <p><strong>AuthorizeRequest</strong> </p>
            </td>
            <td>
            <p>使用 Windows 身份验证时，此 HTTP 模块将检查以确保 Microsoft&#174; Windows&#174; 帐户对被请求的资源具有足够的权限。</p>
            </td>
        </tr>
        <tr>
            <td>
            <p><strong>UrlAuthorizationModule</strong> </p>
            </td>
            <td>
            <p><strong>AuthorizeRequest</strong> </p>
            </td>
            <td>
            <p>检查以确保请求者可以访问指定的 URL。通过 Web.config 文件中的 &lt;authorization&gt; 和 &lt;location&gt; 元素来指定 URL 授权。</p>
            </td>
        </tr>
    </tbody>
</table>
<table>
    <tbody>
    </tbody>
</table>
<p>如上所述，<strong>BeginRequest</strong> 事件在 <strong>AuthenticateRequest</strong> 之前触发，后者在 <strong>AuthenticateRequest</strong> 之前触发。 </p>
<p>可以执行 URL 重写的一个安全位置是在 <strong>BeginRequest</strong> 事件中。也就是说，如果 URL 需要重写，该操作将在任何一个内置 HTTP 模块运行后执行。使用窗体身份验证时，这种方法存在一定的缺陷。如果您以前使用过窗体身份验证，您会了解当用户访问受限资源时，他们将被自动重定向到指定的登录页面。成功登录后，用户将被返回到他们第一次尝试访问的页面。 </p>
<p>如果在 <strong>BeginRequest</strong> 或 <strong>AuthenticateRequest</strong> 事件中执行 URL 重写，登录页面（提交后）将把用户重定向到重写后的页面上。也就是说，假设用户在其浏览窗口中键入了 /people/ScottMitchell.aspx，此地址将被重写为 /info/employee.aspx?empID=1001。如果将 Web 应用程序配置为使用窗体身份验证，当用户第一次访问 /people/ScottMitchell.aspx 时，首先，URL 将被重写为 /info/employee.aspx?empID=1001；接下来，<strong>FormsAuthenticationModule</strong> 将运行，并将用户重定向到登录页面（如果需要）。但是，用户在成功登录后将被发送到 /info/employee.aspx?empID=1001，因为当 <strong>FormsAuthenticationModule</strong> 运行后，此 URL 即是请求的 URL。</p>
<p>同样，在 <strong>BeginRequest</strong> 或 <strong>AuthenticateRequest</strong> 事件中执行重写时，<strong>UrlAuthorizationModule</strong> 看到的将是重写后的 URL。也就是说，如果您在 Web.config 文件中使用 &lt;location&gt; 元素来为特定的 URL 指定授权，则必须引用重写后的 URL。</p>
<p>要解决这些细微问题，您可以决定在 <strong>AuthorizeRequest</strong> 事件中执行 URL 重写。此方法解决了 URL 授权和窗体身份验证的一些问题，但同时也产生了新的问题：文件授权无法工作。使用 Windows 身份验证时，<strong>FileAuthorizationModule</strong> 将检查以确保通过身份验证的用户具有访问特定 ASP.NET 页面的相应权限。</p>
<p>假设一组用户对 C:\Inetput\wwwroot\info\employee.aspx 没有 Windows 级别的文件访问权限，并要尝试访问 /info/employee.aspx?empID=1001，他们将会收到授权错误消息。但是，如果我们将 URL 重写移到 <strong>AuthenticateRequest</strong> 事件中，当 <strong>FileAuthorizationModule</strong> 检查安全设置时，仍然认为被请求的文件是 people/ScottMitchell.aspx，因为该 URL 必须被重写。因此，文件授权检查将通过，允许此用户查看重写后的 URL /info/employee.aspx?empID=1001 的内容。</p>
<p>那么，应该何时在 HTTP 模块中执行 URL 重写？这取决于要使用的身份验证类型。如果不想使用任何身份验证，则无论 URL 重写发生在 <strong>BeginRequest</strong>、<strong>AuthenticateRequest</strong> 还是 <strong>AuthorizeRequest</strong> 中都没有什么关系。如果要使用窗体身份验证而不使用 Windows 身份验证，请将 URL 重写放在 <strong>AuthorizeRequest</strong> 事件处理程序中执行。最后，如果要使用 Windows 身份验证，请在 <strong>BeginRequest</strong> 或 <strong>AuthenticateRequest</strong> 事件进行过程中安排 URL 重写。</p>
<h4>在 HTTP 处理程序中执行 URL 重写</h4>
<p>也可以由 HTTP 处理程序或 HTTP 处理程序工厂执行 URL 重写。如上所述，HTTP 处理程序是负责生成特定类型请求的内容的类；HTTP 处理程序工厂是负责返回 HTTP 处理程序实例的类，该实例可以生成特定类型请求的内容。</p>
<p>在本文中，我们将对如何为 ASP.NET 网页创建 URL 重写 HTTP 处理程序工厂进行讨论。HTTP 处理程序工厂必须实现 <strong>IHttpHandlerFactory</strong> 接口，此接口包括<strong> GetHandler()</strong> 方法。初始化相应的 HTTP 模块后，ASP.NET 引擎将确定为给定的请求调用哪个 HTTP 处理程序或 HTTP 处理程序工厂。如果要调用 HTTP 处理程序工厂，ASP.NET 引擎将为 Web 请求调用传入 <strong>HttpContext</strong> 的 HTTP 处理程序工厂的 <strong>GetHandler()</strong> 方法，以及一些其他信息。然后，HTTP 处理程序工厂必须返回一个对象，该对象将实现可以处理请求的 <strong>IHttpHandler</strong>。</p>
<p>要通过 HTTP 程序程序执行 URL 重写，我们可以创建一个 HTTP 处理程序工厂，该处理程序工厂的 <strong>GetHandler()</strong> 方法将检查被请求的路径，以确定是否需要重写 URL。如果需要，它可以调用传入的 <strong>HttpContext</strong> 对象的 <strong>RewritePath()</strong> 方法，如前面所讨论的。最后，HTTP 处理程序工厂可以返回由 <strong>System.Web.UI.PageParser</strong> 类的 <strong>GetCompiledPageInstance()</strong> 方法返回的 HTTP 处理程序。（此技术与内置 ASP.NET 网页 HTTP 处理程序工厂 <strong>PageHandlerFactory</strong> 工作时所应用的技术相同。）</p>
<p>由于所有 HTTP 模块都将在实例化自定义 HTTP 处理程序工厂之前进行初始化，因此，在将 URL 重写放在事件的后半段时，使用 HTTP 处理程序工厂就会带来相同的风险，即文件授权无法工作。因此，如果您依赖于 Windows 身份验证和文件授权，您可能希望为 URL 重写使用 HTTP 模块方法。</p>
<p>在下一部分中，我们将对构建可重用的 URL 重写引擎进行讨论。在介绍了 URL 重写引擎（可通过下载本文的代码获得）之后，我们将在剩下的两个部分中对 URL 重写的实际使用情况进行介绍。首先，我们将讨论如何使用 URL 重写引擎，并介绍一个简单的 URL 重写示例。接下来，我们将利用重写引擎的正则表达式功能来提供真正&#8220;可删节&#8221;的 URL。</p>
<div><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_up(zh-cn,MSDN.10).gif" border="0" alt="" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection">返回页首</a> <br />
</div>
<h3 id="XSLTsection126121120120">构建 URL 重写引擎</h3>
<p>为了有助于描述如何在 ASP.NET Web 应用程序中实现 URL 重写，我创建了 URL 重写引擎。此重写引擎将提供以下功能： </p>
<ul>
    <li>
    <p>使用 URL 重写引擎的 ASP.NET 页面开发人员可以在 Web.config 文件中指定重写规则。 </p>
    </li><li>
    <p>重写规则可以使用正则表达式来实现功能强大的重写规则。 </p>
    <li>
    <p>可以轻松地将 URL 重写配置为使用 HTTP 模块或 HTTP 处理程序。 </p>
    </li>
</li></ul>
<p>在本文中，我们将介绍仅使用 HTTP 模块的 URL 重写。要查看如何使用 HTTP 处理程序来执行 URL 重写，请参考可随本文下载的代码。</p>
<h4>为 URL 重写引擎指定配置信息</h4>
<p>让我们先介绍一下 Web.config 文件中重写规则的结构。首先，您需要在 Web.config 文件中指明要使用 HTTP 模块还是 HTTP 处理程序来执行 URL 重写。在下载代码中，Web.config 文件包含两个已注释掉的条目：</p>
<pre>&lt;!--
&lt;httpModules&gt;
&lt;add type="URLRewriter.ModuleRewriter, URLRewriter"
name="ModuleRewriter" /&gt;
&lt;/httpModules&gt;
--&gt;
&lt;!--
&lt;httpHandlers&gt;
&lt;add verb="*" path="*.aspx"
type="URLRewriter.RewriterFactoryHandler, URLRewriter" /&gt;
&lt;/httpHandlers&gt;
--&gt;
</pre>
<p>注释掉 &lt;httpModules&gt; 条目，以使用 HTTP 模块执行重写；注释掉 &lt;httpHandlers&gt; 条目，以使用 HTTP 处理程序执行重写。</p>
<p>除了指定使用 HTTP 模块还是 HTTP 处理程序执行重写外，Web.config 文件还包含重写规则：重写规则由两个字符串组成：要在被请求的 URL 中查找的模式；要替换此模式的字符串（如果找到）。在 Web.config 文件中，此信息是使用以下语法表达的：</p>
<pre>&lt;RewriterConfig&gt;
&lt;Rules&gt;
&lt;RewriterRule&gt;
&lt;LookFor&gt;要查找的模式&lt;/LookFor&gt;
&lt;SendTo&gt;要用来替换模式的字符串&lt;/SendTo&gt;
&lt;/RewriterRule&gt;
&lt;RewriterRule&gt;
&lt;LookFor&gt;要查找的模式&lt;/LookFor&gt;
&lt;SendTo&gt;要用来替换模式的字符串&lt;/SendTo&gt;
&lt;/RewriterRule&gt;
...
&lt;/Rules&gt;
&lt;/RewriterConfig&gt;
</pre>
<p>每个重写规则均由 &lt;<strong>RewriterRule</strong>&gt; 元素表达。要搜索的模式由 &lt;<strong>LookFor</strong>&gt; 元素指定，而要替换所找到的模式的字符串将在 &lt;<strong>SentTo</strong>&gt; 元素中输入。这些重写规则将从头到尾进行计算。如果发现与某个规则匹配，URL 将被重写，并且对重写规则的搜索将会终止。</p>
<p>在 &lt;<strong>LookFor</strong>&gt; 元素中指定模式时，请注意，要使用正则表达式来执行匹配和字符串替换。（稍后，我们将介绍一个真实的示例，说明如何使用正则表达式来搜索模式。）由于模式是正则表达式，应确保转义正则表达式中的任何保留字符。（一些正则表达式保留字符包括：.、?、^、$ 及其他。可以通过在前面加反斜杠（如 \.）对这些字符进行转义，以匹配文字句点。）</p>
<h4>使用 HTTP 模块执行 URL 重写</h4>
<p>创建 HTTP 模块与创建可以实现 <strong>IHttpModule</strong> 接口的类一样简单。<strong>IHttpModule</strong> 接口定义了两种方法： </p>
<ul>
    <li>
    <p><strong>Init(HttpApplication)</strong>。此方法在初始化 HTTP 模块后触发。在此方法中，您将把事件处理程序绑定到相应的 <strong>HttpApplication</strong> 事件。 </p>
    <li>
    <p><strong>Dispose()</strong>。当请求已完成并已发送回 IIS 时调用此方法。您应当在此处执行所有最终的清除操作。 </p>
    </li>
</li></ul>
<p>为了便于为 URL 重写创建 HTTP 模块，我将从创建抽象基类 <strong>BaseModuleRewriter</strong> 开始介绍。此类将实现 <strong>IHttpModule</strong>。在<strong> Init() </strong>事件中，它将 <strong>HttpApplication</strong> 的 <strong>AuthorizeRequest</strong> 事件绑定到 <strong>BaseModuleRewriter_AuthorizeRequest</strong> 方法。<strong>BaseModuleRewriter_AuthorizeRequest </strong>方法将调用该类传入被请求的 <strong>Path</strong> 的 <strong>Rewrite()</strong> 方法，以及传入 <strong>Init()</strong> 方法的 <strong>HttpApplication</strong> 对象。<strong>Rewrite() </strong>方法是抽象的，也就是说，在 <strong>BaseModuleRewriter</strong> 类中，<strong>Rewrite()</strong> 方法没有方法主体；从 <strong>BaseModuleRewriter</strong> 派生而来的类必须覆盖此方法并提供方法主体。</p>
<p>具有此基类后，只需创建由 <strong>BaseModuleRewriter</strong> 派生的类即可，该类可以覆盖 <strong>Rewrite() </strong>并在那里执行 URL 重写逻辑。下面显示了 <strong>BaseModuleRewriter</strong> 的代码。</p>
<pre>public abstract class BaseModuleRewriter : IHttpModule
{
public virtual void Init(HttpApplication app)
{
// 警告！此代码不适用于 Windows 身份验证！
// 如果使用 Windows 身份验证，
// 请改为 app.BeginRequest
app.AuthorizeRequest += new
EventHandler(this.BaseModuleRewriter_AuthorizeRequest);
}
public virtual void Dispose() {}
protected virtual void BaseModuleRewriter_AuthorizeRequest(
object sender, EventArgs e)
{
HttpApplication app = (HttpApplication) sender;
Rewrite(app.Request.Path, app);
}
protected abstract void Rewrite(string requestedPath,
HttpApplication app);
}
</pre>
<p>请注意，<strong>BaseModuleRewriter</strong> 类将在 <strong>AuthorizeRequest</strong> 事件中执行 URL 重写。如上所述，如果将 Windows 身份验证与文件授权结合使用，您需要对此做出更改，以便可以在 <strong>BeginRequest</strong> 或 <strong>AuthenticateRequest</strong> 事件中执行 URL 重写。</p>
<p><strong>ModuleRewriter</strong> 类扩展了 <strong>BaseModuleRewriter</strong> 类，并负责执行实际的 URL 重写。<strong>ModuleRewriter</strong> 包含单一覆盖方法（<strong>Rewrite()</strong>），如下所示：</p>
<pre>protected override void Rewrite(string requestedPath,
System.Web.HttpApplication app)
{
// 获得配置规则
RewriterRuleCollection rules =
RewriterConfiguration.GetConfig().Rules;
// 遍历每个规则...
for(int i = 0; i &lt; rules.Count; i++)
{
// 获得要查找的模式，并且
// 解析 Url（转换为相应的目录）
string lookFor = "^" +
RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath,
rules[i].LookFor) + "$";
// 创建 regex（请注意，已设置 IgnoreCase...）
Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);
// 查看是否找到了匹配的规则
if (re.IsMatch(requestedPath))
{
// 找到了匹配的规则 -- 进行必要的替换
string sendToUrl =
RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath,
re.Replace(requestedPath, rules[i].SendTo));
// 重写 URL
RewriterUtils.RewriteUrl(app.Context, sendToUrl);
break;      // 退出 For 循环
}
}
}
</pre>
<p><strong>Rewrite() </strong>方法从获取 Web.config 文件中的一组重写规则开始。然后，它将遍历重写规则，每次遍历一个，对于每个规则，它将获取规则的 <strong>LookFor</strong> 属性，并使用正则表达式来确定是否在被请求的 URL 中找到了匹配的规则。</p>
<p>如果找到了匹配的规则，将在具有<strong> SendTo</strong> 属性值的被请求路径上执行正则表达式替换。然后，替换后的 URL 将被传递到 <strong>RewriterUtils.RewriteUrl()</strong> 方法中。<strong>RewriterUtils</strong> 是一个 helper 类，此类将提供一对由 URL 重写 HTTP 模块和 HTTP 处理程序使用的静态方法。<strong>RewriterUrl()</strong> 方法仅调用 <strong>HttpContext</strong> 对象的 <strong>RewriteUrl() </strong>方法。</p>
<p><strong>注意</strong>：您可能已注意到，执行正则表达式匹配和替换时，将调用 <strong>RewriterUtils.ResolveUrl()</strong>。此 helper 方法只替换具有应用程序路径值的字符串中的所有 ~ 实例。</p>
<p>URL 重写引擎的整个代码可随本文下载。我们已经介绍了大部分密切相关的组件，但还有一些其他组件（例如，对 Web.config 文件中 XML 格式的重写规则进行反序列化以使其成为对象的类），以及用于 URL 重写的 HTTP 处理程序工厂。本文剩余的三个部分将对 URL 重写的实际使用情况进行介绍。</p>
<div><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_up(zh-cn,MSDN.10).gif" border="0" alt="" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection">返回页首</a> <br />
</div>
<h3 id="XSLTsection127121120120">使用 URL 重写引擎执行简单的 URL 重写</h3>
<p>为了实际演示 URL 重写引擎，我们来构建一个使用简单 URL 重写的 ASP.NET Web 应用程序。假设我们所工作的公司通过网络销售分类产品。这些产品分为以下几个类别：</p>
<table>
    <tbody>
        <tr>
            <th>
            <p>类别 ID</p>
            </th>
            <th>
            <p>类别名称</p>
            </th>
        </tr>
        <tr>
            <td>
            <p>1</p>
            </td>
            <td>
            <p>饮料</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>2</p>
            </td>
            <td>
            <p>调味品</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>3</p>
            </td>
            <td>
            <p>糖果</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>4</p>
            </td>
            <td>
            <p>奶制品</p>
            </td>
        </tr>
        <tr>
            <td>
            <p>...</p>
            </td>
            <td>
            <p>...</p>
            </td>
        </tr>
    </tbody>
</table>
<table>
    <tbody>
    </tbody>
</table>
<p>假设我们已创建了名为 ListProductsByCategory.aspx 的 ASP.NET 网页，该网页在查询字符串中接受类别 ID 值，并显示属于该类的所有产品。因此，要查看我们销售的饮料的用户可以访问 ListProductsByCategory.aspx?CategoryID=1，而那些要查看奶制品的用户可以访问 ListProductsByCategory.aspx?CategoryID=4。此外，还假设我们有一个名为 ListCategories.aspx 的页面，该页面列出了待售的所有产品类别。</p>
<p>很显然，这是一个 URL 重写事例，因为提供给用户的 URL 没有为用户带来任何意义，也没有为他们提供任何&#8220;可删节性&#8221;。因此，让我们使用 URL 重写，以便在用户访问 /Products/Beverages.aspx 时，他们的 URL 将被重写为 ListProductsByCategory.aspx?CategoryID=1。我们可以在 Web.config 文件中使用以下 URL 重写规则来实现此功能。</p>
<pre>&lt;RewriterConfig&gt;
&lt;Rules&gt;
&lt;!-- 产品制表者规则 --&gt;
&lt;RewriterRule&gt;
&lt;LookFor&gt;~/Products/Beverages\.aspx&lt;/LookFor&gt;
&lt;SendTo&gt;~/ListProductsByCategory.aspx?CategoryID=1&lt;/SendTo&gt;
&lt;/RewriterRule&gt;
&lt;RewriterRule&gt;
&lt;/Rules&gt;
&lt;/RewriterConfig&gt;
</pre>
<p>正如您可以看到的，此规则将进行搜索，以查看用户请求的路径是否为 /Products/Beverages.aspx。如果是，它便将 URL 重写为 /ListProductsByCategory.aspx?CategoryID=1。</p>
<p><strong>注意</strong>：请注意，&lt;<strong>LookFor</strong>&gt; 元素对 Beverages.aspx 中的句点进行了转义。这是因为在正则表达式模式中使用了 &lt;<strong>LookFor</strong>&gt; 值，并且句点是正则表达式中的特殊字符，该字符表示&#8220;匹配任意字符&#8221;，例如，与 URL /Products/BeveragesQaspx 匹配。通过转义句点（使用 \.），可以表明我们要匹配的是文字句点，而不是任何旧的字符。</p>
<p>有了此规则之后，当用户访问 /Products/Beverages.aspx 时，页面上将显示待售的饮料。图 3 显示了访问 /Products/Beverages.aspx 的浏览器的快照。请注意，在浏览器的地址栏中，URL 将读取 /Products/Beverages.aspx，但用户实际看到的是 ListProductsByCategory.aspx?CategoryID=1 的内容。（实际上，Web 服务器上根本不存在 /Products/Beverages.aspx 文件！）</p>
<p><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.urlrewriting_fig03(zh-cn,MSDN.10).gif" alt="" /> </p>
<div>图 3. 重写 URL 之后请求类别</div>
<p>与 /Products/Beverages.aspx 相似，下面我们要为其他产品类别添加重写规则。此操作仅包括在 Web.config 文件的 &lt;Rules&gt; 元素内添加附加的 &lt;<strong>RewriterRule</strong>&gt; 元素。请参阅下载内容中的 Web.config 文件，以获取用于此演示的一组完整的重写规则。</p>
<p>为了使 URL 更具可删节性，最好使用户只需从 /Products/Beverages.aspx 中删除 Beverages.aspx 即可看到产品类别的列表。乍一看，这可能是一项很普通的任务（只需添加一个将 /Products/ 映射到 /ListCategories.aspx 的重写规则即可）。但此操作存在一个微妙之处，即您必须首先创建一个 /Products/ 目录，并在 /Products/ 目录中添加一个空的 Default.aspx 文件。</p>
<p>要理解需要执行这些额外步骤的原因，可以参考前面的内容，即 URL 重写引擎位于 ASP.NET 级别上。也就是说，如果 ASP.NET 引擎永远没有机会处理请求，URL 重写引擎就没有办法检测传入的 URL。而且，请记住，仅当被请求的文件具有相应的扩展名时，IIS 才会将传入请求传递给 ASP.NET 引擎。因此，如果用户访问 /Products/，而 IIS 没有看到任何文件扩展名，那么它将检查目录，以查看是否存在这样一个文件，即该文件名为默认文件名中的一个。（Default.aspx、Default.htm、Default.asp 等等。&#8220;IIS 管理&#8221;对话框中&#8220;Web 服务器属性&#8221;对话框的&#8220;文档&#8221;选项卡对这些默认文件名进行了定义。）当然，如果 /Products/ 目录不存在，IIS 将返回 HTTP 404 错误。 </p>
<p>因此，我们需要创建 /Products/ 目录。另外，我们还需要在此目录中创建一个文件 Default.aspx。这样，当用户访问 /Products/ 时，IIS 将检测目录，查看是否存在一个名为 Default.aspx 的文件，然后将处理过程传递给 ASP.NET 引擎。然后，URL 重写器将在重写 URL 时分解。</p>
<p>创建目录和 Default.aspx 文件后，请继续操作，并向 &lt;<strong>Rules</strong>&gt; 元素中添加以下重写规则：</p>
<pre>&lt;RewriterRule&gt;
&lt;LookFor&gt;~/Products/Default\.aspx&lt;/LookFor&gt;
&lt;SendTo&gt;~/ListCategories.aspx&lt;/SendTo&gt;
&lt;/RewriterRule&gt;
</pre>
<p>有了此规则之后，当用户访问 /Products/ 或 /Products/Default.aspx 时，他们将看到产品类别列表，如图 4 所示。</p>
<p><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.urlrewriting_fig04(zh-cn,MSDN.10).gif" alt="" /> </p>
<div><strong>图</strong> <strong>4. </strong>向 URL 添加&#8220;可删节性&#8221;</div>
<h4>处理回发</h4>
<p>如果要重写的 URL 中包含一个服务器端的 Web 窗体并执行回发，则窗体回发后，将使用带下划线的 URL。也就是说，如果用户在浏览器中输入 /Products/Beverages.aspx，他们在浏览器地址栏中看到的将是 /Products/Beverages.aspx，但是他们看到的内容将是 ListProductsByCategory.aspx?CategoryID=1 的内容。如果 ListProductsByCategory.aspx 执行了回发，用户将被回发到 ListProductsByCategory.aspx?CategoryID=1，而不是 /Products/Beverages.aspx。这样不会中断任何内容，但从用户的角度考虑，如果单击按钮时突然看到 URL 更改会使他们感到不安。</p>
<p>出现这种情况的原因是：在呈现 Web 窗体时，它会将其操作属性直接设置为 Request 对象中文件路径的值。当然，在呈现 Web 窗体时，URL 已从 /Products/Beverages.aspx 重写为 ListProductsByCategory.aspx?CategoryID=1，这表明 Request 对象报告用户要访问 ListProductsByCategory.aspx?CategoryID=1。只需使服务器端窗体不呈现操作属性即可解决此问题。（默认情况下，如果窗体不包含操作属性，浏览器将会回发。）</p>
<p>不幸的是，Web 窗体不允许您明确指定操作属性，也不允许您设置某些属性以禁用操作属性的呈现。因此，我们必须自己来扩展 System.Web.HtmlControls.HtmlForm 类，覆盖 RenderAttribute() 方法并明确指出它不会呈现操作属性。</p>
<p>由于继承功能，我们可以获得 HtmlForm 类的所有功能，并且只需添加几行代码即可获得所需的行为。以下显示了自定义类的完整代码：</p>
<pre>namespace ActionlessForm {
public class Form : System.Web.UI.HtmlControls.HtmlForm
{
protected override void RenderAttributes(HtmlTextWriter writer)
{
writer.WriteAttribute("name", this.Name);
base.Attributes.Remove("name");
writer.WriteAttribute("method", this.Method);
base.Attributes.Remove("method");
this.Attributes.Render(writer);
base.Attributes.Remove("action");
if (base.ID != null)
writer.WriteAttribute("id", base.ClientID);
}
}
}
</pre>
<p>已被覆盖的 <strong>RenderAttributes()</strong> 方法的代码仅包含 <strong>HtmlForm</strong> 类的 <strong>RenderAttributes()</strong> 方法的准确代码，而不设置操作属性。（我使用 Lutz Roeder 的 <a href="http://www.aisto.com/roeder/DotNet/" id="ctl00_rs1_mainContentContainer_ctl12" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl12',this);">Reflector</a> 来查看 <strong>HtmlForm</strong> 类的源代码。）</p>
<p>创建此类并对其进行编译之后，要在 ASP.NET Web 应用程序中使用它，应首先将其添加到 Web 应用程序的 References 文件夹中。然后，要使用它来代替 <strong>HtmlForm</strong> 类，只需在 ASP.NET 网页的顶部添加以下内容即可：</p>
<pre>&lt;%@ Register TagPrefix="skm" Namespace="ActionlessForm"
Assembly="ActionlessForm" %&gt;
</pre>
<p>然后，将 &lt;form runat="server"&gt;（如果有）替换为：</p>
<pre>&lt;skm:Form id="Form1" method="post" runat="server"&gt;
</pre>
<p>并将右边的 &lt;/form&gt; 标记替换为：</p>
<pre>&lt;/skm:Form&gt;
</pre>
<p>您可以在 ListProductsByCategory.aspx（包含在本文的下载代码中）中发现操作中的此自定义 Web Form 类。下载内容中还包含了用于无操作 Web Form 的 Visual Studio .NET 项目。</p>
<p><strong>注意</strong>：如果要重写的目标 URL 没有执行回发，则无需使用此自定义 Web Form 类。</p>
<div><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_up(zh-cn,MSDN.10).gif" border="0" alt="" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection">返回页首</a> <br />
</div>
<h3 id="XSLTsection128121120120">创建真正&#8220;可删节&#8221;的 URL</h3>
<p>前一部分中介绍的简单 URL 重写显示了如何轻松地为 URL 重写引擎配置新的重写规则。但在使用正则表达式时，重写规则的真正功能才会发挥更大作用，本部分将对此进行探讨。</p>
<p>Blog 在当今正变得越来越流行，似乎每个人都拥有自己的 blog。如果您不熟悉 blog：blog 是经常更新的个人页面，通常作为联机期刊。大多数 blog 只记录每天发生的事情，还有一些 blog 可能关注于特定的主题（例如，电影回顾、体育团队或计算机技术）。</p>
<p>可以在任何地点对 blog 进行更新，更新频率为从每天几次到每周一次或两次，具体情况取决于作者。通常，blog 主页将显示最近的 10 个条目，但实际上，所有 blog 软件均提供存档，访问者可以通过存档读取较早的帖子。Blog 是用于&#8220;可删节&#8221;URL 的一个功能强大的应用程序。假设在搜索 blog 的存档时，您在 URL /2004/02/14.aspx 上发现了您自己。如果您发现自己在阅读 2004 年 2 月 14 日的帖子，您是否觉得很惊讶？而且，您可能希望查看 2004 年 2 月的所有帖子，在这种情况下，您可以尝试将 URL 删节为 /2004/02/。要查看 2004 年的所有帖子，您可以尝试访问 /2004/。</p>
<p>维护 blog 时，最好为访问者提供此级别的 URL&#8220;可删节性&#8221;。许多 blog 引擎都提供此功能，但我们将讨论如何使用 URL 重写来实现此功能。 </p>
<p>首先，我们需要一个 ASP.NET 网页，此页面将按照日、月或年来显示 blog 条目。假设我们有一个 ShowBlogContent.aspx 页面，该页面的查询字符串参数为年、月和日。要查看 2004 年 2 月 14 日的帖子，我们可以访问 ShowBlogContent.aspx?year=2004&amp;month=2&amp;day=14。要查看 2004 年 2 月的所有帖子，我们可以访问 ShowBlogContent.aspx?year=2004&amp;month=2。最后，要查看 2004 年的所有帖子，我们可以浏览到 ShowBlogContent.aspx?year=2004。（可以在本文的下载内容中找到 ShowBlogContent.aspx 的代码。）</p>
<p>在这种情况下，如果用户访问 /2004/02/14.aspx，我们需要将 URL 重写为 ShowBlogContent.aspx?year=2004&amp;month=2&amp;day=14。所有三种情况（URL 指定了年、月和日时；URL 仅指定了年和月时；URL 仅指定了年时）均可使用重写规则进行处理：</p>
<pre>&lt;RewriterConfig&gt;
&lt;Rules&gt;
&lt;!-- Blog 内容显示程序规则 --&gt;
&lt;RewriterRule&gt;
&lt;LookFor&gt;~/(\d{4})/(\d{2})/(\d{2})\.aspx&lt;/LookFor&gt;
&lt;SendTo&gt;~/ShowBlogContent.aspx?year=$1&amp;amp;month=$2&amp;amp;day=$3&lt;/SendTo&gt;
&lt;/RewriterRule&gt;
&lt;RewriterRule&gt;
&lt;LookFor&gt;~/(\d{4})/(\d{2})/Default\.aspx&lt;/LookFor&gt;
&lt;SendTo&gt;&lt;![CDATA[~/ShowBlogContent.aspx?year=$1&amp;month=$2]]&gt;&lt;/SendTo&gt;
&lt;/RewriterRule&gt;
&lt;RewriterRule&gt;
&lt;LookFor&gt;~/(\d{4})/Default\.aspx&lt;/LookFor&gt;
&lt;SendTo&gt;~/ShowBlogContent.aspx?year=$1&lt;/SendTo&gt;
&lt;/RewriterRule&gt;
&lt;/Rules&gt;
&lt;/RewriterConfig&gt;
</pre>
<p>这些重写规则表明了正则表达式的功能。在第一个规则中，我们使用模式 (\d{4})/(\d{2})/(\d{2})\.aspx 查找 URL。在简明英语中，它对应了这样一个字符串：首先是四个数字，后跟一个斜杠，然后是两个数字，后跟一个斜杠，然后再跟两个数字，最后是一个 .aspx。每个数字组周围的括号非常重要，通过它可以在相应的 &lt;<strong>SendTo</strong>&gt; 属性中引用这些括号内的匹配字符。 特别是，我们可以针对第一、第二和第三个括号组分别使用 $1、$2 和 $3 引用回括号内的匹配组。 </p>
<p><strong>注意</strong>：由于 Web.config 文件采用 XML 格式，但是必须对元素文字部分中的字符（如 &amp;、&lt; 和 &gt;）进行转义。在第一个规则的 &lt;<strong>SendTo</strong>&gt; 元素中，&amp; 被转义为 &amp;amp;。在第二个规则的 &lt;<strong>SendTo</strong>&gt; 中使用了另外一种技术（使用 &lt;<strong>![CDATA[...]]</strong>&gt; 元素），无需对内部的内容进行转义。可以使用两种方法中的任何一种，并且都会得到相同的结果。</p>
<p>图 5、6 和 7 显示了操作中的 URL 重写。数据实际上是从我的 blog <a href="http://scottonwriting.net/" id="ctl00_rs1_mainContentContainer_ctl13" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl13',this);">http://scottonwriting.net/</a> 中拖过来的。图 5 中显示了 2003 年 11 月 7 日的帖子；图 6 中显示了 2003 年 11 月的所有帖子；图 7 显示了 2003 年的所有帖子。</p>
<p><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.urlrewriting_fig05(zh-cn,MSDN.10).gif" alt="" /> </p>
<div><strong>图</strong> <strong>5.</strong> 2003 年 11 月 7 日的帖子</div>
<p><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.urlrewriting_fig06(zh-cn,MSDN.10).gif" alt="" /> </p>
<div><strong>图</strong> <strong>6.</strong> 2003 年 11 月的所有帖子</div>
<p><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.urlrewriting_fig07(zh-cn,MSDN.10).gif" alt="" /> </p>
<div><strong>图</strong> <strong>7. </strong>2003 年的所有帖子</div>
<p><strong>注意</strong>：URL 重写引擎在 &lt;<strong>LookFor</strong>&gt; 元素中需要使用正则表达式模式。如果您对正则表达式不熟悉，可以阅读我在早些时候编写的一篇文章 <a href="http://www.4guysfromrolla.com/webtech/090199-1.shtml" id="ctl00_rs1_mainContentContainer_ctl14" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl14',this);">An Introduction to Regular Expressions</a>。另外，还有一个很好的网站：<a href="http://regexlib.com/" id="ctl00_rs1_mainContentContainer_ctl15" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl15',this);">RegExLib.com</a>，在那里您可以获取有关常用正则表达式的帮助信息，还可以共享您自己的自定义正则表达式。</p>
<h4>构建必备的目录结构</h4>
<p>当请求 /2004/03/19.aspx 时，IIS 将通知 .aspx 扩展，并将请求路由到 ASP.NET 引擎。请求在 ASP.NET 引擎的管道中移动时，URL 将被重写为 ShowBlogContent.aspx?year=2004&amp;month=03&amp;day=19，并且访问者会看到 2004 年 3 月 19 日的 blog 条目。但是当用户浏览到 /2004/03/ 时将会发生什么情况呢？除非有一个 /2004/03/ 目录，否则 IIS 将返回一个 404 错误。此外，此目录中还需要具有 Default.aspx 页面，以便可以将请求传递给 ASP.NET 引擎。</p>
<p>因此，要使用这种方法，必须手动创建一个用于每年的目录（其中包含 blog 条目），并且目录中具有一个 Default.aspx 页面。另外，在每年目录中，您需要再手动创建十二个目录（01、02、?、?...、12），并且每个目录中均有一个 Default.aspx 文件。（如上所述，我们还必须执行前面演示中的操作，即在 /Products/ 目录中添加一个 Default.aspx 文件，以便访问 /Products/ 时可以正确显示 ListCategories.aspx。）</p>
<p>很显然，添加这样一个目录结构可能是一件很痛苦的事情。解决此问题的方法是使所有传入的 IIS 请求都映射到 ASP.NET 引擎。通过这种方法，即使访问 URL /2004/03/，IIS 也会如实地将请求传递给 ASP.NET 引擎（即使并不存在 /2004/03/ 目录）。但是，使用这种方法将使 ASP.NET 引擎负责处理到达 Web 服务器的所有类型的传入请求，包括图像、CSS 文件、外部 JavaScript 文件、Macromedia Flash 文件，等等。</p>
<p>对处理所有文件类型的全面讨论远远超出了本文的范围。有关使用此技术的 ASP.NET Web 应用程序的示例，请参阅 .Text，一个开放源 blog 引擎。<a href="http://www.gotdotnet.com/community/workspaces/default.aspx" id="ctl00_rs1_mainContentContainer_ctl16" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00|ctl00_rs1_mainContentContainer_ctl16',this);">.Text</a> 可以配置为将所有请求均映射到 ASP.NET 引擎。它可以使用自定义 HTTP 处理程序来处理生成所有文件类型的问题，自定义 HTTP 处理程序了解如何生成典型的静态文件类型（图像、CSS 文件，等等）。</p>
<div><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection"><img src="http://msdn2.microsoft.com/zh-cn/library/ms972974.arrow_px_up(zh-cn,MSDN.10).gif" border="0" alt="" /> </a><a href="http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx#mainSection">返回页首</a> <br />
</div>
<h3 id="XSLTsection129121120120">结论</h3>
<p>在本文中，我们讨论了如何在 ASP.NET 级别通过 <strong>HttpContext</strong> 类的<strong> RewriteUrl() </strong>方法来执行 URL 重写。正如我们所看到的，<strong>RewriteUrl() </strong>更新了特定的 <strong>HttpContext's Request</strong> 属性，从而更新了被请求的文件和路径。最终结果是，从用户角度来看，他们要访问某个特定的 URL，但从 Web 服务器端来看，被请求的却是另一个 URL。</p>
<p>可以在 HTTP 模块或 HTTP 处理程序中重写 URL。在本文中，我们介绍了如何使用 HTTP 模块执行重写，并讨论了在管道中的不同阶段执行重写的结果。</p>
<p>当然，如果执行 ASP.NET 级别的重写，则仅当已成功地将请求从 IIS 传递给 ASP.NET 引擎后才会发生 URL 重写。实际上，只有用户请求带 .aspx 扩展名的页面时才会出现这种情况。但是，如果您要使用户可以进入实际并不存在的 URL，但又希望重写到现有的 ASP.NET 页面，则必须创建虚拟目录和 Default.aspx 页面，或者对 IIS 进行配置，以使所有传入请求一律被路由到 ASP.NET 引擎。</p>
</div>
</div>
</div>

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183621#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 15 Apr 2008 10:28:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183621</link>
        <guid>http://jonescheng.javaeye.com/blog/183621</guid>
      </item>
      <item>
        <title>初用LINUX几点感受</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183622" style="color:red;">http://jonescheng.javaeye.com/blog/183622</a>&nbsp;
          发表时间: 2008年04月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h2>初用LINUX几点感受</h2>
<p>&nbsp;</p>
<div class="t_msgfont" id="postmessage_446242">迫于想要完全学习Oracle的需要，开始学习LINUX，而网上到处是人在说LINUX如何如何的好！<br />
但是经过一段时间的学和使用，就我自己来说，用LINUX却是困难重重。<br />
先说下背景<br />
五年左右的软件开发<br />
用DOS-win2003，<br />
也用过一段时间的UNIX，不过没有装过，终端机上的<br />
<br />
这一段时间<span href="tag.php?name=%B0%B2%D7%B0" class="t_tag" onclick="tagshow(event)">安装</span>了LINUX <span href="tag.php?name=redhat" class="t_tag" onclick="tagshow(event)">redhat</span> 9，这是我从公司管理员那拿来的。<br />
<br />
学习的目的，因为要好好研究Oracle，而windows下是不会用来装Oracle做<span href="tag.php?name=%B7%FE%CE%F1%C6%F7" class="t_tag" onclick="tagshow(event)">服务器</span>的，除非是学习环境。<br />
<br />
装起来困难重重，习惯了Windows傻瓜式操作，连装一个<span href="tag.php?name=%E4%AF%C0%C0%C6%F7" class="t_tag" onclick="tagshow(event)">浏览器</span><span href="tag.php?name=firefox" class="t_tag" onclick="tagshow(event)">firefox</span>都不会。<br />
由于是用自己的T43上装的，<span href="tag.php?name=%CD%F8%BF%A8" class="t_tag" onclick="tagshow(event)">网卡</span>也找不到，也无法<span href="tag.php?name=%C9%CF%CD%F8" class="t_tag" onclick="tagshow(event)">上网</span>。还好自己装的是双<span href="tag.php?name=%CF%B5%CD%B3" class="t_tag" onclick="tagshow(event)">系统</span>，可以切到2003下<span href="tag.php?name=%C9%CF%CD%F8" class="t_tag" onclick="tagshow(event)">上网</span>。<br />
<br />
装好后，我选的是直接进图形界面，减少自己的不适应。但是还是发现除了点打开一下<span href="tag.php?name=%CE%C4%BC%FE" class="t_tag" onclick="tagshow(event)">文件</span>下，其它的真的什么都做不了。<br />
于是想，先看看能不能装好网卡，然后GOOLGE了很久，找到很多教程，却发现，都是简单几句，对于我这种刚进入LINUX的人。来说，完全是一头雾水。经过一个下午的查阅，最后终于懂了，原来是内核不支持，要更新内核，<br />
<br />
这个却又是一个LINUX老手看起来轻车熟路的简单工作，于我是却是完全不知道如何下手，<br />
试着在控制台下执行了<span href="tag.php?name=make" class="t_tag" onclick="tagshow(event)">make</span> menuconfig 结果发现 XXX的target什么的<span href="tag.php?name=%CC%E1%CA%BE" class="t_tag" onclick="tagshow(event)">提示</span>。。在网上GOOLGE才发现原来。是没有安装编译这组套件。<br />
<br />
在wind2003下<span href="tag.php?name=%CF%C2%D4%D8" class="t_tag" onclick="tagshow(event)">下载</span>好套件，<span href="tag.php?name=%CF%C2%D4%D8" class="t_tag" onclick="tagshow(event)">下载</span>好内核，打印好教程开心的切换到<span href="tag.php?name=Linux" class="t_tag" onclick="tagshow(event)">Linux</span>下，结果mount的时候，却提示说 内核不支持vfat。于是在window2003上<span href="tag.php?name=%CF%C2%D4%D8" class="t_tag" onclick="tagshow(event)">下载</span>的资料无法在linux上读取了，，这样完全让我恼火起来，在各大论坛发贴<span href="tag.php?name=%C7%F3%D6%FA" class="t_tag" onclick="tagshow(event)">求助</span>，却也无人回答。<br />
<br />
一怒之下，干脆重新安装LINUX，一古脑的把所有组件都选中了。。。<br />
<br />
LINUX经过这么长时间的发展，其时也还是非常多爱好者手中的玩具（我不是指他在服务器上的能力，那是LINUX的强项，不然也不会这么多人要去学LINUX了）。。。<br />
LINUX要走进大众，除非以后会象两个极端发展，，一支向WINDOWS的易用性发展，放弃一部份服务器性能。一支专于服务器发展。<br />
<br />
不然还是和现在一样，WINDOWS仍然占具90%以上的桌面市场。<br />
易用性才是人们所追求的目标，不管你的多么先进，多么安全，可是操作复杂，难以上手，他也只是少数人可以用的。<br />
你要一个只会开IE，只会开QQ，只会打<span href="tag.php?name=%CD%F8%C2%E7" class="t_tag" onclick="tagshow(event)">网络</span>游戏的人去用LINUX，那只是个笑话，但是很多人要说了，他不适全用LINUX。。<br />
对，他确实不适合用LINUX。可是我要说，到底是软件适用人，还是人要去适用软件。。<br />
当今的发展潮流，是<span href="tag.php?name=%B5%E7%C4%D4" class="t_tag" onclick="tagshow(event)">电脑</span>要适合人用，而不是人要去适应<span href="tag.php?name=%B5%E7%C4%D4" class="t_tag" onclick="tagshow(event)">电脑</span>。。所以我想说LINUX的普及之路，还非常的长。虽然我现在可以用LINUX上网，可以用他来做事，那是因为我也算是一个专业人士了。想想现在还在用五笔的有多少？五笔于拼音到底那个快，答案不荣置疑，可是现在为什么五笔消声无踪了？？<br />
<br />
<font face="Verdana"><a href="http://lamp.linux.gov.cn/Linux/kernel_options.html">http://lamp.linux.gov.cn/Linux/kernel_options.html</a>&nbsp; <br />
make menuconfig;<br />
make;<br />
make install;<br />
</font></div>

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183622#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 11 Apr 2008 14:48:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183622</link>
        <guid>http://jonescheng.javaeye.com/blog/183622</guid>
      </item>
      <item>
        <title>jdbc 连接mysql</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183623" style="color:red;">http://jonescheng.javaeye.com/blog/183623</a>&nbsp;
          发表时间: 2008年04月10日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          这段时间在研究JAVA，虽然看JAVA方面的书籍已经有两年多了，可是实际用JAVA做项目却是现在才真正开始。。<br />
<br />
&nbsp;&nbsp;今天做了一个测试环境，Tomcat5.5+Mysql。写了一个测试页面来检查环境，结果发现无法连结到数据库。.Net用多了就是会让人懒惰。以前装好VS就可以放心的写好代码直接访问数据库了。。<br />
&nbsp; 本着拿来主义的原则，我GOOLGE了一下，发现很多文章，于是照着文章去配值。<br />
&nbsp; 1.下载mysql-connector-java-5.05.jar<br />
&nbsp;&nbsp;2.将这个jar依次复制到j2sd的目标，jre目录，tomcat的comm\lib目标，share\lib目录，依文章的作者的观点，反正心多复制省心。<br />
&nbsp; 3.安文章中配合classpath，又是几乎放了所有的路径<br />
&nbsp; 4。运行我的写好的一个jsp文件，只是访问数据库，从里面读取数据。<br />
<br />
结果发现jit.XX.mm.Driver无法找到,换成com.mysql.driver也是一样，让我非常不解。都是照文章中的做了，怎么还是一样呢，而且最重要的环境都是一样的。。<br />
<br />
&nbsp;又GOOLGE了几篇文章都是大同小异，实在无奈下，我找到下载的mysql-connector-java-5.05.zip，然后仔细查看里面的文件，找帮助文件，终于找到一个readme.txt打开看之。才发现。原来文章做了太多的多余的步骤。<br />
<br />
&nbsp;&nbsp; 1。将mysql-connector-java-5.05.jar复到一个java的环境的目录中。<br />
&nbsp;&nbsp; 2。在classpath增加指向这个他的路径<br />
&nbsp;&nbsp; 3。将mysql-connector-java-5.05.jar复制到tomcat的comm\lib中<br />
&nbsp;&nbsp; 4。jit.XX.mm.Driver是不在使用了，5。0以上版本都要使用com.mysql.Driver这个类来创建Connection实例<br />

          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183623#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 10 Apr 2008 17:48:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183623</link>
        <guid>http://jonescheng.javaeye.com/blog/183623</guid>
      </item>
      <item>
        <title>System.Diagnostics命名空间里的Debug类和Trace类的用途（收藏）</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183624" style="color:red;">http://jonescheng.javaeye.com/blog/183624</a>&nbsp;
          发表时间: 2008年04月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="text1">System.Diagnostics命名空间里的Debug类和Trace类的用途</div>
<hr size="1" color="#cccccc" />
<div class="text2">摘要</div>
<div class="text4"></div>
<div class="text4">在 .NET 类库中有一个 System.Diagnostics 命名空间，该命名空间提供了一些与系统进程、事件日志、和性能计数器进行交互的类库。当中包括了两个对开发人员而言十分有用的类——Debug类和Trace类。本文介绍了这两个类的一些基本用途，旨在提高广大开发人员的开发效率。</div>
<hr size="1" color="#cccccc" />
<div class="text2">目录</div>
<div class="text4"></div>
<ul class="list1">
    <li class="list2"><strong></strong>使用Debug类来帮助调试
    </li><li class="list2"><strong></strong>Debug类和Trace类的区别
    </li><li class="list2"><strong></strong>使用Trace类来做程序日志
    </li><li class="list2"><strong></strong>小结
    <li class="list2"><strong></strong>参考资料</li>
</li></ul>
<hr size="1" color="#cccccc" />
<div class="text2">使用Debug类来帮助调试</div>
<div class="text4"></div>
<div class="text4">调试程序对每个程序员来说是家常便饭。可是我们会经常遇到一些情况让我们头疼，例如：</div>
<ul class="list1">
    <li class="list2"><strong></strong>当我们在开发一个界面控件的时候，简单的设断点会增加Paint事件的响应次数，而造成的环境参数改变。
    <li class="list2"><strong></strong>断点设多了，程序常常停在正常运行的地方；这样一来，调试一个错误要花费大量时间去寻找错误。</li>
</li></ul>
<div class="text4">这时，我们就需要利用System.Diagnostics.Debug类来帮助我们调试。我们可以通过调用Debug.WriteLine(String message)函数，将我们所关心的信息打印在Visual Studio IDE的Output窗口中。也可以利用Debug.Assert(bool condition)来让程序停在错误的地方，并且显示Call stack。</div>
<div class="text4">Debug类中所有函数的调用都不会在Release版本里有效。也就是说，我们通过这种方法所加的代码可以仅用于调试；在发布的时候无需删任何代码，就可以给用户一个没有调试指令的程序了。</div>
<div class="text4">下面的这个例子演示了这两个函数来帮助调试的方法：</div>
<div class="text4">1、 新建一个Visual Studio C# Project，采用默认的项目名。</div>
<div class="text4">2、 往Form1上拖一个label，并采用其缺省ID。</div>
<div class="text4">3、 在Form1.cs中的Form1类中添加下面的函数代码：</div>
<div width="100%" style="background-color: #eeeeee">
<pre class="text5">private int time=0;
protected override void OnPaint(PaintEventArgs e)
{
time++;
this.label1.Text="OnPain called "+time.ToString()+" Times.";
}
protected override void OnResize(EventArgs e)
{
System.Diagnostics.Debug.Assert(this.Width&gt;200,"Width should be larger than 200.");
System.Diagnostics.Debug.WriteLine(Size.ToString());
}
</pre>
</div>
<div class="text4">4、 编译并运行项目的Debug版本。</div>
<div class="text4">5、 切换Visual Studio .NET IDE到Output窗口。</div>
<div class="text4">6、 切换到刚才的程序，改变主窗口的大小，您可以在IDE中看到Form1窗口的实时大小，并在Form1上看到OnPaint被调用的次数。当窗口的宽度小于等于200个像素的时候，系统会弹出一个Assertion Fail的对话框。里面显示了当前程序的Call Stack。如果您在OnPaint中设置了断点，想要调试程序的话，那么您会进入一个死循环，直到您停止调试。</div>
<img src="http://www.microsoft.com/china/community/images/TechArticleImages/DebugTraceClass01.gif" alt="" /><br />
<div class="text6"></div>
<div class="text4"></div>
<img src="http://www.microsoft.com/china/community/images/TechArticleImages/DebugTraceClass02.gif" alt="" /><br />
<div class="text6"></div>
<hr size="1" color="#cccccc" />
<div class="text2">Debug类和Trace类的区别</div>
<div class="text4"></div>
<div class="text4">您一定发现了在System.Diagnostics命名空间中还有一个名为Trace的类。它的函数功能和Debug非常相似。为什么要有这样两个功能类似的类呢？</div>
<div class="text4">原因是这样的，Debug类里所提供的函数仅在编译时带#Debug宏参数才奏效，一旦到了Release版本中，这些函数都会被忽略。也就是说Debug类的功能仅在程序员开发的时候能用。而Trace则不同，它能在Release版本的程序中也被运行，这样程序员就可以在Release版本的程序中添加一些Debug类提供的功能了。</div>
<hr size="1" color="#cccccc" />
<div class="text2">使用Trace类来做程序日志</div>
<div class="text4"></div>
<div class="text4">接下来的问题就是：我们程序员能利用Trace类的功能做些什么呢？我们可以用它来做程序的日志。</div>
<div class="text4">1、 打开刚刚的project。</div>
<div class="text4">2、 用下面的代码覆盖刚才第2步的代码：</div>
<div width="100%" style="background-color: #eeeeee">
<pre class="text5">private void Calculate()
{
int a=1,b=1;
try
{
System.Random r = new Random();
while (true)
{
a=(int)(r.NextDouble()*10);
b=(int)(r.NextDouble()*10);
System.Diagnostics.Trace.WriteLine(System.DateTime.Now.ToString()+": "+
a.ToString()+"/"+b.ToString()+"="+(a/b).ToString());
}
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(System.DateTime.Now.ToString()+": "+a.ToString()+
"/"+b.ToString()+"="+" ERROR: "+ex.Message);
MessageBox.Show(ex.Message);
}
}
</pre>
</div>
<div class="text4">3、 在构造函数Form1()的最后添加下面的代码，将Trace的输出重定向到app.log文件中：</div>
<div width="100%" style="background-color: #eeeeee">
<pre class="text5">System.Diagnostics.Trace.Listeners.Clear();
System.Diagnostics.Trace.AutoFlush=true;
System.Diagnostics.Trace.Listeners.Add(new System.Diagnostics.TextWriterTraceListener("app.log"));
</pre>
</div>
<div class="text4">4、 拖一个按钮到该Form上，双击按钮，在button1_Click函数中添加如下代码：</div>
<div width="100%" style="background-color: #eeeeee">
<pre class="text5">Calculate();
Application.Exit();
</pre>
</div>
<div class="text4">5、 运行该程序的Release版本，点击添加的按钮，程序便开始执行一位随机数除法。由于是随机数，可能会出现出数为0的情况，这样程序就会抛出Exception，这是程序会自动中止。</div>
<div class="text4">6、 在该程序所在的目录里您可以发现出现了一个新的文件app.log，里面记录了各个时刻的运算纪录，并把Exception纪录在日志中。</div>
 
          <br/>
          <span style="color:red;">
            <a href="http://jonescheng.javaeye.com/blog/183624#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 07 Apr 2008 17:38:00 +0800</pubDate>
        <link>http://jonescheng.javaeye.com/blog/183624</link>
        <guid>http://jonescheng.javaeye.com/blog/183624</guid>
      </item>
      <item>
        <title>常用的JavaScript验证正则表达式</title>
        <author>jonescheng</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://jonescheng.javaeye.com">jonescheng</a>&nbsp;
          链接：<a href="http://jonescheng.javaeye.com/blog/183625" style="color:red;">http://jonescheng.javaeye.com/blog/183625</a>&nbsp;
          发表时间: 2008年04月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p><code>下面都是我收集的一些比较常用的正则表达式，因为平常可能在表单验证的时候，用到的比较多。特发出来，让各位朋友共同使用。呵呵。</code></p>
<p>匹配中文字符的正则表达式： [u4e00-u9fa5]<br />
评注：匹配中文还真是个头疼的事，有了这个表达式就好办了</p>
<p>匹配双字节字符(包括汉字在内)：[^x00-xff]<br />
评注：可以用来计算字符串的长度（一个双字节字符长度计2，ASCII字符计1）</p>
<p>匹配空白行的正则表达式：ns*r<br />
评注：可以用来删除空白行</p>
<p>匹配HTML标记的正则表达式：&lt; (S*?)[^&gt;]*&gt;.*?|&lt; .*? /&gt;<br />
评注：网上流传的版本太糟糕，上面这个也仅仅能匹配部分，对于复杂的嵌