NoSpamEmailHyperlink: 3 电子邮件编码和解码

分享于 

22分钟阅读

Web开发

  繁體

C# is used to encode while rendering to HTML, Javascript is used to decode while rendering in a browser

简介

这是系列六篇文章中第三篇,遵循设计。开发和实际使用一个功能完备的ASP.NET 自定义控件。

全文的完整 List 如下所示:

这些文章不是对自定义控制开发( 有 700页的网页书几乎没有覆盖它)的全面看法,但它们覆盖了很多基本的基础。

目的是在一个完全可以重用和可以自定义的控制( 与许多虚构的例子相反) 上进行这样的操作。

本文检查 Page 类提供的JavaScript呈现方法,以及如何在页加载时将JavaScript应用于控件的所有实例。 它适用于知道如何呈现控件但是新建脚本块的人。

它假定有 C#。.NET 字符串操作。JavaScript v1.0和 WebControl -derived类的基本知识。

从系列的第一篇文章中可以下载到。

编码/解码功能

在服务器上执行编码功能并用 C# 编写,解码发生在客户端,因此是用JavaScript编写的。

本系列的第一篇文章详细定义了用于对电子邮件地址进行编码的算法: NoSpamEmailHyperlink: 1.规格说明

一个微妙的改变会改变整个过程。

编码:C#

protectedvirtualstring Encode (string Unencoded)
{
 // Convert string to char[] char[] scramble = Email.ToCharArray();
 // Initialize variablesint baseNum = ScrambleSeed;
 bool subtract = true;
 // Find the @ symbol and the following. // if either don't exist then we don't have a// valid email address and should return it unencodedint atSymbol = Array.IndexOf(scramble, '@');
 if (atSymbol == -1) atSymbol = 0;
 int stopAt = Array.IndexOf(scramble, '.', atSymbol);
 if (stopAt == -1) stopAt = scramble.Length;
 // Go through the section of the address to be scrambledfor (int i=0; i < stopAt; i++)
 {
 // Find each character in the scramble key stringchar ch = scramble[i];
 int idx = CodeKey.IndexOf(ch);
 // If it isn't there then ignore the characterif (idx <0) continue;
 // Encode the character idx += (subtract? -baseNum : baseNum);
 baseNum -= (subtract? -i : i);
 while (idx <0) idx += CodeKey.Length;
 idx %= CodeKey.Length;
 scramble[i] = CodeKey[idx];
 subtract =!subtract;
 }
 // Return the encoded stringreturnnewstring(scramble);
}

代码不太复杂,我们只需要使用已经定义的算法来编码和调整每个字母数字字符。

服务器的起始点始终是第一个字符,结束点是 @ 符号之后的第一个句点的索引。 如果没有 @ 符号或者句点之后,我们将处理有效的电子邮件地址,但是我们将端点设置为字符串结尾( 例如 )。 对整个字符串进行编码,使算法更完整。

当然,这是任意的决定。 你可以选择根本不编码字符串,甚至抛出异常。 无效的电子邮件地址在这里不应该是一个主要的问题。

解码:JavaScript

function NoSpamEmailHyperlink_DecodeScript(link, seed)
{
 // This is the decoding key for all NoSpamEmailHyperlink_FieldNames objectsvar ky = "yJzdeB4CcDnmEFbZtvuHlI1hA8SiLo9MwfN3O6Y5QaRqKTjUpxVk2WgXrP7Gs0";
 // Store the innerHTML so that it doesn't get// distorted when updating the href latervar storeText = link.innerHTML;
 // Initialize variablesvar baseNum = parseInt(seed);
 var atSym = link.href.indexOf("@");
 if (atSym == -1) atSym = 0;
 var dotidx = link.href.indexOf(".", atSym);
 if (dotidx == -1) dotidx = link.href.length;
 var scramble = link.href.substring(7, dotidx);
 var unscramble = "";
 var su = true;
 // Go through the scrambled section of the addressfor (i=0; i <scramble.length; i++)
 {
 // Find each character in the scramble key stringvar ch = scramble.substring(i,i + 1);
 var idx = ky.indexOf(ch);
 // If it isn't there then add the character// directly to the unscrambled email addressif (idx <0)
 {
 unscramble = unscramble + ch;
 continue;
 }
 // Decode the character idx -= (su? -baseNum : baseNum);
 baseNum -= (su? -i : i);
 while (idx <0) idx += ky.length;
 idx %= ky.length;
 //.. . and add it to the unscrambled email address unscramble = unscramble + ky.substring(idx,idx + 1);
 su =!su;
 }
 // Adjust the href property of the linkvar emAdd = unscramble + link.href.substring(dotidx, link.href.length + 1);
 link.href = "mailto:" + emAdd;
 // If the scrambled email address is also in the text// of the hyperlink, replace itvar findEm = storeText.indexOf(scramble);
 while (findEm> -1)
 {
 storeText = storeText.substring(0, findEm) + emAdd + 
 storeText.substring(findEm + emAdd.length, storeText.length);
 findEm = storeText.indexOf(scramble);
 }
 link.innerHTML = storeText;
}

