最近在用
ASP.NET MVC 3
,在通过
TinyMCE HTML
编辑器,向服务器端输入
HTML
代码时,收到了下面这个错误信息:
异常详细信息: System.Web.HttpRequestValidationException: 从客户端(test="<a>adfasdf</a>")中检测到有潜在危险的 Request.Form 值。
很明显,这是ASP.NET为了阻止跨站脚本攻击所实现的防御措施,然而在我的情况下,我的确需要ASP.NET临时关闭这个检查机制,因为这个时候我需要保存原始的HTML文本。在网上搜了一下,发现很多人的解决方案有点粗暴,要么是直接将整个站点的HTML跨站攻击检测机制禁止掉:
- 将web.config文件里,添加上<httpRuntime requestValidationMode="2.0" />。
- 然后再<pages>节设置validateRequest="false" 禁用请求验证。
安全一点的,也是把整个页面的检测机制禁止掉—即通过在webform页面的Page指令禁用请求验证,或在MVC的Controller上加上[ValidateInput(false)]属性。下面这个razor语法的页面就可以让你重现这个问题:
代码
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
<
head
>
<
title
>
Sample Page
</
title
>
<
script
src
="@Url.Content("
~/Scripts/jquery-1.4.2.min.js")" type
="text/javascript"
></
script
>
<
script
type
="text/javascript"
src
="@Url.Content("
~/Scripts/tinymce/tiny_mce.js")"
></
script
>
<
script
type
="text/javascript"
src
="@Url.Content("
~/Scripts/tinymce/jquery.tinymce.js")"
></
script
>
<
script
language
="javascript"
type
="text/javascript"
>
$(document).ready(
function
() {
$(
'
#test
'
).tinymce({
mode:
"
textareas
"
,
theme:
"
simple
"
});
});
</
script
>
</
head
>
<
body
>
<
form
method
="post"
action
=""
>
<
label
for
="test"
>
测试
</
label
>
<
textarea
name
="test"
id
="test"
>
</
textarea
>
<
br
/>
<
input
type
="submit"
value
="提交"
/>
</
form
>
<
br
/>
@if ( IsPost ) {
<
div
>
@Request["test"]
</
div
>
}
</
body
>
</
html
>
在MVC 3里,请求验证以及集成到HttpModule这一层了,也就是说,即使你用上面那两个方法,有可能还是禁用不了请求验证。这是因为上面两个方法是在处理Page的时候才有效,而HttpModule在Page接收到请求之前就已经执行请求验证了。在MVC 3里,你可以告诉请求验证程序对一个字段忽略请求验证,但依然对其他字段执行请求验证。通过下面这条语句就可以指明你希望跳过验证的字段了—参数就是表单里输入控件的name属性值:
@{
var text
=
Request.Unvalidated().Form[
"
test
"
];
}
另外,请注意Unvalidated这个函数,你只有在安装了WebMatrix Beta2以及ASP.NET MVC 3 RC后才有。
装好WebMatrix Beta 2,再次访问网页,你就可以看到在test文本框里输入的html字符串可以正常通过验证,但是在其他文本框输入的html字符串则不能通过验证。
但是,接下来又有另外一个问题,当你回显HTML字符串的时候,它并没有跟你想象的那样作为HTML代码插入,而被当作了普通文本输出,如下图:
这是因为,在MVC 3的Razor引擎里,默认情况下,所有的字符串都会先被转义以后才能输出,也就是说,下面这段代码:
实际上是被Razor引擎替换成下面的代码:
<
div
>
@Server.EncodeHtml(text)
</
div
>
如果你需要修改这个行为,应该将代码改成下面这样:
var wrapper
=
new
HtmlString(text);
<
div
>
@wrapper
</
div
>
下面是完整的代码(注意,你需要安装了WebMatrix Beta2以及ASP.NET MVC 3 RC才能顺利编译):
代码
@{
var text = Request.Unvalidated().Form["test"];
}
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html
xmlns
="http://www.w3.org/1999/xhtml"
>
<
head
>
<
title
>
Sample Page
</
title
>
<
script
src
="/Scripts/jquery-1.4.2.min.js"
type
="text/javascript"
></
script
>
<
script
type
="text/javascript"
src
="/Scripts/tinymce/tiny_mce.js"
></
script
>
<
script
type
="text/javascript"
src
="/Scripts/tinymce/jquery.tinymce.js"
></
script
>
<
script
language
="javascript"
type
="text/javascript"
>
$(document).ready(
function
() {
$(
'
#test
'
).tinymce({
mode:
"
textareas
"
,
theme:
"
simple
"
});
});
</
script
>
</
head
>
<
body
>
<
form
method
="post"
action
=""
>
<
label
for
="test"
>
测试
</
label
>
<
textarea
name
="test"
id
="test"
>
</
textarea
>
<
br
/>
<
input
type
="submit"
value
="提交"
/>
</
form
>
<
br
/>
@if ( IsPost ) {
var wrapper = new HtmlString(text);
<
div
>
@wrapper
</
div
>
}
</
body
>
</
html
>