ASP的隐藏特性。网

这个问题存在是因为它已经存在了 具有历史意义,但事实并非如此 被认为是一个很好的主题问题 对于这个网站,所以请不要使用它 作为证据,你可以问类似的问题 问题。< /强> < / p >

更多信息:https://stackoverflow.com/faq


总有一些功能在边缘场景中很有用,但正是因为这个原因,大多数人都不知道它们。我要求的是课本上通常没有教过的特性。

你知道的是什么?

36231 次浏览
这似乎是一个巨大而模糊的问题… 但我将加入Reflection,因为它允许我做一些令人难以置信的强大的事情,如可插拔的DALs等

HttpModules。建筑非常优雅。也许不是一个隐藏的功能,但仍然很酷。

HttpContext。项作为请求级缓存工具

HttpContext。IsCustomErrorEnabled是一个很酷的特性。我不止一次发现它很有用。这里有一个关于它的短的文章

有两件事在我脑海中浮现:

1)你可以在代码中打开和关闭Trace:

#ifdef DEBUG
if (Context.Request.QueryString["DoTrace"] == "true")
{
Trace.IsEnabled = true;
Trace.Write("Application:TraceStarted");
}
#endif

2)您可以使用一个共享的“代码隐藏”文件构建多个.aspx页面。

构建一个类.cs文件:

public class Class1:System.Web.UI.Page
{
public TextBox tbLogin;


protected void Page_Load(object sender, EventArgs e)
{


if (tbLogin!=null)
tbLogin.Text = "Hello World";
}
}

然后你可以有任意数量的。aspx页面(在你删除VS生成的。designer.cs和。cs代码之后):

  <%@ Page Language="C#"  AutoEventWireup="true"  Inherits="Namespace.Class1" %>
<form id="form1" runat="server">
<div>
<asp:TextBox  ID="tbLogin" runat="server"></asp: TextBox  >
</div>
</form>

你可以在ASPX中拥有没有在Class1中出现的控件,反之亦然,但是你需要记住检查你的控件是否为空。

你可以使用:

 Request.Params[Control.UniqueId]

在viewstate初始化之前获取控件的值。文本等将在此时为空)。

这对于Init中的代码很有用。

  • < >强HttpContext。>将始终给你访问当前上下文的请求/响应等。,即使您没有访问Page属性的权限(例如,从松耦合的helper类)。

  • 通过调用Response将用户重定向到另一个页面后,可以继续在同一页面上执行代码。重定向(url false )

  • 你不需要。ASPX文件,如果你想要的只是一个编译的Page(或任何IHttpHandler)。只需将路径和HTTP方法设置为指向<httpHandlers>元素中的类。配置文件。

  • Page对象可以从中检索。通过编程调用PageParser.GetCompiledPageInstance(virtualPath,aspxFileName,Context) . /em>文件

throw new HttpException(404, "Article not found");

这将被ASP捕获。NET,它将返回customErrors页面。在最近的.NET每日小贴士中学习了这个

默认情况下,自定义控件的标记之间的任何内容都被添加为子控件。这可以在AddParsedSubObject ()重写中截获,用于过滤或额外的解析(例如,在LiteralControls中的文本内容):

    protected override void AddParsedSubObject(object obj)
{ var literal = obj as LiteralControl;
if (literal != null) Controls.Add(parseControl(literal.Text));
else base.AddParsedSubObject(obj);
}

...

   <uc:MyControl runat='server'>
...this text is parsed as a LiteralControl...
</uc:MyControl>

如果你放置一个名为app_offline.htm 在一个web应用程序目录的根目录,ASP。NET 2.0+将关闭应用程序并停止对该应用程序的任何新传入请求的正常处理,只显示app_offline.htm文件对于所有新请求.

.htm的内容

这是在将更改重新部署(或回滚)到生产服务器时显示“站点暂时不可用”通知的最快速和最简单的方法。

另外,正如marxidad所指出的,确保文件中至少有512字节的内容,这样IE6才能正确呈现它。

如果你有ASP。NET生成一个RSS提要,它有时会在页面顶部放置额外的一行。这不会用普通RSS验证器进行验证。你可以通过在页面底部放置页面指令<@Page>来解决这个问题。

当我将xmlDocument()转储到标签中并使用它的xsl转换显示时,我认为这很整洁。

