2011-02-09 24 views
7

Ho iniziato a modificare le funzionalità di convalida lato client in ASP.net MVC dopo aver letto ScottGU's blog post sull'argomento. E 'abbastanza facile da usare le System.ComponentModel.DataAnnotations attributi come questo:asp.net convalida lato client mvc

[Required(ErrorMessage = "You must specify a reason")] 
    public string ReasonText { get; set; } 

... ma cosa succede se avete bisogno di qualcosa di un po' più complessa. Cosa succede se hai una classe Address con un campo PostalCode e CountryCode. Vorresti convalidare il codice postale con una regex diversa per ogni paese. [0-9] {5} funziona per gli Stati Uniti, ma per il Canada è necessario uno diverso.

ho ottenuto intorno a quella da dolci mia classe ValidationService che prende la proprietà ModelState del controller e convalida di conseguenza. Funziona perfettamente sul lato server, ma non funziona con la nuova convalida lato client.

In Webforms userei i controlli di emissione javascript come RequiredFieldValidator o CompareValidator per le cose semplici e quindi utilizzare un CustomValidator per le regole complesse. In questo modo ho tutta la mia logica di convalida in un posto, e ottengo il beneficio della convalida rapida di javascript per cose semplici (il 90% delle volte) mentre ottengo ancora la sicurezza della convalida lato server come backstop.

Quale sarebbe l'approccio equivalente in MVC?

+0

Non sono sicuro su questo, ma penso che sia necessario implementare la convalida del proprio client per cose personalizzate. Magari guarda Jquery Validation-http: //docs.jquery.com/Plugins/validation – Vishal

+0

Quale versione di ASP.NET MVC stai scegliendo come target? –

risposta

1

Ho appena visto qualcosa sull'interfaccia IValidatableObject in MVC 3 e farò un tentativo.

1

derivare il proprio attributo di convalida da ValidationAttribute e applicare la logica conseguenza. In MVC 2, al fine di eseguire la convalida di un immobile in base al valore di un'altra proprietà, questo deve essere fatto all'interno di un validatore che si registra da utilizzare con la convalida personalizzato attributo utilizzando (supponendo che il utilizzando DataAnnotationsModelValidatorProvider)

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ValidationAttribute), typeof(ValidationValidator)); 

perché nell'attributo di convalida è possibile accedere solo al valore della proprietà a cui è associato l'attributo e non al modello.

Dai un'occhiata a MVC FoolProof Validation per vedere come questo approccio è fatto.

+0

Viene automaticamente prelevato nella convalida lato client della casella? – CRice

+0

@CRice: se derivano da uno degli attributi in System.ComponentModel.DataAnnotations, il lato client verrà preso in carico per te. Se definisci la tua convalida, devi anche occuparti di scrivere da solo la logica del lato client. Supponendo di utilizzare la convalida jQuery, si aggiungerà una funzione di convalida per chiamare la convalida jQuery con chiave da una stringa da utilizzare quando la regola di convalida corrisponde alla chiave. –

+0

Come fa il sistema a sapere quale javascript utilizzare per la convalida di una classe personalizzata ereditata da System.ComponentModel.DataAnnotations.ValidationAttribute? – CRice

7

Modifica: Si presume che si stia utilizzando MVC 3. Sfortunatamente il mio codice è in VB.NET poiché è quello che devo usare al lavoro.

Al fine di far funzionare tutto bene con la nuova convalida discreto ci sono alcune cose che hanno a che fare. Li ho fatti passare un paio di settimane fa.

In primo luogo, creare una classe attributo personalizzato che eredita da ValidationAttribute. Una semplice classe di attributi RequiredIf è qui sotto:

Imports System.ComponentModel 
Imports System.ComponentModel.DataAnnotations 

<AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> _ 
Public NotInheritable Class RequiredIfAttribute 
    Inherits ValidationAttribute 

    Private Const _defaultErrorMessage As String = "'{0}' is required." 
    Private ReadOnly _dependentProperty As String 
    Private ReadOnly _targetValues  As Object() 

    Public Sub New(dependentProperty As String, targetValues As Object()) 

     MyBase.New(_defaultErrorMessage) 

     _dependentProperty = dependentProperty 
     _targetValues  = targetValues 

    End Sub 

    Public Sub New(dependentProperty As String, targetValues As Object(), errorMessage As String) 

     MyBase.New(errorMessage) 

     _dependentProperty = dependentProperty 
     _targetValues  = targetValues 

    End Sub 

    Public ReadOnly Property DependentProperty() As String 
     Get 
      Return _dependentProperty 
     End Get 
    End Property 

    Public ReadOnly Property TargetValues() As Object() 
     Get 
      Return _targetValues 
     End Get 
    End Property 

    Public Overrides Function FormatErrorMessage(name As String) As String 

     Return String.Format(Globalization.CultureInfo.CurrentUICulture, ErrorMessageString, name) 

    End Function 

    Protected Overrides Function IsValid(value As Object, context As ValidationContext) As ValidationResult 

     ' find the other property we need to compare with using reflection 
     Dim propertyValue = context.ObjectType.GetProperty(DependentProperty).GetValue(context.ObjectInstance, Nothing).ToString() 

     Dim match = TargetValues.SingleOrDefault(Function(t) t.ToString().ToLower() = propertyValue.ToLower()) 

     If match IsNot Nothing AndAlso value Is Nothing Then 
      Return New ValidationResult(FormatErrorMessage(context.DisplayName)) 
     End If 

     Return Nothing 

    End Function 

