开发者

How to include a link in AddModelError message?

I want to a开发者_如何学编程dd a ModelState error, like so:

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

However, the link doesn't get encoded, and so is displayed like text. I tried using

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

instead of

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

But no luck.

Anyone have any idea how to get this working?

Cheers


The simpliest way (works also with MVC 4):

In controller:

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

In view:

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


<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>

or in razor:

<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>


The ValidationSummary helper automatically HTML encodes all messages. One possible workaround is to write a custom validation summary helper which doesn't HTML encode the messages:

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);
    }
}

and then:

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

Of course by doing this you should be careful as what text you are putting into those error messages as now they will not be HTML encoded. This means that if you ever wanted to use some special characters such as <, >, & into your message you will need to HTML encode it yourself or the markup will break.


Using HttpUtility.HtmlDecode or @Html.Raw as suggested in other answers introduces a reflected XSS issue because user input is reflected as part of the error message.

The ASP.NET Framework by default will block HTML and return a validation error which doesn't reflect the original value i.e. HTML not allowed for ParameterName

However it only does this for String properties.

For none String data types the AttemptedValue is encoded before being reflected; applying HttpUtility.HtmlDecode or otherwise injecting your own HTML into validation messages and writing custom code to render HTML in any validation message, introduces a reflected XSS bug if you have any none string parameters with default validation.

Rather that disabling built in behaviour, given that you don't necessarily know where the message will be set in the future, you should write a custom ValidationSummary helper which aggregates ModelState errors and a collection of custom validation errors which you know contain HTML, and crucially that you know don't contain any user input.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