基于目标浏览器设置服务器控件属性更多的

<asp:Label runat="server" ID="labelText"
ie:Text="This is IE text"
mozilla:Text="This is Firefox text"
Text="This is general text"
/>

这句话让我有点吃惊。

在ASP。NET v3.5添加了一些路由,你可以创建自己的友好url,只需在页面管道的早期编写HTTPModule并重写请求(如BeginRequest事件)。

http://servername/page/Param1/SomeParams1/Param2/SomeParams2这样的url将被映射到如下所示的另一个页面(通常使用正则表达式)。

HttpContext.RewritePath("PageHandler.aspx?Param1=SomeParms1&Param2=SomeParams2");

DotNetNuke有一个非常好的HttpModule来为他们的友好url做这个。对于不能部署. net v3.5的机器仍然有用。

包含在ASP中。Net 3.5 sp1:

  • customErrors现在支持“redirectMode”属性,值为“ResponseRewrite”。显示错误页面而不改变URL。
  • 表单标记现在识别操作属性。非常适合在使用URL重写时使用

在测试时,您可以将电子邮件发送到计算机上的文件夹,而不是SMTP服务器。把这个放到你的web.config中:

<system.net>
<mailSettings>
<smtp deliveryMethod="SpecifiedPickupDirectory">
<specifiedPickupDirectory pickupDirectoryLocation="c:\Temp\" />
</smtp>
</mailSettings>
</system.net>

将位于App_Code文件夹中的类附加到全局应用程序类文件。

ASP。NET 2.0 -全球。asax -代码背后文件

它也可以在Visual Studio 2008中工作。

你可以通过使用它的UniqueID属性找到任何控件:

Label label = (Label)Page.FindControl("UserControl1$Label1");

HttpContext.Current.IsDebuggingEnabled . HttpContext.Current.IsDebuggingEnabled

这对于决定要输出哪些脚本(最小或完整版本)或其他您在开发中可能想要但不是实时的脚本非常有用。

System.Web.VirtualPathUtility . vref ="nofollow noreferrer

你可以使用ASP。.aspx页面中的。NET注释用于注释掉页面的全部部分,包括服务器控件。而且被注释掉的内容永远不会被发送到客户端。

<%--
<div>
<asp:Button runat="server" id="btnOne"/>
</div>
--%>

VS阻塞的有效语法:

<input type="checkbox" name="roles" value='<%# Eval("Name") %>'
<%# ((bool) Eval("InRole")) ? "checked" : "" %>
<%# ViewData.Model.IsInRole("Admin") ? "" : "disabled" %> />

我曾经开发过一个asp.net应用程序,它通过了一家领先的安全公司的安全审计,我学会了这个简单的技巧来防止一个不太为人所知但很重要的安全漏洞。

以下解释来自: http://www.guidanceshare.com/wiki/ASP.NET_2.0_Security_Guidelines_-_Parameter_Manipulation#Consider_Using_Page.ViewStateUserKey_to_Counter_One-Click_Attacks < / p >

考虑使用Page。ViewStateUserKey用于对抗一键式攻击。如果您对调用者进行身份验证并使用ViewState,请设置Page。Page_Init事件处理程序中的ViewStateUserKey属性,以防止一键式攻击。

void Page_Init (object sender, EventArgs e) {
ViewStateUserKey = Session.SessionID;
}

将属性设置为您知道对每个用户都是唯一的值,例如会话ID、用户名或用户标识符。

当攻击者创建一个Web页面(.htm或.aspx),其中包含一个名为__VIEWSTATE的隐藏表单字段,该字段已经填充了ViewState数据时,就会发生一键式攻击。ViewState可以从攻击者先前创建的页面生成,例如包含100个商品的购物车页面。攻击者诱使不知情的用户浏览页面,然后攻击者将页面发送到ViewState有效的服务器。服务器无法知道ViewState来自攻击者。ViewState验证和hmac不能对抗这种攻击,因为ViewState是有效的,并且页面是在用户的安全上下文中执行的。

通过设置ViewStateUserKey属性,当攻击者浏览到一个页面以创建ViewState时,该属性将初始化为攻击者的名字。当合法用户向服务器提交页面时,将使用攻击者的名称对页面进行初始化。结果,ViewState HMAC检查失败并生成异常。

