自从 mvc3 被广泛的推进生产环境后,这个runat="server" 慢慢被人遗忘了。。。

asp.net 的 webForm 基于控件的 html  渲染过程是否还记得呢?是否还记得那 一坨坨的控件?

HtmlTextWriter 还有记得的吗?

那迷人的 Page的生命周期?

https://msdn.microsoft.com/zh-cn/library/ms178472.aspx

下面文章希望有所启发。。

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标签,否则将会出现“分析器错误”(分析器错误消息: 路径中具有非法字符。):

比如一个<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=”head”则解决问题,为什么?

因为去掉runat=”head”后,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);。如果需要将其提升为HtmlButton控件,将显示指定其为<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>
 

老古董---ASP.NET中aspx页面runat="server"的更多相关文章

  1. ASP.Net中防止页面刷新重复提交的几种方法

    [摘要] 目前很多网站都要提交页面插入或更新数据库,比如留言本,一个用户提交留言后,如果按F5,就会重新提交一遍留言,导致数据库出现两条一模一样的留言,本文介绍了几种防止页面刷新,导致重复提交数据的方 ...

  2. Asp.Net中动态页面转静态页面

    关于在Asp.Net中动态页面转静态页面的方法网上比较多.结合实际的需求,我在网上找了一些源代码,并作修改.现在把修改后的代码以及说明写一下. 一个是一个页面转换的类,该类通过静态函数Changfil ...

  3. ASP.NET中实现页面间的参数传递

    ASP.NET中实现页面间的参数传递   编写人:CC阿爸 2013-10-27 l  近来在做泛微OA与公司自行开发的系统集成登录的问题.在研究泛微页面间传递参为参数,综合得了解了一下现行页面间传参 ...

  4. asp.net中父子页面通过gridview中的按钮事件进行回传值的问题

    这两天写BS程序,遇到父子页面传值的问题,以前没写过web系统,用了几天时间才将问题解决,总结下记录下来: 问题描述: 父页面A中有一个gridview,每行6个列,有5列中均有一个按钮,单击按钮,会 ...

  5. ASP.NET中实现页面间数据传递的方法

    说到页面间数据传递,很多人都会想到通过像Session这样的全局变量,但是向Session中添加的东西太多会增加服务器的压力,页面间数据传递,数据的作用范围越小越好.   ASP.NET页面间数据传递 ...

  6. asp.net 6.aspx页面

    1.aspx页面的头部 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Us ...

  7. webform工程中aspx页面为何不能调用appcode文件夹下的类(ASP.NET特殊文件夹的用法)

    App_code 只有website类型的工程才有效. App_Code 下创建的.cs文件仅仅是“内容”不是代码.你设置那个文件为“编译”就行了. 其他特殊文件夹 1. Bin文件夹 Bin文件夹包 ...

  8. asp.net 中 .ASPX 与.CS文件的关系

    .aspx文件继承自.cs文件 虽然一个 Web 窗体页由两个单独的文件组成,但这两个文件在应用程序运行时形成了一个整体.项目中所有 Web 窗体的代码隐藏类文件都被编译成由项目生成的动态链接库 (. ...

  9. asp.net中iframe页面用jQuery向父页面传值

    在asp.net页面有时一个页面会通过iframe嵌套另一个页面,下面的例子讲述的是被嵌套的iframe页面向父页传值的一种方式,用jQuery即可. iframe页面代码: <!DOCTYPE ...

随机推荐

  1. 第二章 TypeScript 开发环境搭建

    Mac OS X 下 TypeScript 开发环境搭建 一.集成开发环境 WebStrom VSCode 二.安装 TypeScript Homebrew(macOS 缺失的软件包管理器) ruby ...

  2. Centos7 Mysql 双机热备实现数据库高可用

    mysql双主热备,也称主主互备,目的是mysql数据库高可用,只支持双机,原因是mysql的复制是一主多从,但一个从服务器只能有一个主服务器. 双机热备的条件是双机mysql版本必须一致. 服务器分 ...

  3. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(二十四)Structured Streaming:Encoder

    一般情况下我们在使用Dataset<Row>进行groupByKey时,你会发现这个方法最后一个参数需要一个encoder,那么这些encoder如何定义呢? 一般数据类型 static ...

  4. easyui tree loader用法

    easyui的tree每次都展开,在获取子节点,自定义参数解决方案,兄跌是不是找很久了! 直接上代码 //重写tree的loader $.extend($.fn.tree.defaults, { lo ...

  5. 创建mysql数据库并指定编码

    xplanner的readme.txt里有句话“XPlanner has only been tested on mysql 4.x, myslq 5.0, Tomcat 5.x, java 1.4, ...

  6. Tomcat的性能与最大并发配置

    当一个进程有 500 个线程在跑的话,那性能已经是很低很低了.Tomcat 默认配置的最大请求数是 150,也就是说同时支持 150 个并发,当然了,也可以将其改大. 当某个应用拥有 250 个以上并 ...

  7. setTimeout迭代替换setInterval

    一.它们之间的区别 setTimeout - 仅执行一次 setInterval - 间隔执行     二.为什么推荐用setTimeout替换掉setIntelval?   javascript是异 ...

  8. Direct hosting of SMB over TCP/IP

    http://support.microsoft.com/kb/204279 System TipThis article applies to a different version of Wind ...

  9. Shell 字符串分割

    入门级别 入门级别:类似1,2,3,4,5这样的字符串 #!/bin/bash var="1,2,3,4,5" var=${var//,/ } for i in $var; do ...

  10. Linux下利用signal函数处理ctrl+c等信号

    前言 linux下能够通过信号机制来实现程序的软中断,是一个很实用的编程方法. 我们平时在程序执行的时候按下ctrl-c.ctrl-z或者kill一个进程的时候事实上都等效于向这个进程发送了一个特定信 ...