End Class 

Successivamente, è necessario implementare una classe di convalida. Questa classe è responsabile per consentire a MVC di conoscere le regole di convalida del client richieste per il funzionamento della libreria di convalida non invadente.

Public Class RequiredIfValidator 
    Inherits DataAnnotationsModelValidator(Of RequiredIfAttribute) 

    Public Sub New(metaData As ModelMetadata, context As ControllerContext, attribute As RequiredIfAttribute) 

     MyBase.New(metaData, context, attribute) 

    End Sub 

    Public Overrides Function GetClientValidationRules() As IEnumerable(Of ModelClientValidationRule) 

     Dim rule As New ModelClientValidationRule() With {.ErrorMessage = ErrorMessage, 
                  .ValidationType = "requiredif"} 

     rule.ValidationParameters("dependentproperty") = Attribute.DependentProperty.Replace("."c, HtmlHelper.IdAttributeDotReplacement) 

     Dim first  As Boolean = True 
     Dim arrayString As New StringBuilder() 

     For Each param In Attribute.TargetValues 
      If first Then 
       first = False 
      Else 
       arrayString.Append(",") 
      End If 
      arrayString.Append(param.ToString()) 
     Next 

     rule.ValidationParameters("targetvalues") = arrayString.ToString() 

     Return New ModelClientValidationRule() {rule} 

    End Function 

End Class 

Ora è possibile registrare tutto nel metodo start applicazione di Global.asax:

DataAnnotationsModelValidatorProvider.RegisterAdapter(GetType(RequiredIfAttribute), GetType(RequiredIfValidator)) 

In questo modo si ottiene il 90% della strada.Ora non vi resta che dire JQuery convalidare e strato di convalida discreto di MS Come leggere I nuovi attributi:

/// <reference path="jquery-1.4.1-vsdoc.js" /> 
/// <reference path="jquery.validate-vsdoc.js" /> 

/* javascript for custom unobtrusive validation 
    ==================================================== */ 

(function ($) { 

    // this adds the custom "requiredif" validator to the jQuery validate plugin 
    $.validator.addMethod('requiredif', 
          function (value, element, params) { 

           // the "value" variable must not be empty if the dependent value matches 
           // one of the target values 
           var dependentVal = $('#' + params['dependentProperty']).val().trim().toLowerCase(); 
           var targetValues = params['targetValues'].split(','); 

           // loop through all target values 
           for (i = 0; i < targetValues.length; i++) { 
            if (dependentVal == targetValues[i].toLowerCase()) { 
             return $.trim(value).length > 0; 
            } 
           } 

           return true; 
          }, 
          'not used'); 

    // this tells the MS unobtrusive validation layer how to read the 
    // HTML 5 attributes that are output for the custom "requiredif" validator 
    $.validator.unobtrusive.adapters.add('requiredif', ['dependentProperty', 'targetValues'], function (options) { 

     options.rules['requiredif'] = options.params; 
     if (options.message) { 
      options.messages['requiredif'] = options.message; 
     } 

    }); 

} (jQuery)); 

Spero che questo aiuti, questo è stato un vero e proprio dolore per arrivare a lavorare.

+0

Una buona spiegazione, ha senso vederlo dall'inizio alla fine – CRice

+0

Grazie, @CRice. Spero che questo aiuti qualcun altro. Avrei ucciso per qualcosa di simile mentre MVC 3 era in beta, ma era tutto a pezzi. –

+0

@SheaDaniels "Sfortunatamente il mio codice è in VB.NET poiché è quello che devo usare al lavoro." Questo è un motivo sufficiente per un nuovo lavoro! – Elisabeth

3

ScottGu ha twittato questa mattina come Pluralsight ha gratis MVC 3 training per le prossime 48 ore. Hanno un video che mostra come eseguire questa sorta di convalida personalizzata. I video pertinenti si trovano in "Modelli in ASP.NET MVC 3.0", in particolare "Attributi di convalida personalizzati" e "Modelli di auto-convalida".

+0

Grandi video. E un marketing brillante da regalare per due giorni. –

+0

+1 Grandi video. Mi sono appena registrato per la prova gratuita e ho guardato il messaggio "Convalida cliente personalizzato". E 'stato abbastanza completo e ha aiutato. Ora sono in prova per 10 giorni e potrei guardarne altri prima che finisca. Offrono un buon servizio. – Aaron

0

penso che la soluzione al vostro problema è System.ComponentModel.DataAnnotations in MVC

Per logica complessa è possibile implementare la propria logica. È molto facile. dare un'occhiata per la personalizzazione su questo link: http://msdn.microsoft.com/en-us/library/cc668224.aspx

Per cosa fondamentale ti fanno esigenze Vedi rilegata con il modello di classe e aggiungere l'attributo in cima alla proprietà ... Come

public class CustomerSearchDE 
{ 
    [StringLength(2, ErrorMessageResourceType = typeof(Translation), ErrorMessageResourceName = MessageConstants.conCompanyNumberMaxLength)] 
    public string CompanyNumber { get; set; } 
} 

visione fortemente scrivere con questa classe

Problemi correlati