除了从 C# 转换到JavaScript的明显变化以外,有些原因下降了一些原因,从编码功能来看。

首先,要反转整个编码过程,行

idx += (subtract? -baseNum : baseNum);

已经被反转为

idx -= (su? -baseNum : baseNum);

其次,它接收 link 对象并返回转换后的字符串,从 href 属性解析电子邮件地址,并在链接属性中对原始( 已经编码) 电子邮件地址进行解析,并替换该地址。

注意,在早期版本的Netscape ( 4.x 或者更早版本) 中,无法在 innerHTML 属性中解码电子邮件地址,因此我们删除了存储和修改该属性的代码。

使用以下简单启动脚本块之一,可以在页上解码所有 NoSpamEmailHyperlink s:

IE/Opera
for (i = 0; i <LinkNames.length; i++)
{
 NoSpamEmailHyperlink_DecodeScript(
 document.links.item(LinkNames[i]), Seed[i]
 );
}
其他浏览器
for (i = 0; i <document.links.length; i++)
{
 for (j = 0; j <LinkNames.length; j++)
 {
 if (LinkNames[j] == document.links[i].id)
 {
 NoSpamEmailHyperlink_DecodeScript(
 document.links[i], Seed[j]
 );
 }
 }
}

实际上,我们使用的字段名比这更长,以避免与它的他控件的JavaScript冲突,但现在不重要。

构建 JavaScript

为了构建函数脚本和调用脚本,NoSpamEmailHyperlink 使用了 JavaScriptBuilder 类,如同一作者的另一篇文章所述:

JavaScriptBuilder: 自定义控件控件的JavaScript处理程序类。

这里方法对于 NoSpamEmailHyperlink 项目有两个显著的优点:

  • 使用可以重写属性有效地将字段名插入到脚本中的能力。
  • 压缩该控件版本版本的代码的能力,使得它几乎不可以读的查看器( 电子邮件收割机)。

本系列的final 部分对前者进行了更详细的介绍: NoSpamEmailHyperlink: 6.自定义插件。

后面的JavaScriptBuilder 文章将详细讨论后者,但在这里有一个简短的演示。

列出的JavaScript是使用下面的代码生成的:

protectedvirtualstring GetFuncScript()
{#if DEBUG// Formatted script text in debug version JavaScriptBuilder jsb = new JavaScriptBuilder(true);#else// Compress script text in release version JavaScriptBuilder jsb = new JavaScriptBuilder();#endif jsb.AddLine("function", FuncScriptName, "(link, seed)");
 jsb.OpenBlock(); // function() jsb.AddCommentLine("This is the decoding key for all",
 LinkArrayName, " objects");
 jsb.AddLine("var", CodeKeyName, " ="", CodeKey, "";");
 jsb.AddLine();
 if (!BrowserNeedsHide)
 {
 jsb.AddCommentLine("Store the innerHTML so that it doesn't get");
 jsb.AddCommentLine("distorted when updating the href later");
 jsb.AddLine("var storeText = link.innerHTML;");
 jsb.AddLine();
 }
 jsb.AddCommentLine("Initialize variables");
 jsb.AddLine("var baseNum = parseInt(seed);");
 jsb.AddLine("var atSym = link.href.indexOf("@");");
 jsb.AddLine("if (atSym == -1) atSym = 0;");
 jsb.AddLine("var dotidx = link.href.indexOf(".", atSym);");
 jsb.AddLine("if (dotidx == -1) dotidx = link.href.length;");
 jsb.AddLine("var scramble = link.href.substring(7, dotidx);");
 jsb.AddLine("var unscramble ="";");
 jsb.AddLine("var su = true;");
 jsb.AddLine();
 jsb.AddCommentLine("Go through the scrambled section of the address");
 jsb.AddLine("for (i=0; i <scramble.length; i++)");
 jsb.OpenBlock(); // for (i = 0; i <scramble.length; i++) jsb.AddCommentLine("Find each character in the scramble key string");
 jsb.AddLine("var ch = scramble.substring(i,i + 1);");
 jsb.AddLine("var idx =", CodeKeyName, ".indexOf(ch);");
 jsb.AddLine();
 jsb.AddCommentLine("If it isn't there then add the character");
 jsb.AddCommentLine("directly to the unscrambled email address");
 jsb.AddLine("if (idx <0)");
 jsb.OpenBlock(); // if (idx <0) jsb.AddLine("unscramble = unscramble + ch;");
 jsb.AddLine("continue;");
 jsb.CloseBlock(); // if (idx <0) jsb.AddLine();
 jsb.AddCommentLine("Decode the character");
 jsb.AddLine("idx -= (su? -baseNum : baseNum);");
 jsb.AddLine("baseNum -= (su? -i : i);");
 jsb.AddLine("while (idx <0) idx +=", CodeKeyName, ".length;");
 jsb.AddLine("idx %=", CodeKeyName, ".length;");
 jsb.AddLine();
 jsb.AddCommentLine("... and add it to the unscrambled email address");
 jsb.AddLine("unscramble = unscramble +", CodeKeyName,
 ".substring(idx,idx + 1);");
 jsb.AddLine("su =!su;");
 jsb.CloseBlock(); // for (i = 0; i <scramble.length; i++) jsb.AddLine();
 jsb.AddCommentLine("Adjust the href property of the link");
 jsb.AddLine("var emAdd = unscramble + link.href.substring(", 
 "dotidx, link.href.length + 1);");
 jsb.AddLine("link.href ="mailto:" + emAdd;");
 jsb.AddLine();
 if (!BrowserNeedsHide)
 {
 jsb.AddCommentLine("If the scrambled email address is also in the text");
 jsb.AddCommentLine("of the hyperlink, replace it");
 jsb.AddLine("var findEm = storeText.indexOf(scramble);");
 jsb.AddLine("while (findEm> -1)");
 jsb.OpenBlock(); // while (findEm> -1) jsb.AddLine("storeText = storeText.substring(0, findEm) + emAdd",
 "+ storeText.substring(findEm + emAdd.length, storeText.length);");
 jsb.AddLine("findEm = storeText.indexOf(scramble);");
 jsb.CloseBlock(); // while (findEm> -1) jsb.AddLine();
 jsb.AddLine("link.innerHTML = storeText;");
 }
 jsb.CloseBlock(); // function()return jsb.ToString();
}

