9 Stimmen

ASP.NET MVC Modellbindung und Validierung Frage

Ich versuche, MVC für ein neues Projekt zu verwenden, nachdem ich mit all den Beispielen und Tutorials und so um den Block gegangen bin. Allerdings habe ich eine harte Zeit herauszufinden, wo bestimmte Dinge stattfinden sollte.

Ich habe zum Beispiel eine Entität namens Profil. Diese Entität enthält den normalen Profiltyp zusammen mit einer DateOfBirth-Eigenschaft vom Typ DateTime. Auf dem HTML-Formular ist das Feld für das Geburtsdatum in 3 Felder aufgeteilt. Ich weiß, dass ich einen benutzerdefinierten Modellbinder verwenden kann, um dies zu behandeln, aber was ist, wenn das eingegebene Datum kein gültiges Datum ist? Sollte ich das im Model Binder überprüfen? Sollte die gesamte Validierung im Modellbinder stattfinden? Ist es in Ordnung, nur ein paar Dinge im Model Binder zu validieren und den Rest im Controller oder dem Modell selbst?

Hier ist der Code, den ich jetzt habe, aber er sieht für mich einfach nicht richtig aus. Sieht schmutzig oder stinkend aus.

namespace WebSite.Models
{
    public class ProfileModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            DateTime birthDate;

            var form = controllerContext.HttpContext.Request.Form;
            var state = controllerContext.Controller.ViewData.ModelState;

            var profile = new Profile();
            profile.FirstName = form["FirstName"];
            profile.LastName = form["LastName"];
            profile.Address = form["Address"];
            profile.Address2 = form["Address2"];
            profile.City = form["City"];
            profile.State = form["State"];
            profile.Zip = form["Zip"];
            profile.Phone = form["Phone"];
            profile.Email = form["Email"];
            profile.Created = DateTime.UtcNow;
            profile.IpAddress = controllerContext.HttpContext.Request.UserHostAddress;

            var dateTemp = string.Format("{0}/{1}/{2}",
                form["BirthMonth"], form["BirthDay"], form["BirthYear"]);

            if (string.IsNullOrEmpty(dateTemp))
                state.AddModelError("BirthDate", "Required");
            else if (!DateTime.TryParse(dateTemp, out birthDate))
                state.AddModelError("BirthDate", "Invalid");
            else
                profile.BirthDate = birthDate;

            return profile;
        }        
    }
}

Aufbauend auf dem Beispielcode oben, wie würden Sie die Validierung Nachricht für ein 3-Teil-Feld tun? Im obigen Fall verwende ich einen völlig separaten Schlüssel, der nicht wirklich einem Feld im Formular entspricht, weil ich nicht möchte, dass eine Fehlermeldung neben allen 3 Feldern erscheint. Ich möchte, dass sie nur rechts neben dem Feld "Jahr" erscheint.

0 Stimmen

Sind Sie auf der Suche nach einer Modellvalidierung oder einer Formularvalidierung? Ich würde beides empfehlen. Auf diese Weise können Sie moq das Modell als auch eine reiche UI auf dem Frontend bieten.

5voto

OdeToCode Punkte 4796

Ich halte es für sinnvoll, die Validierung im Modellordner durchzuführen. Wie Craig hervorhebt, ist die Validierung jedoch größtenteils Eigentum der Geschäftsdomäne:

  1. Manchmal ist Ihr Modell nur ein dummes Präsentationsmodell und kein Geschäftsobjekt.
  2. Es gibt verschiedene Mechanismen, mit denen Sie das Validierungswissen in den Modellbinder einbringen können.

Thomas gibt Ihnen ein Beispiel für Nr. 1.

Ein Beispiel für Nr. 2 ist die deklarative Beschreibung von Validierungswissen mit Hilfe von Attributen (wie dem DataAnnotation-Attribut [Required]) oder die Einbindung eines Validierungsdienstes der Geschäftsschicht in einen benutzerdefinierten Modellbinder. In diesen Situationen ist der Modellbinder ein idealer Ort, um die Validierung zu übernehmen.

