完全控制您的 Webforms 输出





5.00/5 (2投票s)
完全控制您的 Webforms 输出
我非常喜欢正则表达式……非常。我知道我不是专家,但即使是业余爱好者,只需几个正则表达式,也能做出一些令人惊叹的事情。
WebForms 的输出一直有点……混乱。您的页面通常充斥着大量过长的 ID 和名称,用于实际上不需要的内容(例如,我曾在一个供应商程序中看到一个很棒的 ID: accounts_overview_body_quickAction_quickActionTitleLabel
,仅用于一个 span
元素)。
您还必须接受 ViewState
,它很快就会失控。您可以禁用它,但无论如何它仍然会渲染一些内容。可能不多,但当您不需要时,您宁愿它消失。
WebForms 的另一个令人讨厌之处是您无法在页面级别的表单内放置其他表单。有些浏览器不介意,但其他浏览器根本不起作用。如果您不需要 WebControls
做任何事情(例如保持其状态),那么能够完全删除它们会很好。
掌控您的页面
如我一开始提到的,通过重写页面的 Render
事件,您可以在输出发送给用户之前对其进行完全访问。我之前曾写过一些关于此主题的代码,当时我谈论的是最小化您的内容。如果您读过那篇文章,您就会对下一步会发生什么有一个大致了解。
第一步是实际渲染我们的内容,以便我们可以对其进行操作。不深入细节,我们将把内容写入我们自己的本地 HtmlTextWriter
,以便在发送给实际 HtmlTextWriter
之前对其进行修改。这并不难……
protected override void Render(HtmlTextWriter writer) {
//render our output
StringWriter output = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(output);
base.Render(html);
//get the rendered string
string rendered = output.ToString();
//Make our changes
//... discussed shortly
//cleanup our writers
html.Dispose();
output.Dispose();
//then write it
writer.Write(rendered);
}
很酷,对吧?我们现在有了 string
,可以开始进行更改了。
删除 ViewState
有些页面确实根本不需要 ViewState
。也许是产品信息,也许是几张照片,但页面上没有任何用户需要回发的控件。编写一个正则表达式来删除它并不难。
rendered = Regex.Replace(
rendered,
"<input.*name=\"__VIEWSTATE\"[^>]*>",
string.Empty,
RegexOptions.IgnoreCase
);
这段简短的代码将完全从页面中删除 ViewState
。您可以通过将表达式更改为 "<input.*name=\"__\\w+\"[^>]*>"
来进一步修改它,以删除其他内容(例如 __EVENTSTATE
)。
移除页面表单
正如我在文章前面所说,如果您想在页面上放置其他表单,它们必须在页面表单之外。并非所有浏览器都支持嵌套表单。
使用 WebForms,您可以拥有一个页面表单,在其中使用所有控件,然后在发送给客户端之前删除标记。这样,您就可以使用 WebControls
和嵌套表单。
这个过程自然会稍微复杂一些。
//find all the forms and closed elements
MatchCollection matches = Regex.Matches(
rendered,
"</?form[^>]*>",
RegexOptions.IgnoreCase
);
//create the expression to match for the ID
//master pages rename the form to 'aspnetForm' -- weird
Regex expectedId = new Regex(
string.Format("id=\"?({0}|aspnetForm)\"?", this.Form.ID),
RegexOptions.IgnoreCase
);
//expression to check for a close of a form
Regex closeForm = new Regex(
@"</\s?form\s?>",
RegexOptions.IgnoreCase
);
//loop through and remove the page form
Match open = null;
Match close = null;
int depth = 0;
//we're checking for the open form THEN checking for the
//matching close tag (checking for nesting)
for (int i = 0; i < matches.Count; i++) {
Match match = matches[i];
//check if this is the page form
if (expectedId.IsMatch(match.Value) && !(open is Match)) {
open = match;
}
//if we aren't tracking yet, just continue
if (!(open is Match)) { continue; }
//change the direction of the nesting
depth += closeForm.IsMatch(match.Value) ? -1 : 1;
//if the nesting is back to zero we can assume this
//is the correct tag
if (depth == 0) {
close = match;
break;
}
}
//remove the tags - make sure to start with the close tag
//since this will affect the index of the Matches
if (open is Match && close is Match) {
rendered = rendered.Remove(close.Index, close.Length);
rendered = rendered.Remove(open.Index, open.Length);
}
有了这段代码,我们就可以从页面中移除页面表单,但保留所有其他表单,以便它们可以按照自己的方式处理回发。
值得注意的是,与删除 ViewState
一样,依赖这些功能的postbacks 和 WebControls 很可能将不再起作用(或者至少不像预期那样)。
缩短您的 ID
ASP.NET 4.0 将提供一些有用的功能,以帮助开发人员将有意义的 ID 推送到客户端。目前,ID 是通过父级、父级的父级等的 ID 生成的。这对于避免命名冲突非常有用——但如果您试图做任何客户端操作,那就太糟糕了。不仅如此,有时您最终会在您无意操作的元素上设置 ID,这只是浪费空间。
再次,只需一点正则表达式的魔力,我们就可以更改页面上的 ID。
//lastly, drop any unwanted IDs
MatchCollection extraIds = Regex.Matches(
rendered,
@"<[^>]*actualId=""(?<id>[^""]*)""[^>]*>",
RegexOptions.IgnoreCase
);
//loop backwards to avoid affecting indexes on the matches
for (int i = extraIds.Count; i-- > 0; ) {
Match extra = extraIds[i];
//drop the unwanted parts
string newElement = extra.Value;
string newID = extra.Groups["id"].Value;
//lastly, remove the actual ID field
newElement = Regex.Replace(
newElement,
@"actualId=""[^""]*"" ?",
string.Empty,
RegexOptions.IgnoreCase
);
//if the ID is blank, just remove it
newElement = Regex.Replace(newElement, @"(id|name)=""[^""]*"" ?", (str) => {
if (string.IsNullOrEmpty(newID)) { return string.Empty; }
return string.Concat(
str.Value.StartsWith("id", StringComparison.OrdinalIgnoreCase) ? "id" : "name",
"=\"",
newID,
"\" "
);
});
//finally, replace the new string
rendered = rendered.Remove(extra.Index, extra.Length);
rendered = rendered.Insert(extra.Index, newElement);
}
显然,这至少可以说是自定义解决方案。这里的想法是,我们现在可以将属性“actualId
”添加到我们的控件上,id
和 name
属性将被更新以反映该值。此外,我们的 actualId
属性将完全从元素中删除。
您可以进一步调整代码,以获取所有未以某种方式标记的 ID,以保留其 ID。
绝对的权力导致腐败
深入研究您的渲染输出并更改发送给客户端的内容很有趣。您有很多权力可以完全改变您的内容的外观。但要小心——您更改的内容越多,ASP.NET 的工作方式就越有可能出错。
如果您想出一些很酷的使用方法,请告诉我!
注意:正如下面的评论者所指出的,这不适用于 UpdatePanels
— 至少需要更多工作。UpdatePanels
的内容由管道分隔,并包含所有返回 ID 的内容的字符计数(我有一段时间没用过它们了,所以这是凭记忆)。在不破坏任何东西的情况下进行更改会相当棘手(但并非不可能!!)。