使用ASP.NET MVC中的自定义模型绑定器验证整数字段

时间:2020-12-14 11:50:59

I ran into an issue that I thought was interesting.

我遇到了一个我认为很有趣的问题。

Take an input form that has an input field for an integer value (cost, in my case). I do not want "$100" or "Twenty Dollars" to be sent to the database as it will likely complain. I want to add an error to the model and send it back to the user. Seemed simple ;).

获取一个输入表单,其中包含一个整数值的输入字段(在我的例子中为cost)。我不希望将“$ 100”或“Twenty Dollars”发送到数据库,因为它可能会抱怨。我想向模型添加错误并将其发送回用户。似乎很简单;)。

I am using a custom model binder because I am using some inheritance in my domain model. I have multiple types of events, each type implementing IEvent - thus, I needed a custom model binder.

我正在使用自定义模型绑定器,因为我在我的域模型中使用了一些继承。我有多种类型的事件,每种类型实现IEvent - 因此,我需要一个自定义模型绑定器。

The issue I have is, when I am trying to bind the cost field, and the conversion from string to int fails, I am really not sure how best to handle this.

我遇到的问题是,当我尝试绑定成本字段,并且从字符串到int的转换失败时,我真的不确定如何最好地处理它。

My Custom Model Binder public class EventModelBinder : IModelBinder { #region IModelBinder Members

我的自定义模型Binder公共类EventModelBinder:IModelBinder {#region IModelBinder成员

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        // Trivial code ... binding other fields...

        _event.ProjectedCost = GetA<int>(bindingContext, "ProjectedCost").GetValueOrDefault(-1);

        return _event;
    }

    #endregion

    // From Scott Hanselman's blog :)
    private Nullable<T> GetA<T>(ModelBindingContext bindingContext, string key) where T : struct
    {
        if (String.IsNullOrEmpty(key)) return null;
        ValueProviderResult valueResult;
        Nullable<T> ret;
        //Try it with the prefix...
        bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName + "." + key, out valueResult);
        //Didn't work? Try without the prefix if needed...
        if (valueResult == null && bindingContext.FallbackToEmptyPrefix == true)
        {
            bindingContext.ValueProvider.TryGetValue(key, out valueResult);
        }
        if (valueResult == null)
        {
            return null;
        }

        try
        {
            ret = (Nullable<T>)valueResult.ConvertTo(typeof(T));
        }
        catch
        {
            return null;
        }

        return ret;
    }

This works really well - if Enums are null or not selected, I can validate against them and let the user know it is required. For the cost field, I get -1 if the conversion failed. But, when I go to validate the data on the server and go back to the UI, the Model.Event.ProjectedCost field is null

这非常有效 - 如果Enums为null或未选中,我可以对它们进行验证并让用户知道它是必需的。对于成本字段,如果转换失败,我得-1。但是,当我去验证服务器上的数据并返回UI时,Model.Event.ProjectedCost字段为null

My EventService

// ...
protected bool ValidateEvent(IEvent eventToValidate)
    {            
        if (eventToValidate.ProjectedCost < 0)
            _validationDictionary.AddError("ProjectedCost", "Incorrect format");

        return _validationDictionary.IsValid;
    }

    public bool SaveEvent(IEvent _event)
    {
        if (!ValidateEvent(_event))
            return false;
        try
        {
            _repository.SaveEvent(_event);
            return true;
        }
        catch
        {
            return false;
        }

My Edit.aspx View

我的Edit.aspx视图

<h2>Edit</h2>

<%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>

<% using (Html.BeginForm()) {%>

    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="ProjectedCost">ProjectedCost:</label>
            <%= Html.TextBox("ProjectedCost", Model.Event.ProjectedCost) %>
            <%= Html.ValidationMessage("ProjectedCost", "*") %>
        </p>

        <% Html.RenderPartial(String.Format("~/Views/Shared/{0}Form.ascx", Model.Event.Type), Model.Event, ViewData); %>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>

<% } %>

<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>

In my view, Model.Event.ProjectedCost is null if it doesn't validate, giving me this (Line 54 is the culprit):

在我看来,Model.Event.ProjectedCost如果没有验证则为null,给我这个(第54行是罪魁祸首):

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

Line 52:             <p>
Line 53:                 <label for="ProjectedCost">ProjectedCost:</label>
Line 54:                 <%= Html.TextBox("ProjectedCost", (Model.Event.ProjectedCost == null ? 0 : Model.Event.ProjectedCost)) %>
Line 55:                 <%= Html.ValidationMessage("ProjectedCost", "*") %>
Line 56:             </p>

What I'd kind of like to do is send the value the user entered back to the user, but my custom model binder and/or validation logic seems to be setting something to null?

我喜欢做的是将用户输入的值发送回用户,但我的自定义模型绑定器和/或验证逻辑似乎将某些内容设置为null?

I realize this may not be the easiest question to read, so let me know if I can clarify in any way!

我意识到这可能不是最容易阅读的问题,所以如果我能以任何方式澄清,请告诉我!

2 个解决方案

#1


1  

You could use javascript to restrict the input of the the textbox to just integers.

您可以使用javascript将文本框的输入限制为整数。

#2


0  

Seems that if you call AddModelError without SetModelValue first, you'll get a shiny-new NullReferenceExceptions sometimes:

似乎如果你先调用没有SetModelValue的AddModelError,你有时会得到一个闪亮的新NullReferenceExceptions:

http://www.crankingoutcode.com/?aspxerrorpath=/2009/02/01/IssuesWithAddModelErrorSetModelValueWithMVCRC1.aspx

#1


1  

You could use javascript to restrict the input of the the textbox to just integers.

您可以使用javascript将文本框的输入限制为整数。

#2


0  

Seems that if you call AddModelError without SetModelValue first, you'll get a shiny-new NullReferenceExceptions sometimes:

似乎如果你先调用没有SetModelValue的AddModelError,你有时会得到一个闪亮的新NullReferenceExceptions:

http://www.crankingoutcode.com/?aspxerrorpath=/2009/02/01/IssuesWithAddModelErrorSetModelValueWithMVCRC1.aspx