Abgesehen davon sind die Modellbindung (Auffinden, Konvertieren und Verschieben von Daten in ein Objekt) und die Validierung (Daten entsprechen unseren Spezifikationen) zwei getrennte Bereiche. Man könnte argumentieren, dass sie getrennte Phasen/Komponenten/Erweiterungspunkte sein sollten, aber wir haben, was wir haben, obwohl der DefaultModelBinder eine gewisse Unterscheidung zwischen diesen beiden Aufgaben macht. Wenn Sie lediglich eine Validierung für einen bestimmten Objekttyp bereitstellen möchten, können Sie vom DefaultModelBinder ableiten und die Methode OnPropertyValidating für Validierungen auf Eigenschaftsebene oder OnModelUpdated überschreiben, wenn Sie eine ganzheitliche Sicht benötigen.

Hier ist der Code, den ich jetzt habe, aber er sieht für mich einfach nicht richtig aus. Scheint schmutzig oder stinkend.

Für Ihren spezifischen Code würde ich versuchen, einen Modellbinder nur für DateTime zu schreiben. Der Standardmodellbinder kann sich um die Bindung von Vorname, Nachname usw. kümmern und an Ihren benutzerdefinierten Modellbinder delegieren, wenn er eine DateTime-Eigenschaft auf dem Profil erreicht. Versuchen Sie außerdem, den valueProvider im bindingContext zu verwenden, anstatt direkt zum Formular zu gehen. Diese Dinge können Ihnen mehr Flexibilität geben.

Weitere Gedanken hier: 6 Tipps für ASP.NET MVC-Modellbindung .

0 Stimmen

Dies sollte für serverseitige Validierungen funktionieren (wenn das Formular abgeschickt wird), aber was ist mit clientseitigen? Ich bin in der gleichen Situation wie der OP, aber kann nicht herausfinden, wie man in eine neue Regel in der Client-seitigen Validierungs-Engine (anstatt mit drei Validierungen - eine für jedes Feld) zu jimmy. Was denken Sie?

0 Stimmen

Ja, die Erweiterung der clientseitigen Validierungen ist ein wenig komplex, aber möglich. Was ich in Betracht ziehen würde, ist der Aufbau einer UI, die es schwer für Benutzer macht, ungültige Werte einzugeben (mit Spinnern oder Schieberegler, vielleicht), und dann fangen die Randfälle nur auf dem Server.

4voto

Thomas Eyde Punkte 3702

Manchmal handelt es sich bei dem Modell um ein Ansichtsmodell und nicht um ein Domänenmodell. In diesem Fall könnten Sie davon profitieren, diese beiden zu trennen und das Ansichtsmodell so zu gestalten, dass es zu Ihrer Ansicht passt.

Jetzt können Sie das Ansichtsmodell die Eingabe validieren lassen und die drei Felder in eine DateTime . Dann kann er das Domänenmodell aktualisieren:

public ActionResult SomeAction(ViewModel vm)
{
    if (vm.IsValid)
    {
        var dm = repositoryOrSomething.GetDomainModel();
        vm.Update(dm);
    }

    // more code...
}

2voto

Ich hatte genau die gleiche Situation den anderen Tag... unten ist mein Modell Bindungscode. Im Grunde bindet es alle DateTime? Felder eines Modells zu Monat/Tag/Jahr-Felder aus einem Formular (wenn möglich) So, ja, ich füge in der Validierung hier, da es scheint angemessen, dies zu tun.

