2009-03-14 7 views
23

Quando utilizzo UpdateModel o TryUpdateModel, il framework MVC è abbastanza intelligente da sapere se si sta tentando di passare un valore null in un tipo di valore (ad esempio l'utente dimentica di compilare il campo Giorno di nascita richiesto).ASP.NET MVC - Messaggio di convalida personalizzato per i tipi di valore

Sfortunatamente, non so come sovrascrivere il messaggio predefinito, "È necessario un valore." nel sommario in qualcosa di più significativo ("Per favore, inserisci il tuo giorno di nascita").

Deve esserci un modo per farlo (senza scrivere troppo codice di aggiramento), ma non riesco a trovarlo. Qualsiasi aiuto?

EDIT

Inoltre, credo che questo sarebbe anche un problema per le conversioni non valide, per esempio BirthDay = "Ciao".

risposta

1

Cercare ModelState.AddError.

+0

Conosco ModelState.AddModelError(), ma UpdateModel() aggiunge automaticamente i valori alla raccolta degli errori. A meno che non fraintenda la tua risposta? –

+1

Bene quando si convalida, aggiungere qualcosa al ModelState. Ecco un ottimo tutorial. http://blog.maartenballiauw.be/post/2008/08/29/Form-validation-with-ASPNET-MVC-preview-5.aspx –

+0

Speravo di non dover legare tutto da solo, ma forse il suo l'unico modo se voglio i miei messaggi? –

4

Ho utilizzato il fantastico framework di convalida xVal. Mi consente di eseguire tutte le convalide nel modello (anche LINQ-SQL :)). Emette anche il javascript richiesto per la validazione lato client.

EDIT: dispiace lasciato fuori la link per sapere come farlo funzionare per LINQ-SQL

Il flusso di lavoro di base più o meno così.

public partial class YourClass 
{ 
    [Required(ErrorMessage = "Property is required.")] 
    [StringLength(200)] 
    public string SomeProperty{ get; set; } 
} 

try 
{ 
    // Validate the instance of your object 
    var obj = new YourClass() { SomeProperty = "" } 
    var errors = DataAnnotationsValidationRunner.GetErrors(obj); 
    // Do some more stuff e.g. Insert into database 
} 
catch (RulesException ex) 
{ 
    // e.g. control name 'Prefix.Title' 
    ex.AddModelStateErrors(ModelState, "Prefix"); 
    ModelState.SetModelValue("Prefix.Title", new ValueProviderResult(ValueProvider["Prefix.Title"].AttemptedValue, collection["Prefix.Title"], System.Globalization.CultureInfo.CurrentCulture)); 

} 
+0

Solo per il record, xVal è deprecato [secondo] (http://xval.codeplex.com/) al team xVal. Ti raccomandano solo di usarlo con MVC 1.0 e non viene più mantenuto o sviluppato attivamente. –

6

Con il DefaultModelBinder è possibile ignorare il messaggio di errore predefinito richiesto ma purtroppo si applicherebbe globalmente che IMHO rende completamente inutile. Ma nel caso in cui si decide di farlo ecco come:

  1. aggiungere la cartella App_GlobalResources al tuo sito ASP.NET
  2. aggiungere un file di risorse chiamato Messages.resx
  3. All'interno del file risorse dichiarare una nuova risorsa di tipo stringa con la chiave PropertyValueRequired e qualche valore
  4. Nel Application_Start aggiungere la seguente riga:

    DefaultModelBinder.ResourceClassKey = "Messages"; 
    

Come si può vedere non c'è alcun collegamento tra la proprietà del modello che si sta convalidando e il messaggio di errore.

In conclusione è preferibile scrivere una logica di convalida personalizzata per gestire questo scenario. Un modo potrebbe essere quello di utilizzare un tipo nullable (System.Nullable <TValueType>) e poi:

if (model.MyProperty == null || 
    /** Haven't tested if this condition is necessary **/ 
    !model.MyProperty.HasValue) 
{ 
    ModelState.AddModelError("MyProperty", "MyProperty is required"); 
} 
+0

sai se il comportamento di ResourceClassKey è cambiato ultimamente? Questo sembra rotto in MVC 3. Il documento dice anche che dovrebbe generare un'eccezione per una classe non valida, ma non lo farà, anche se faccio DefaultModelBinder.ResourceClassKey = "lkjhjkjdswqq". –

+0

non importa ... hai appena trovato la tua risposta su http://stackoverflow.com/questions/5395040/globally-localize-validation/5395297#5395297 con la citazione di Brad sul perché questo non funziona :) –

3

come su questo?

[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$", 
        ErrorMessage = "Characters are not allowed.")] 

che dovrebbe permettere di codificare le proprietà con i messaggi di errore specifici per qualsiasi MVC validatori che si desidera utilizzare ...

