如何在AddModelError消息中包含一个链接?

时间:2011-07-09 22:59:51

标签: c# .net asp.net-mvc-2

我想添加一个ModelState错误,如下所示:

ModelState.AddModelError("", "Some message, <a href="/controller/action">click here</a>)

但是,链接不会被编码,因此显示为文本。我尝试使用

<%= Html.ValidationSummary(true, "Some message")

而不是

<%: Html.ValidationSummary(true, "Some message")

但没有运气。

任何人都知道如何让这个工作?

干杯

4 个答案:

答案 0 :(得分:19)

最简单的方法(也适用于MVC 4):

在控制器中:

ModelState.AddModelError("", "Please click <a href=\"http://stackoverflow.com\">here</a>");

在视图中:

if (ViewData.ModelState.Any(x => x.Value.Errors.Any()))
{
@Html.Raw(HttpUtility.HtmlDecode(Html.ValidationSummary().ToHtmlString()))
}

答案 1 :(得分:5)

ValidationSummary助手会自动对所有邮件进行HTML编码。一种可能的解决方法是编写自定义验证摘要助手,该助手不对消息进行HTML编码:

public static class HtmlExtensions
{
    public static MvcHtmlString MyValidationSummary(this HtmlHelper htmlHelper, bool excludePropertyErrors, string message)
    {
        var formContext = htmlHelper.ViewContext.ClientValidationEnabled ?  htmlHelper.ViewContext.FormContext : null;
        if (formContext == null && htmlHelper.ViewData.ModelState.IsValid)
        {
            return null;
        }

        string messageSpan;
        if (!string.IsNullOrEmpty(message))
        {
            TagBuilder spanTag = new TagBuilder("span");
            spanTag.SetInnerText(message);
            messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
        }
        else
        {
            messageSpan = null;
        }

        var htmlSummary = new StringBuilder();
        TagBuilder unorderedList = new TagBuilder("ul");

        IEnumerable<ModelState> modelStates = null;
        if (excludePropertyErrors)
        {
            ModelState ms;
            htmlHelper.ViewData.ModelState.TryGetValue(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix, out ms);
            if (ms != null)
            {
                modelStates = new ModelState[] { ms };
            }
        }
        else
        {
            modelStates = htmlHelper.ViewData.ModelState.Values;
        }

        if (modelStates != null)
        {
            foreach (ModelState modelState in modelStates)
            {
                foreach (ModelError modelError in modelState.Errors)
                {
                    string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */);
                    if (!String.IsNullOrEmpty(errorText))
                    {
                        TagBuilder listItem = new TagBuilder("li");
                        listItem.InnerHtml = errorText;
                        htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
                    }
                }
            }
        }

        if (htmlSummary.Length == 0)
        {
            htmlSummary.AppendLine(@"<li style=""display:none""></li>");
        }

        unorderedList.InnerHtml = htmlSummary.ToString();

        TagBuilder divBuilder = new TagBuilder("div");
        divBuilder.AddCssClass((htmlHelper.ViewData.ModelState.IsValid) ? HtmlHelper.ValidationSummaryValidCssClassName : HtmlHelper.ValidationSummaryCssClassName);
        divBuilder.InnerHtml = messageSpan + unorderedList.ToString(TagRenderMode.Normal);

        if (formContext != null)
        {
            // client val summaries need an ID
            divBuilder.GenerateId("validationSummary");
            formContext.ValidationSummaryId = divBuilder.Attributes["id"];
            formContext.ReplaceValidationSummary = !excludePropertyErrors;
        }
        return MvcHtmlString.Create(divBuilder.ToString());
    }

    private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState)
    {
        if (!String.IsNullOrEmpty(error.ErrorMessage))
        {
            return error.ErrorMessage;
        }
        if (modelState == null)
        {
            return null;
        }

        string attemptedValue = (modelState.Value != null) ? modelState.Value.AttemptedValue : null;
        return String.Format(CultureInfo.CurrentCulture, "The value {0} is invalid.", attemptedValue);
    }
}

然后:

<%= Html.MyValidationSummary(true, "Some message") %>

当然,通过这样做,您应该小心,因为您将这些错误消息放入文本中,因为它们现在不会被HTML编码。这意味着,如果您想在邮件中使用<>&等特殊字符,则需要自行对其进行HTML编码,否则标记会中断。

答案 2 :(得分:4)

<div class="validation-summary-errors">
    <ul>
    <% foreach(var error in ViewData.ModelState.Where(s => s.Value.Errors.Count!=0).SelectMany(s => s.Value.Errors)) { %>
        <li><%= error.ErrorMessage %></li>
    <% } %>
    </ul>
</div>

或剃须刀:

<div class="validation-summary-errors">
    <ul>
    @foreach(var error in ViewData.ModelState.Where(s => s.Value.Errors.Count!=0).SelectMany(s => s.Value.Errors)) {
        <li>@Html.Raw(error.ErrorMessage)</li>
    }
    </ul>
</div>

答案 3 :(得分:1)

使用其他答案中建议的HttpUtility.HtmlDecode或@ Html.Raw会引起XSS问题,因为用户输入反映为错误消息的一部分。

默认情况下,ASP.NET Framework将阻止HTML并返回不反映原始值的验证错误,即ParameterName不允许使用HTML

但是 仅对字符串属性执行此操作。

对于没有String数据类型,AttemptedValue在被反映之前已被编码;应用HttpUtility.HtmlDecode或以其他方式将自己的HTML注入到验证消息中,并编写自定义代码以在 any 验证消息中呈现HTML,如果您没有任何字符串,则会引入一个反映的XSS错误参数具有默认验证。

宁可禁用内置行为,也不用担心将来会在哪里设置消息,您应该编写一个自定义ValidationSummary帮助程序,该帮助程序汇总ModelState错误和您知道包含的一组自定义验证错误HTML,最重要的是您知道它不包含任何用户输入。