ASHX文件类型使用情况:
.使用实例 如果你只想输出一些基本的html或xml,而不需要通过页面事件处理程序,那么你可以以一种简单的方式实现HttpModule

将页面命名为SomeHandlerPage。Ashx和只是把下面的代码(只有一行)

<%@ webhandler language="C#" class="MyNamespace.MyHandler" %>

然后是代码文件

using System;
using System.IO;
using System.Web;


namespace MyNamespace
{
public class MyHandler: IHttpHandler
{
public void ProcessRequest (HttpContext context)
{
context.Response.ContentType = "text/xml";
string myString = SomeLibrary.SomeClass.SomeMethod();
context.Response.Write(myString);
}


public bool IsReusable
{
get { return true; }
}
}
}

在内容页中启用MasterPages的智能感知 我相信这是一个非常鲜为人知的黑客

大多数情况下,当你想要使用母版页中的控件时,你必须使用findcontrol方法并从内容页中强制转换它们,MasterType指令将在visual studio中启用智能感知

只需在页面上再添加一条指令

<%@ MasterType VirtualPath="~/Masters/MyMainMasterPage.master" %>

如果您不想使用虚拟路径,则使用类名

<%@ MasterType TypeName="MyMainMasterPage" %>

获取全文在这里

Page对象上的ClientScript属性。

代码表达式构建器

样本的标记:

Text = '<%$ Code: GetText() %>'
Text = '<%$ Code: MyStaticClass.MyStaticProperty %>'
Text = '<%$ Code: DateTime.Now.ToShortDateString() %>'
MaxLenth = '<%$ Code: 30 + 40 %>'

代码表达式构建器的真正美妙之处在于,您可以像在非数据绑定情况下使用表达式一样使用数据绑定。您还可以创建执行其他功能的其他表达式生成器。

. config:

<system.web>
<compilation debug="true">
<expressionBuilders>
<add expressionPrefix="Code" type="CodeExpressionBuilder" />

实现这一切的cs类:

[ExpressionPrefix("Code")]
public class CodeExpressionBuilder : ExpressionBuilder
{
public override CodeExpression GetCodeExpression(
BoundPropertyEntry entry,
object parsedData,
ExpressionBuilderContext context)
{
return new CodeSnippetExpression(entry.Expression);
}
}

< a href = " http://msdn.microsoft.com/en-us/library/system.web.hosting.hostingenvironment.mappath.aspx " rel =“nofollow”noreferrer > System.Web.Hosting.HostingEnvironment.MapPath < / >

零售模式在机器。配置水平:

<configuration>
<system.web>
<deployment retail="true"/>
</system.web>
</configuration>

覆盖网络。配置设置以强制调试为false,打开自定义错误并禁用跟踪。不再忘记在发布之前更改属性—只需将它们全部配置为开发或测试环境,并更新生产零售设置。

我想到了一个特性,有时候你需要隐藏页面的某些部分。你可以用javascript或下面的简单代码来实现:

if (Request.Browser.Crawler){
HideArticleComments();

EnsureChildControls方法:它检查子控件是否被初始化。如果子控件未初始化,则调用CreateChildControls方法。

我的团队经常使用这个方法:

WebRequest myRequest = WebRequest.Create("http://www.google.com");
WebResponse myResponse = myRequest.GetResponse();
StreamReader sr = new StreamReader(myResponse.GetResponseStream());


// here's page's response loaded into a string for further use


String thisReturn = sr.ReadToEnd().Trim();

它以字符串的形式加载网页的响应。你也可以发送post参数。

当我们需要一些便宜和快速的东西时,我们用它来代替ASCX/AJAX/WebServices。基本上,它是一种跨服务器访问web可用内容的快速方法。事实上,我们昨天刚刚把它命名为“乡下人网络服务”。

这是最好的一个。把它加到你的网里。配置更快的编译。这是3.5SP1后通过这种压

<compilation optimizeCompilations="true">
快速总结:我们正在介绍一个 新的optimizeCompilations开关 ASP。NET,可以大大提高 某些场景下的编译速度。 这里有一些陷阱,所以继续往下读 更多的细节。这个开关是 目前可用作QFE

. 3.5SP1,将是VS 2010的一部分 < p > ASP。NET编译系统需要一个 非常保守的方法 它会清除之前的 它在任何时候所做的工作都是“顶部” 关卡文件更改。“顶级”文件 包括bin和App_Code中的任何东西, 以及global.asax。虽然这 它适用于小型应用程序 几乎不能用于非常大的应用程序。 例如,一位顾客遇到了一个 案子花了十分钟才到 在进行任何更改后刷新页面

为了减轻痛苦,我们增加了一个 “优化”编译模式 不那么保守

通过在这里:

WebMethods。

你可以使用ASP。NET AJAX回调到ASPX页面中的web方法。你可以用[WebMethod()]和[ScriptMethod()]属性来修饰一个静态方法。例如:

[System.Web.Services.WebMethod()]
[System.Web.Script.Services.ScriptMethod()]
public static List<string> GetFruitBeginingWith(string letter)
{
List<string> products = new List<string>()
{
"Apple", "Banana", "Blackberry", "Blueberries", "Orange", "Mango", "Melon", "Peach"
};


return products.Where(p => p.StartsWith(letter)).ToList();
}

现在,在你的ASPX页面你可以这样做:

<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />
<input type="button" value="Get Fruit" onclick="GetFruit('B')" />
</div>
</form>

并通过JavaScript调用您的服务器端方法使用:

    <script type="text/javascript">
function GetFruit(l)
{
PageMethods.GetFruitBeginingWith(l, OnGetFruitComplete);
}


function OnGetFruitComplete(result)
{
alert("You got fruit: " + result);
}
</script>

ASP的一个鲜为人知和很少使用的特性。网络:

标签映射

它很少使用,因为只有在特定的情况下你需要它,但当你需要它时,它是所以方便。

一些关于这个小功能的文章:

< p > ASP中的标签映射。网 < br > 在ASP中使用标签映射。NET 2.0 < / p >

从上一篇文章来看:

标签映射允许交换 上编译时的兼容控件 web应用程序中的每个页面。一个 有用的例子是如果你有一只股票 ASP。NET控件,例如控件 下拉列表,你想替换 它有一个自定义的控件 派生自下拉列表。这可能 为已自定义的控件 提供更优化的缓存 查找数据。而不是编辑每一个 Web表单和替换内置 下拉列表与您的自定义 版本,你可以有ASP。净的 效果做它为您修改 . config: < / p >

<pages>
<tagMapping>
<clear />
<add tagType="System.Web.UI.WebControls.DropDownList"
mappedTagType="SmartDropDown"/>
</tagMapping>
</pages>

请求。IsLocal属性:

它指示当前请求是否来自本地计算机。

if( Request.IsLocal )
{
LoadLocalAdminMailSettings();
}
else
{
LoadServerAdminMailSettings();
}

面板中的DefaultButton属性。

它为特定面板设置默认按钮。

Page指令中的MaintainScrollPositionOnPostback属性。它用于在回发时保持aspx页面的滚动位置。

在开始一个长时间运行的任务之前,检查客户端是否仍然连接:

if (this.Response.IsClientConnected)
{
// long-running task
}

与optimizeCompilations= " true "解决方案类似,这里有另一个解决方案,可以加快在构建之间等待的时间(非常好,特别是如果你正在处理一个大型项目):创建一个基于ram的驱动器(即使用RamDisk),并更改默认的“临时ASP。NET文件”到这个基于内存的驱动器。

关于如何做到这一点的完整细节在我的博客上:http://www.wagnerdanda.me/2009/11/speeding-up-build-times-in-asp-net-with-ramdisk/

基本上你首先配置一个RamDisk(同样,在我的博客中有一个免费RamDisk的链接),然后你改变你的网络。按此配置:

 <system.web>
....
<compilation debug="true" tempDirectory="R:\ASP_NET_TempFiles\">
....
</compilation>
....
</system.web>

它大大增加了我的开发时间,你只需要为你的电脑投资内存:)

编程的快乐!

瓦格纳Danda

你知道可以用ASP。IIS或Visual Studio之外的网络?

整个运行时都打包好了,可以在任何想要尝试的进程中托管。使用ApplicationHostHttpRuntimeHttpApplication类,你也可以打磨这些.aspx页面,并从中获得闪亮的HTML输出。