BrowserNeedsHide 属性只检查 Page.Request.Browser的版本 4.x 或者 below,在链接属性的innerHTML 中解码电子邮件地址是不可能的。

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]protectedvirtualbool BrowserNeedsHide
{
 get {
 // If the Browser is Netscape (v4.x or less), we cannot change// the innerHTML at run time, so we'll just hide it HttpBrowserCapabilities bc = Page.Request.Browser;
 Version bv = new Version(bc.Version);
 return (bc.Browser.ToLower().IndexOf("netscape") > -1 
 && bv.Major <5);
 }
}

我们已经查看了DLL的调试版本中生成的JavaScript,但是当你生成发布版本时,脚本被压缩了一行。 假设 BrowserNeedsDecodefalse 这将与下面的内容类似:

function NoSpamEmailHyperlink_DecodeScript(link, seed) { var ky ="yJzdeB4CcDnmEFbZtvuHlI1hA8SiLo9MwfN3O6Y5QaRqKTjUpxVk2WgXrP7Gs0"; var storeText =
link.innerHTML; var baseNum = parseInt(seed); var atSym = link.href.indexOf("@");if (atSym == -1) atSym = 0; var dotidx = link.href.indexOf(".", atSym); if(dotidx == -1) dotidx = link.href.length; var scramble = link.href.substring(7,
dotidx); var unscramble = ""; var su = true; for (i=0; i <scramble.length; i++)
{ var ch = scramble.substring(i,i + 1); var idx = ky.indexOf(ch); if (idx <0)
{ unscramble = unscramble + ch; continue; } idx -= (su? -baseNum : baseNum);
baseNum -= (su? -i : i); while (idx <0) idx += ky.length; idx %= ky.length;
unscramble = unscramble + ky.substring(idx,idx + 1); su =!su; } var emAdd =
unscramble + link.href.substring(dotidx, link.href.length + 1); link.href ="mailto:" + emAdd; var findEm = storeText.indexOf(scramble); while (findEm> 
-1) { storeText = storeText.substring(0, findEm) + emAdd +
storeText.substring(findEm + emAdd.length, storeText.length); findEm =
storeText.indexOf(scramble); } link.innerHTML = storeText; }

它采用全功能的JavaScript处理软件来解释这种性质的代码。 任何将来设置的邮件下载器都会非常昂贵。 但是,当前版本的IE。Netscape和 Opera 在处理这里代码时不会出现问题。

还要注意,这里代码一旦压缩,就小于 1kb 个长度,因这里不会出现大量的带宽。

渲染 JavaScript

必要的JavaScript在 OnPreRender 事件的页面中注册。 这是在设计器中呈现时避免编写脚本块的主要优点,它不支持 JavaScript。 但是要注意:RegisterClientScriptBlock() 在控件的生命周期后注册时不会工作。 客户端脚本块在控件本身之前呈现,因这里如果 register 在 Render() 中重写,则不会在页面呈现客户端脚本。

