当使用 jQuery.serialize() 序列化时,如何 *HTML* 对表单内容进行编码?

How can I *HTML*-encode form contents when serializing with jQuery.serialize()?

jQuery表单.serialize()方法将表单内容序列化为字符串并自动URL-编码字符串。然后我的服务器反转这个过程并在反序列化时 URL-解码字符串。

但是我需要能够做的是HTML-在表单出现之前对表单内容进行编码序列化。换句话说,如果用户在我的表单中的文本输入中输入 HTML,我希望使用 HTML 编码使其安全,然后完全按照上述方式传输(使用 URL-正常编码)。

让我举个例子来说明:

当前使用 .serialize() 的实现

  1. User enters My name is <b>Fred</b> into a form input with name Details.
  2. .serialize() serializes this as Details=My+name+is+%3Cb%3EFred%3C%2Fb%3E (URL-encoding)
  3. The server deserializes the string and gets My name is <b>Fred</b> (URL-decoding)

我想要发生的事情

  1. User enters My name is <b>Fred</b> into a form input with name Details.
  2. This gets HTML-encoded to My name is &lt;b&gt;Fred&lt;/b&gt; (HTML-encoding)
  3. .serialize() serializes this as Details=My+name+is+%26lt%3Bb%26gt%3BFred%26lt%3B%2Fb%26gt%3B (URL-encoding)
  4. The server URL-decodes the string and gets My name is &lt;b&gt;Fred&lt;/b&gt; (URL-decoding only)

我希望 .serialize() 可以使用一个参数来指定表单内容应该是 HTML 编码的,但没有这样的运气。其他几个可能的解决方案是:

  1. 在调用 .serialize() 之前遍历表单输入并 HTML 对它们进行编码 "by hand":我宁愿不必这样做,因为它会使代码更混乱和更少健壮。
  2. 修改我的服务器以接受非 HTML 编码的值:出于各种原因我不会深入讨论这是有问题的而不是实际的解决方案。

有没有更简单的解决方案?

默认情况下,输入值将始终进行编码。正如您所说,您必须遍历每个值才能首先解码。您可以使用以下 jQuery 片段来执行此操作:

$('<div/>').html(value).text();

字符串必须 html 编码 在任何其他更改 之后,例如 url 编码或 sql 字符串转义。
所以你首先序列化你的字符串,在链接中使用它,然后反序列化你html-编码它。像以前一样做,但使用下面的功能。

为什么这么重要?

因为我可以自己在 url 中输入一个非 html 转义的字符串,然后可以将其提供给您。你会认为它已经逃脱了,但事实并非如此。解决方案是在页面上打印之前将其转义。

这个问题描述了如何html-转义一个字符串:HtmlSpecialChars equivalent in Javascript?

function escapeHtml(text) {
  var map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };

  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}

当您使用 MVC(见评论)时,只需在需要它的单个 属性 上方应用 [AllowHtml] 属性。

您需要添加以下 using 语句(如果不存在):

using System.Web.Mvc;

注意:如果您还使用 MetadataTypeAttribute 它可能无法开箱即用(但在这种情况下不太可能成为问题)

更新

根据评论,由于您无法修改表单数据属性(动态表单),您可以在控制器中使用以下控制器操作将其关闭

[ValidateInput(false)] 

您还可以更改整个服务器的设置(安全性较低)。请参阅此博客条目:

http://weblogs.asp.net/imranbaloch/handling-validateinputattribute-globally

解决方案是使用 jQuery 的 .serializeArray() 并将 HTML 编码应用于循环中的每个元素。

换句话说,我不得不改变这个:

$.ajax({
    url: form.attr('action'),
    async: false,
    type: 'POST',
    data: form.serialize(),
    success: function (data) {
        //...
    }
});

对此:

// HTML-encode form values before submitting
var data = {};
$.each(form.serializeArray(), function() {
    data[this.name] = this.value
        .replace(/&/g, '&amp;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;');
});

$.ajax({
    url: form.attr('action'),
    async: false,
    type: 'POST',
    data: data,
    success: function (data) {
        //...
    }
});

一个选项可能是直接更新 jquery 库并在 uriEncode 发生之前对 dom 值调用 htmlEncode。

我在 ASP.NET/MVC 应用程序中对此进行了测试,我在 jquery-1.8.2.js 中更新的行(第 7222 行,取决于版本)是:

        s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );

        s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( htmlEncode(value) );

使用您认为合适但似乎有效的任何 htmlEncode 方法。

扩展此方法并调用执行 htmlEncode 的 customSerialize 方法实际上可能更有意义。

我相信这是最简单的方法,意味着您不必在调用序列化之前遍历 dom。