HostingClass host = ApplicationHost.CreateApplicationHost(typeof(HostingClass),
"/virtualpath", "physicalPath");
host.ProcessPage(urlToAspxFile);

你的主持课程:

public class HostingClass : MarshalByRefObject
{
public void ProcessPage(string url)
{
using (StreamWriter sw = new StreamWriter("C:\temp.html"))
{
SimpleWorkerRequest worker = new SimpleWorkerRequest(url, null, sw);
HttpRuntime.ProcessRequest(worker);
}
// Ta-dah!  C:\temp.html has some html for you.
}
}
如果你使用web服务而不是WCF服务,你仍然可以使用标准的。net成员资格来强制认证和登录会话行为 在一组web服务上,类似于你如何通过会员表单认证来保护网站;不需要专门的治疗 和/或soap头实现,只需调用System.Web.Security.FormsAuthentication即可。SetAuthCookie(userName, false)[调用后 成员资格。ValidateUser(用户名,密码)当然]在响应中创建cookie,就像用户已经通过web表单登录一样。 然后,您可以使用Response.Cookies[]检索这个身份验证cookie。值,并将其作为字符串返回给用户 它可以用来验证用户在后续的调用中,通过在Application_BeginRequest中提取
cookie方法从请求调用参数。并在成员身份验证请求之前重新创建身份验证cookie 会员资格提供者会被欺骗,并知道请求已经过身份验证并执行其所有规则。< / p > 示例web方法签名返回此cookie给用户将是: 字符串登录(用户名,密码)

示例后续web方法调用将是: 字符串DoSomething(字符串authcookie,字符串methodParam1,int methodParam2等,等),你需要提取authcookie(这是从登录方法获得的值)参数从请求。InputStreamis < / p > 这也模拟了一个登录会话并调用FormsAuthentication。签出在web方法,如注销(authcookie)将

.

.

很多人提到了如何在重新编译时优化代码。最近我发现我可以在aspx页面中完成大部分的开发(后台代码),完全跳过构建步骤。只需保存文件并刷新页面。你所要做的就是把你的代码包装在下面的标签中:

<script runat="server">


Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Response.Write("Look Ma', I didn't even had to build!")
End Sub


</script>

一旦你完成了,只要移动到代码背后,构建,测试一切工作,瞧!

- d

我使用的一个东西,与多个版本的VB, VBScript和VB一起工作。NET将记录集值转换为字符串,以消除对NULL或空白的多次测试。例如Trim(rsData("FieldName").Value & " ")
在整数值的情况下,这将是:CLng("0" & Trim(rsData("FieldName").Value & " "))

在网站发布并部署到生产服务器后,如果我们需要对服务器端按钮进行一些更改,则单击事件。我们可以在aspx页面中使用__abc0关键字来覆盖现有的单击事件。

例子

代码背后方法

 Protected void button_click(sender object, e System.EventArgs)
{
Response.Write("Look Ma', I Am code behind code!")
}

覆盖方法:

<script runat="server">
Protected void new button_click(sender object, e System.EventArgs)
{
Response.Write("Look Ma', I am overrided method!")
}


</script

通过这种方式,我们可以轻松地修复生产服务器错误,而无需重新部署。

使用configSource拆分配置文件。

你可以在web中使用configSource属性。将配置元素推送到其他.config文件,例如: 而不是:< / p >
    <appSettings>
<add key="webServiceURL" value="https://some/ws.url" />
<!-- some more keys -->
</appSettings>

...您可以将整个appSettings部分存储在另一个配置文件中。下面是新的web.config:

    <appSettings configSource="myAppSettings.config" />

myAppSettings.config文件:

    <appSettings>
<add key="webServiceURL" value="https://some/ws.url" />
<!-- some more keys -->
</appSettings>

这对于您向客户部署应用程序并且不希望他们干扰web的场景非常有用。配置文件本身,只是希望他们能够改变只是几个设置。

裁判:http://weblogs.asp.net/fmarguerie/archive/2007/04/26/using-configsource-to-split-configuration-files.aspx

应用程序变量可以与web应用程序一起使用,以便在整个应用程序中进行通信。它在全局中初始化。Asax文件,并在该web应用程序的页面上使用的所有用户独立于他们创建的会话。