2

In ASP.NET MVC 1, ho incontrato anche questo problema.

Nel mio progetto, c'è un modello o un oggetto business denominato "Entry" e la sua chiave primaria EntryId è int? digita, e il valore di EntryId può essere permesso dagli utenti.

Quindi il problema è che quando il campo è vuoto o zero o qualche valore intero che è esistito, i messaggi di errore personalizzati possono essere mostrati bene, ma se il valore è un valore non intero come "a", posso non trovare un modo per utilizzare il messaggio personalizzato per sostituire il messaggio predefinito come "Il valore 'a' non è valido".

quando a monitorare il messaggio di errore nel ModelState, ho trovato quando il valore è non intero, ci saranno due errori relativi a EntryID, e il messaggio di errore del primo elemento è vuoto ...

Ora ho usare un codice così brutto per violare il problema.

if (ModelState["EntryId"].Errors.Count > 1) 
{ 
    ModelState["EntryId"].Errors.Clear(); //should not use ModelState["EntryId"].remove(); 
    ModelState.AddModelError("EntryId", "必须为大于0的整数"); //必须为大于0的整数 means "it should be an integer value and great than 0" 
} 

ma questo rende il controller grasso, sperare che ci sia una soluzione reale per risolverlo.

0

sì, c'è un modo, è necessario utilizzare System.ComponentModel.DataAnnotations in combinazione con xVal e che si sta per essere in grado di impostare le regole di convalida e messaggi (u può anche utilizzare i file di risorse per la localizzazione) per ciascuna della vostra proprietà utilizzando attributi
un'occhiata quihttp://blog.codeville.net/2009/01/10/xval-a-validation-framework-for-aspnet-mvc/

19

Fai la tua ModelBinder estendendo DefaultModelBinder:

public class LocalizationModelBinder : DefaultModelBinder 

Override SetProperty:

 base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value); 

     foreach (var error in bindingContext.ModelState[propertyDescriptor.Name].Errors. 
      Where(e => IsFormatException(e.Exception))) 
     { 
      if (propertyDescriptor.Attributes[typeof(TypeErrorMessageAttribute)] != null) 
      { 
       string errorMessage = 
        ((TypeErrorMessageAttribute)propertyDescriptor.Attributes[typeof(TypeErrorMessageAttribute)]).GetErrorMessage(); 
       bindingContext.ModelState[propertyDescriptor.Name].Errors.Remove(error); 
       bindingContext.ModelState[propertyDescriptor.Name].Errors.Add(errorMessage); 
       break; 
      } 
     } 

Aggiungere la funzione bool IsFormatException(Exception e) per controllare se un'eccezione è un FormatException:

if (e == null) 
      return false; 
     else if (e is FormatException) 
      return true; 
     else 
      return IsFormatException(e.InnerException); 

creare una classe Attribute:

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)] 
public class TypeErrorMessageAttribute : Attribute 
{ 
    public string ErrorMessage { get; set; } 
    public string ErrorMessageResourceName { get; set; } 
    public Type ErrorMessageResourceType { get; set; } 

    public TypeErrorMessageAttribute() 
    { 
    } 

    public string GetErrorMessage() 
    { 
     PropertyInfo prop = ErrorMessageResourceType.GetProperty(ErrorMessageResourceName); 
     return prop.GetValue(null, null).ToString(); 
    } 
} 

aggiungere l'attributo a la proprietà che si desidera convalidare:

[TypeErrorMessage(ErrorMessageResourceName = "IsGoodType", ErrorMessageResourceType = typeof(AddLang))] 
    public bool IsGood { get; set; } 

AddLang è un file resx e IsGoodType è il nome della risorsa.

E infine aggiungere questo in Global.asax.cs Application_Start:

ModelBinders.Binders.DefaultBinder = new LocalizationModelBinder(); 

Cheers!

+4

Ottima risposta, ma c'è un problema con esso. 'propertyDescriptoy.Name' non è sempre usato come chiave per' ModelState'. Dovresti invece usare il metodo ereditato 'CreateSubPropertyName', per creare la chiave, in questo modo:' ModelMetadata propertyMetadata = bindingContext.PropertyMetadata [propertyDescriptor.Name]; string propertyName = CreateSubPropertyName (bindingContext.ModelName, propertyMetadata.PropertyName); 'al suo posto è possibile utilizzare' bindingContext.ModelState [propertyName] '. – Iravanchi

+2

Ow, ... e 'GetErrorMessage' nella classe attributo dovrebbe restituire' ErrorMessage' nel caso in cui non sia vuoto (o viceversa). Non tiene conto del caso in cui l'utente dell'attributo specifica il messaggio di errore in linea (senza alcuna risorsa). – Iravanchi

Problemi correlati