• ASP.NET中aspx页面runat="server"的本质(Essensial of runat=”server” in ASP.NET)(转)


    今天同事问我一个“神奇”的问题,另一个同事“神奇”地找出了问题但无法解释,归咎于一种“习惯”或者“下次注意”。现在我把问题描述一下,并做一些解释。

    我的同事先是在现有工程中新加了一个aspx页面,然后从现有的执行正确的页面的源码中copy了一部分内容到新页面的相应位置,但却无意中留下了runat=”server”标签。具体代码还原如下:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebAppHeadRunatServer._Default" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <
    html xmlns="http://www.w3.org/1999/xhtml">

    <
    script runat="server">
    string str = "mypath";
    </script>

    <
    head runat="server">
    <
    title>
    <%="My title" %></title>
    <
    link rel="Stylesheet" href="<%= str %>/a.css" />

    <
    script language="javascript" type="text/javascript" src="<%=str %>/a.js"></script>

    </
    head>
    <
    body>
    </
    body>
    </
    html>

    1、以上代码究竟会有哪些诡异的现象?

    在代码没有出现问题的时候,我们总是不太在意runat=”server”的存在,即便在代码出现问题的时候,我们也很少会重视runat=”server”的问题,因此当问题出现的时候我们总是用“经验”或者“尝试”去解决,却很少能够知根知底地了解它,因为它确实看起来太“小”了。

    那么上面的代码究竟如何诡异呢?因为我的朋友始终没有注意到runat=”server”,因此当出现下面问题的时候,我们总是在比较两个文本的差异。

    <head><title>
            My title
    </title><link rel="Stylesheet" href="&lt;%= str %>/a.css" />

        <script language="javascript" type="text/javascript" src="mypath/a.js"></script>

    起初,我们认为是我们写错了,我们重复将正确位置的<%=str%>拷贝至出错的位置,但并非如我们所愿,使用断点调试,我们所跟到的str的值是正确的,但后续动作我们跟踪不到,因此我们开始相信“诡异”,当然,崇尚科学的我们也知道,这仅仅是我们暂时无法解决的。

    2、为何在调试的时候,只有link里的href会出问题,而别的却不会?

    当我们重复了3-4次之后,我们开始怀疑我们的人品,因为我同事说代码是从另一个页面上拷过来的,因此肉眼加事实告诉我们,这是一个特例,直到我们请另一个同事过来鉴证诡异之后,我们才决定去比对一下“代码是如何被拷过来”的,原来代码是被局部拷贝,影响代码的罪魁祸首是head处的runat=”server”。嗯,问题找到了,但发现问题的同事却无法解释,理由仅是,这样是不行的,我们都得把这个去掉。确实太牵强了,难道我们就不能解释一下?当然,求人不如求己,自己思考一下就很容易有结果了。

    3、runat=”server”的时候到底发生了什么?

    既然问题是从runat=”server”引起的,那么就得从它入手。runat=”server”这个标记,旨在aspx页面被编译的时候,用来标识我们页面上的html应该如何解释的。准确地说,aspx页面的生成原理是,aspx页面会被读入分析器,当一个HTML标签内不包含runat=”server”的时候,它将被当作字符串输出或者编译成new LiteralControl(“具体的文本”);

    当遇到runat=”server”的时候,该标签将被当作一个HtmlControl来对待,当然,首先这个HtmlControl是必须存在的,下面的表格显示了可以设置runat=”server”的Html标签,否则将会出现“分析器错误”(分析器错误消息: 路径中具有非法字符。)

    比如:div没有对应的HtmlDiv,如果你非得写上<div runat=”server” …则会出现如下错误。

    分析器错误

    下面的类继承图展示了所有继承自HtmlControls的控件。

    System.Web.UI.HtmlControls

    比如一个<head runat=”server”></head>

    将被解析为:

    HtmlHead head = new HtmlHead(“head”);
    HtmlTitle title = this.__BuildControl__control3();
    IParserAccessor accessor = head;
    accessor.AddParsedSubObject(title);

    这里的AddParsedSubObject大致是如下代码:

    public class Control
    {
            protected virtual void AddParsedSubObject(object obj)
            {
                Control child = obj as Control;
                if (child != null)
                {
                    this.Controls.Add(child);
                }
            }
    }

    也就是Controls.Add方法。

    这里用增加输出委托的方式来进行一些HTML呈现的逻辑:

    private HtmlTitle __BuildControl__control3()
    {
        HtmlTitle title = new HtmlTitle();
        title.SetRenderMethodDelegate(new RenderMethod(this.__Render__control3));
        return title;
    }
    private void __Render__control3(HtmlTextWriter __w, Control parameterContainer)
    {
        __w.Write("\r\n        ");
        __w.Write("My title");
    }

    4、那为何link的href会出错,而script则不会呢?

    // 生成script

    private void __Render__control2(HtmlTextWriter __w, Control parameterContainer)
    {
        parameterContainer.Controls[0].RenderControl(__w);
        parameterContainer.Controls[1].RenderControl(__w);
        __w.Write("\r\n\r\n    <script language=\"javascript\" type=\"text/javascript\" src=\"");
        __w.Write(this.str);
        __w.Write("/a.js\"></script>\r\n\r\n    ");
        parameterContainer.Controls[2].RenderControl(__w);
    }

    // 生成HtmlLink
    private HtmlLink __BuildControl__control4()
    {
        HtmlLink link = new HtmlLink();
        ((IAttributeAccessor) link).SetAttribute("rel", "Stylesheet");
        link.Href = "<%= str %>/a.css";
        return link;
    }

    很显然,link的Href是直接将页面上的文本进行赋值的,因此最终并没有使用str中的值去替代文本,而script中,str则是被直接输出的,因此script是可以的,而link则会出现问题。

    5、去掉runat=”server”则解决问题,为什么?

    因为去掉runat=”server”后,link标签将不再转换成HtmlLink,因此就不会调用link.Href属性去赋值,因此就不会出错了。

    private void __Render__control1(HtmlTextWriter __w, Control parameterContainer)
    {
        __w.Write("\r\n\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n\r\n");
        __w.Write("\r\n\r\n<head>\r\n    <title>\r\n        ");
        __w.Write("My title");
        __w.Write("</title>\r\n    <link rel=\"Stylesheet\" href=\"");
        __w.Write(this.str);
        __w.Write("/a.css\" />\r\n\r\n    <script language=\"javascript\" type=\"text/javascript\" src=\"");
        __w.Write(this.str);
        __w.Write("/a.js\"></script>\r\n\r\n    ");
        parameterContainer.Controls[0].RenderControl(__w);
        __w.Write("\r\n</head>\r\n<body>\r\n    ");
        parameterContainer.Controls[1].RenderControl(__w);
        __w.Write("\r\n</body>\r\n</html>\r\n");
    }

    6、有什么特征?(结论)

    准确地说,因为在runat=”server”下,编译器会将其中的所有属性的xxxx=yyyy的部分都首先转换成其Attribute,设置的方式通常有以下两种

    a、通过SetAttribute("xxxx", "yyyy");

    b、如果该HtmlControl含有该属性,则由其属性来设置,如head.Href=”yyyy”;

    注意,这里不管yyyy是否是<%=mmm%>,一样会被直接当作文本输出。

    但是,在正文中出现的将以__w.Write(this.str);的方式输出。

        <form id="form1" runat="server" accept="<%=str %>">
    <
    div>
    <%=str %>
    </div>
    </
    form>

    结论:在runat=”server”下的标签,如果可以转换成HtmlControl,那么它的Attribute将不能使用<%=str%>的方式输出,如果不能转换成HtmlControl,则没有具体要求。如果一定要使用<%=str%>的方式,则需要将其以及它的祖先节点上的runat=”server”去掉即可。但是,关于一个嵌套的结构是否会被自动提升为runat=”server”则是根据标准来制定的。比如将link标签放在head中,设置head为runat=”server”,则link会被转换成HtmlLink,但是将其放在<form runat=”server”>下则只会被当作文本输出。而在form下的控件则不会进行自动提升,如<form runat=”server”><input type=”button” /></form>则button将继续以文本的方式输出,遇到<%=str%>将被转换成__w.Write(str);。如果需要将其提升为HtmlInputButton控件,将显示指定其为<input runat=”server” type=”button” />。

    编译下面的代码,并打开“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\”路径下的对应的程序集,用reflector反编译查看源代码,并验证以上规律。
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebAppHeadRunatServer._Default" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <
    html xmlns="http://www.w3.org/1999/xhtml">

    <
    script runat="server">
    string
    str = "mypath";
    </script>

    <
    head runat="server">
    <
    title>
    <%="My title" %></title>
    <
    link rel="Stylesheet<%= str %>" href="<%= str %>/a.css" />
    <
    input id="Button1" type="button" value="<%=str %>" />

    <
    script language="javascript" type="text/javascript" src="<%=str %>/a.js"><%=str %><link rel="Stylesheet<%= str %>" href="<%= str %>/a.css" /></script>

    <
    meta content="volnet.cnblogs.com" name="<%=str %>-blog" runat="server" />
    </
    head>
    <
    body>
    <
    form id="form1" runat="server" accept="<%=str %>">
    <
    link rel="Stylesheet<%= str %>" href="<%= str %>/a.css" />
    <
    div id="<%=str %>x">
    accept="<%=str %>"
    </div>
    <
    input type="button" value="<%=str %>" />
    <
    input type="button" runat="server" value="<%=str %>" />
    </
    form>
    </
    body>
    </
    html>
  • 相关阅读:
    MyEclipse 修改代码不生效
    最简单的网页分享代码
    php libevent 扩展使用示例
    function gzdecode
    20165327 2017-2018-2 《Java程序设计》第9周学习总结
    结对编程_四则运算
    20165327 2017-2018-2 《Java程序设计》第8周学习总结
    2017-2018-2 20165327 实验二 《Java面向对象程序设计》实验报告
    20165327 2017-2018-2 《Java程序设计》第7周学习总结
    20165327 2017-2018-2 《Java程序设计》第6周学习总结
  • 原文地址:https://www.cnblogs.com/aaa6818162/p/1531402.html
Copyright © 2020-2023  润新知