Where should I validate the data entered by a user when I work in 3 layers?

0

Cordial greeting. I am working in a program with the 3 layers model (Vista, model and Controller) but I have doubts about which layer I should validate the information entered by a user with this I mean verify that a field is not empty, that it does not enter characters Alphabetics in a field that only expects numbers for example a phone number.

    
asked by Diego Giraldo 31.03.2018 в 03:23
source

2 answers

2

I use MVVM because I honestly think that MVC is too old for modern graphical interfaces, it's fine for more static things like the web.

I'll explain how I validate my projects.

The base class I use for my ViewModels implements the IDataErrorInfo interface and I use the dataannotations to validate, here is an example of a ViewModel in my MVVM framework:

public class PersonaViewModel : ViewModel
{
    readonly DateTime MinDate = new DateTime(1940, 01,01);
    readonly DateTime MaxDate = DateTime.Today;

    // constructor
    public PersonaViewModel()
    {
        // la propiedad "Edad" se recalcula automáticamente cada que la propiedad FechaNacimiento cambia de valor, es como en excel cuando le pones fórmula a una celda
        this.Edad = this.CreateFormula(
            property: This => This.FechaNacimiento,
            formula: value => DateTime.Today.AddTicks(-value.Ticks).Year - 1);

        // puedeGuardar es una variable que cambia automáticamente cuando el viewmodel cambia su estado de validación
        // ValidationState es una propiedad de la clase ViewModel que contiene el estado de validación del ViewModel que estás creando,
        //  y cambia cada vez que alguna propiedad del viewmodel es modificada
        //  y tiene una propiedad "HasErrors" que es true si alguna propiedad del ViewModel no pasa la validación
        var puedeGuardar = this.ValidationState
            .When(state => state.HasErrors == false); // puede ser !state.HasErrors pero lo pongo así por claridad

        // al comando le paso "puedeGuardar" para indicar cuándo va a estar "enabled" el botón donde lo voy a bindear en la vista
        this.GuardarDatos = new AsyncCommand(
            method = this.Guardar,
            isEnabled = puedeGuardar);
    }

    // este comando guarda los datos capturados
    public Command GuardarDatos { get; }

    [Required(ErrorMessage = "El nombre no puede estar vacío")]
    [StringLength(50, ErrorMessage = "El nombre no puede tener mas de 50 caracteres")]
    public string Nombre
    {
         get => Get<string>();
         set => Set(value);
    }

   [ValidateMethod] // utiliza un método "IsXXXXXValid" para validad donde XXXXX es un nombre de propiedad
   public DateTime FechaNacimiento
   {
       get => Get<DateTime>();
       set => Set(value);
   }

   // esta propiedad se calcula automáticamente cada que la fecha de nacimiento cambia, es inicializada en el constructor
   public Calculated<int> Edad { get;}

   // este método valida la propiedad fechanacimiento
   private bool IsFechaNacimientoValid(DateTime date)
   {
       return date >= MinDate && date <= MaxDate;
   }

    // en este método guardo los datos
   private async Task Guardar()
   {
       // aquí guardo los datos a la base de datos
   }
}

As you can see, the class inherits from ViewModel, which implements validation in its properties, in the form I only add a control type ErrotProvider (already comes standard), and I only override a method where I "bin" each property of the ViewModel with each control in the form:

// formulario, hereda de una clase Form preparada con funcionalidad para bindeo
public class PersonaView : FormBase, IView<PersonaViewModel>
{

    public PersonaViewModel ViewModel { get; set; }

    // dentro de la clase de mi formulario sobrecargo el método OnBinding
    private override void OnBinding(Binder binder)
    {
         binder.Add(this.ViewModel, vm => vm.Nombre, This => This.nombreTextBox.Text);

         binder.Add(this.ViewModel, vm => vm.FechaNacimiento, This => This.fechaDateTimePicker.Value);

         binder.AddReadOnly(this.ViewModel, vm => vm.Edad.Value, This => This.EdadLabel.Text);

         binder.AddCommand(this.ViewModel, vm => vm.GuardarDatos, This => This.btnGuardar);
    }
}

And, you see, it's simple, the validation is done at the UI level, in the capture every control puts a red error message and the validation error message and the btnSave button is set to disable if any property does not pass its validation, such as at the ViewModel level, for example the SaveData command of the ViewModel does not run if any property of the ViewModel does not pass the validation.

So regarding your question, I would say that if you are going to validate you can do it in the view, but I think it is repeating code and can implement something that you can reuse.

You do well to use a design pattern, in your case MVC, in my case I use MVVM because it not only allows to separate the logic of your application or to reuse your ViewModels in other platforms (for example a Xamarin app), but also do not repeat code, do not repeat the implementation of validations for each model.

    
answered by 01.04.2018 / 04:14
source
1

The easiest way to validate what you mention, is that you validate it from the view, regular expressions, masks, methods that verify the data, in this way if there has been any problem with the data entered by the user, You can know quickly, without having to connect to the server.

But at the same time it is also highly recommended to apply a second security phase so to speak, which is usually the controller, before sending the data to the model to interact with the database, the controller validates them again leaving was any problem caused by the data that the user enters.

If you do the latter, I recommend you to have a class with the methods that you would normally need, in this way that class would be your validator and you will only have to call it, especially not to repeat code.

    
answered by 31.03.2018 в 04:43