protectedoverridevoid OnPreRender(EventArgs e)
{
 base.OnPreRender (e);
 if (Email.Length >0)
 {
 // Register the Control's ID and Decode seed in scripted arrays Page.RegisterArrayDeclaration(
 LinkArrayName, String.Format(""{0}"", ClientID)
 );
 Page.RegisterArrayDeclaration(
 SeedArrayName, String.Format(""{0}"", ScrambleSeed)
 );
 // Register the decoder function script blockif (!Page.IsClientScriptBlockRegistered(FuncScriptName))
 Page.RegisterClientScriptBlock(FuncScriptName, GetFuncScript());
 // Register the calling script blockif (!Page.IsStartupScriptRegistered(CallScriptName))
 Page.RegisterStartupScript(CallScriptName, GetCallScript());
 }
}

解码函数注册为客户端脚本 block,以便在控件本身的HTML之前下载。 如果我们不这样做,在 56Kbps 调制解调器下载一个缓慢的下载将在下载和运行解码脚本之前显示链接。 这可以在更改之前显示编码地址。

然而,调用解码函数的脚本 block 被注册为启动脚本 block,以便在控件和 array 呈现之后下载。 因为脚本不包含函数,因此将立即运行。 因为它需要访问控件HTML和数组,所以我们需要先下载它们。

每个脚本 block 只应注册一次,不管在页面上显示多少个控件实例。 事实上,框架只允许你一次执行脚本。 如果用相同的键两次调用 Page.RegisterStartupScript,它将忽略第二个调用。 但是,让自己像上面看到的那样,更加清晰和高效。

另一方面,array 注册方法将从该控件的多个实例中调用,创建一个超链接id和解码种子的匹配。

注意,数组和脚本块的注册名称是从属性中获取的。 有以下两个原因:

  • 属性可以包含控件类型的NAME,这样继承的控件将不会冲突。
  • 通过更改继承控件中的NAME,可以更难识别 array 名称。

本主题将在后面的文章中更详细地介绍: NoSpamEmailHyperlink: 6.自定义插件。

注意,在将字符串值设置为时,需要包含引号。 在 page NoSpamEmailHyperlink controls page"ns1""ns5"registration registration registration registration registration registration registration registration registration将生成以下代码块。

var NoSpamEmailHyperlink_LinkNames = 
 new Array("ns1", "ns2", "ns3", "ns4", "ns5");var NoSpamEmailHyperlink_Seeded = new Array("23", "24", "25", "26", "27");
.NET"功能"警报

你可能会问自己为什么我们可以删除引号,我们不会将种子作为整数注册。 当我们有5 项的array 时,这是很好的,如 上面。 但是,如果你只想在页面上创建控件的一个实例,生成的代码将类似于:

var NoSpamEmailHyperlink_LinkNames = new Array("ns1");var NoSpamEmailHyperlink_Seeded = new Array(23);

代码的第二行不创建包含一个值为 23的项的array。 相反,它创建了 23个"未定义"项的array。 这可以能是一个难以追踪的Bug,因为我学习了很难的方法,所以使用引号对所有 array 注册。

结束语

结合第二和第三篇文章的代码,我们现在有一个完全功能的自定义控件来编码和解码给定的电子邮件 href=":。" 属性和超链接的正文。

现在,.Encode() 方法实际执行一些工作,因此使用以下设置的NoSpamEmailHyperlink 控件:

<cpspam:NoSpamEmailHyperlinkid="nseh"runat="server"Email="pdriley@santt.com"ScrambleSeed="181"> Paul Riley (pdriley@santt.com)</cpspam:NoSpamEmailHyperlink>

将呈现以下 HTML。

Netscape 4.x 或者 below
<aid="nseh"href="mailto:WsyhiJc@7kDit.com"> Paul Riley ([Hidden])</a>
其他浏览器
<aid="nseh"href="mailto:WsyhiJc@7kDit.com"> Paul Riley (WsyhiJc@7kDit.com)</a>

编码地址必须是有效的电子邮件地址,并且只有在收到验证软件( 已经比没有那些没有) 后才能识别为无效的电子邮件地址。

减少我们可以用于电子邮件收取器的数据可以减少他们的利润,以及保护我们的注册用户。

在本文中,我们检查了JavaScript注册函数以及它们如何用于在单页上操作控件的任意数量的实例。

我们还研究了一个简单但非常强大的加密算法示例,以及如何在 C# 中实现这个算法,同时轻松地在JavaScript中。

接下来,我们将在所见即所得设计器( 如 Visual Studio. NET. ) 中将新控件更专业化

修订历史

  • 1.0 12 -Oct-2003 - 已经创建。
  • 1.1 23 -Oct-2003 - 将 innerText 更改为 innerHTML。 用于早期Netscape的Conditionalized解码脚本。

相关文章