public class DateModelBinder : DefaultModelBinder  
    {

        protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
        {

            if (propertyDescriptor.PropertyType == typeof(DateTime?))
            {
                string DateMonth = _GetDateValue(bindingContext, propertyDescriptor.Name + "Month");
                string DateDay = _GetDateValue(bindingContext, propertyDescriptor.Name + "Day");
                string DateYear = _GetDateValue(bindingContext, propertyDescriptor.Name + "Year");
                // Try to parse the date if we have at least a month, day or year
                if (!String.IsNullOrEmpty(DateMonth) || !String.IsNullOrEmpty(DateDay) || !String.IsNullOrEmpty(DateYear))
                {
                    DateTime fullDate;
                    CultureInfo enUS = new CultureInfo("en-US");
                    // If we can parse it, set the model property
                    if (DateTime.TryParse(DateMonth + "/" + DateDay + "/" + DateYear,
                                         enUS,
                                         DateTimeStyles.None, out fullDate))
                    {
                        SetProperty(controllerContext, bindingContext, propertyDescriptor, (DateTime?)fullDate);
                    }
                    // The date is invalid, so we need to add a model error
                    else
                    {
                        string ModelPropertyName = bindingContext.ModelName;
                        if(ModelPropertyName != "")
                        {
                            ModelPropertyName += ".";
                        }
                        ModelPropertyName += propertyDescriptor.Name;
                        bindingContext.ModelState.AddModelError(ModelPropertyName, "Invalid date supplied for " + propertyDescriptor.Name);
                    }
                }
                return;
            }
            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }

        // Get a property from binding context
        private string _GetDateValue(ModelBindingContext bindingContext, string key)
        {
            ValueProviderResult valueResult;
            bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName + "." + key, out valueResult);
            //Didn't work? Try without the prefix if needed...  
            // && bindingContext.FallbackToEmptyPrefix == true
            if (valueResult == null)
            {
                bindingContext.ValueProvider.TryGetValue(key, out valueResult);
            }
            if (valueResult == null)
            {
                return null;
            }
            return (string)valueResult.ConvertTo(typeof(string));
        }

    }

Hinweis: Ich hatte einige Probleme mit bindingContext.FallbackToEmptyPrefix immer falsch ... kann keine nützliche Informationen dazu finden, aber Sie erhalten die Idee.

0 Stimmen

Sieht aus wie eine Kopie des Codes aus einem Blogbeitrag von Scott Hanselman: hanselman.com/blog/

1voto

Craig Stuntz Punkte 124703

Die Validierung sollte an mehreren Stellen erfolgen, je nach der Funktionalität der einzelnen Stellen. Wenn Ihr Modellbinder beispielsweise die übermittelten Werte nicht in einen korrekten DateTime-Wert umwandeln kann, dann kann er einen Modellstatusfehler hinzufügen. Wenn andererseits Ihre Geschäftslogik erfordert, dass das Datum innerhalb eines bestimmten Bereichs liegt, wäre dies nicht angemessen, und das Modell Binder zu tun; es sollte in der Geschäftslogik Schicht sein. Auch Controller können potenziell Validierungsfehler hinzufügen, wenn z. B. das Bearbeitungsmodell nicht in ein Entitätsmodell umgewandelt werden kann.

Ein Validierungsrahmen wie xVal macht dies viel einfacher.

0voto

datacop Punkte 592

Le site Kontakt Manager Beispielanwendung auf der http://www.asp.net/mvc Seite hat eine ausgezeichnete Beschreibung der Trennung Ihrer Validierungslogik in eine Dienstschicht von Ihrem Controller und Modell.

Es lohnt sich zu lesen

0 Stimmen

Ja, ich habe die App gesehen. Das tut nichts für die spezielle Frage, die ich gestellt habe. Mein Problem ist, dass das Modell ein DateTime-Feld enthält. Das Formular stellt dies jedoch als 3 Textfelder dar. Daher muss ich irgendeine Form der Validierung zu tun, bevor das Modell sogar gebunden ist, und ich versuche, den besten Weg, dies zu tun zu finden.

1 Stimmen

Werfen Sie einen Blick auf diesen Beitrag: hanselman.com/blog/

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X