web.config中appsettings元素的“文件”属性。

指定包含自定义应用程序配置设置的外部文件的相对路径。

如果你的应用程序设置很少,需要在不同的环境(prod)上进行修改,这是一个很好的选择。

因为网络的任何变化。配置文件会导致应用程序重新启动,使用单独的文件允许用户修改appSettings部分中的值,而不会导致应用程序重新启动。单独文件的内容与Web中的appSettings部分合并。配置文件。

CompilationMode = "永远"是一个在某些ASP中至关重要的特性。网网站。

如果你有ASP。在asp.net应用程序中,ASPX页面经常通过CMS或其他发布系统生成和更新,使用CompilationMode="Never"是很重要的。

如果没有此设置,ASPX文件更改将触发重新编译,这将快速使您的appdomain重新启动。这可以清除会话状态和httpruntime缓存,更不用说重新编译引起的延迟。

(为了防止重新编译,你可以增加了numRecompilesBeforeAppRestart设置,但这不是理想的,因为它会消耗更多的内存。)

这个特性需要注意的一点是,ASPX页面不能包含任何代码块。为了解决这个问题,可以在自定义控件和/或基类中放置代码。

在ASPX页面不经常更改的情况下,这个特性基本无关紧要。

可以将ASPX页面打包到一个库(.dll)中,并将它们与ASP. dll一起提供。净引擎。

你需要实现自己的VirtualPathProvider,它将通过Relfection特定的DLL加载,或者你可以在路径名中包含DLL名称。由你决定。

当重写VirtualFile。开放方法时,魔术发生了,其中您从汇编类返回ASPX文件作为资源:组装。GetManifestResourceStream。ASP。NET引擎将处理资源,因为它是通过VirtualPathProvider提供的。

这允许插件页面,或者像我所做的那样,使用它来包含带有控件的HttpHandler。

默认情况下,任何web表单页面都继承自System.Web.UI.Page类。如果你想让你的页面继承自自定义基类,继承自System.Web.UI.Page怎么办?

有一种方法可以约束任何页面从您自己的基类继承。只需在web.config上添加新行:

<system.web>
<pages pageBaseType="MyBasePageClass" />
</system.web>

注意:只有当你的类是一个独立的类时,这才有效。我指的是一个没有隐藏代码的类,它看起来像<%@ Page Language="C#" AutoEventWireup="true" %>

模板化用户控件。一旦你知道它们是如何工作的,你就会看到各种各样的可能性。下面是最简单的实现:

TemplatedControl.ascx

这里最棒的事情是使用简单而熟悉的用户控件构建块,并能够使用HTML和一些占位符来布局UI的不同部分。

<%@ Control Language="C#" CodeFile="TemplatedControl.ascx.cs" Inherits="TemplatedControl" %>


<div class="header">
<asp:PlaceHolder ID="HeaderPlaceHolder" runat="server" />
</div>
<div class="body">
<asp:PlaceHolder ID="BodyPlaceHolder" runat="server" />
</div>

TemplatedControl.ascx.cs

这里的“秘密”是使用类型为ITemplate的公共属性,并知道[ParseChildren][PersistenceMode]属性。

using System.Web.UI;


[ParseChildren(true)]
public partial class TemplatedControl : System.Web.UI.UserControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate Header { get; set; }


[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate Body { get; set; }


void Page_Init()
{
if (Header != null)
Header.InstantiateIn(HeaderPlaceHolder);


if (Body != null)
Body.InstantiateIn(BodyPlaceHolder);
}
}

default . aspx

<%@ Register TagPrefix="uc" TagName="TemplatedControl" Src="TemplatedControl.ascx" %>


<uc:TemplatedControl runat="server">
<Header>Lorem ipsum</Header>
<Body>
// You can add literal text, HTML and server controls to the templates
<p>Hello <asp:Label runat="server" Text="world" />!</p>
</Body>
</uc:TemplatedControl>

你甚至会得到内部模板属性的智能感知。因此,如果您在团队中工作,您可以快速创建可重用的UI,以实现与您的团队已经从内置ASP中享受到的相同的可组合性。NET服务器控件。

MSDN的例子(与开头相同的链接)添加了一些额外的控件和命名容器,但只有当你想支持“中继器类型”控件时才有必要这样做。