Comment gérer les erreurs d'état du modèle dans une action de contrôleur appelée par ajax qui renvoie une vue partiale

J'ai une action post controller qui renvoie une vue partielle. Tout semble vraiment facile. mais. Je le charge en utilisant $.ajax(), en définissant le type comme html. Mais quand ma validation de modèle échoue, j'ai pensé que je devrais simplement lancer une erreur avec des erreurs d'état de modèle. Mais ma réponse renvoie toujours une erreur de serveur 500.

Comment puis-je signaler les erreurs d'état du modèle sans renvoyer Json avec n'importe quel résultat. Je voudrais toujours retourner une vue partielle que je peux ajouter directement à du HTML élément.

Modifier

Je voudrais également éviter de renvoyer une vue partielle d'erreur. Cela ressemblerait à un succès sur le client. Le fait que le client analyse le résultat pour voir s'il s'agit d'un succès réel est sujet à des erreurs. Les concepteurs peuvent modifier la sortie de la vue partielle et cela seul briserait la fonctionnalité. Donc, je veux lancer une exception, mais avec le message d'erreur correct retourné au client ajax.

22
demandé sur Robert Koritnik 2010-02-14 18:44:53

2 réponses

Solution

, j'ai dû écrire deux parties distinctes, magiquement fonctionnent exactement comme prévu.

Il devrait donc renvoyer une vue partielle lorsque le processus d'action du contrôleur réussit et il devrait lancer une erreur avec quelques détails d'échec quand les choses ne sont pas ok, donc les choses du côté client distingueraient le succès aussi bien que l'échec au lieu de toujours gérer le succès.

Il y a deux parties principales qui sont utilisées pour y parvenir:

  • une coutume classe d'exception qui est levée quand quelque chose ne va pas afin que nous puissions distinguer entre les exceptions générales qui peuvent se produire à tout moment pour une raison quelconque et les erreurs liées à notre traitement (notamment l'état du modèle invalide)
  • filtre D'action D'Exception {[8] } qui attrape notre exception personnalisée et prépare le résultat en fonction de cette exception; comme vous le verrez dans le code, notre exception personnalisée contiendra des informations sur les erreurs d'état du modèle Code D'état HTTP ainsi que des informations textuelles

Sur les détails alors...

Lien Externe: Toutes ces informations (explication détaillée ainsi que tout le code) est également disponible sur mon blog. Les dernières mises à jour de code seront toujours publié là.

Classe d'exception Personnalisée

Cette classe fournit deux choses

  1. simplifiez la distinction entre les erreurs d'état du modèle et les exceptions régulières
  2. fournir certains fonctionnalité de base que je peux utiliser plus tard

Cette classe est ensuite utilisée dans mon filtre d'erreur personnalisé.

public class ModelStateException : Exception
{
    public Dictionary<string, string> Errors { get; private set; }

    public ModelStateDictionary ModelState { get; private set; }

    public override string Message
    {
        get
        {
            if (this.Errors.Count > 0)
            {
                return this.Errors.First().Value;
            }
            return null;
        }
    }

    private ModelStateException()
    {
        this.Errors = new Dictionary<string, string>();
    }

    public ModelStateException(ModelStateDictionary modelState) : this()
    {
        this.ModelState = modelState;
        if (!modelState.IsValid)
        {
            foreach (KeyValuePair<string, ModelState> state in modelState)
            {
                if (state.Value.Errors.Count > 0)
                {
                    this.Errors.Add(state.Key, state.Value.Errors[0].ErrorMessage);
                }
            }
        }
    }
}

Attribut de filtre D'erreur

Cet attribut aide à renvoyer des erreurs au client en termes de codes D'erreur HTTP lorsqu'il y a des erreurs d'état de modèle.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class HandleModelStateExceptionAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (filterContext.Exception != null && typeof(ModelStateException).IsInstanceOfType(filterContext.Exception) && !filterContext.ExceptionHandled)
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.ContentEncoding = Encoding.UTF8;
            filterContext.HttpContext.Response.HeaderEncoding = Encoding.UTF8;
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
            filterContext.HttpContext.Response.StatusCode = 400;
            filterContext.HttpContext.Response.StatusDescription = (filterContext.Exception as ModelStateException).Message;
        }
    }
}

Après cela, j'ai simplement décoré mon action de contrôleur avec mon attribut et le tour est joué. J'ai eu des erreurs sur le client avec le code 400 et des informations correctes que j'ai définies dans mon filtre. Cette information est alors affiché à l'utilisateur (lorsqu'il est lié à des erreurs d'état du modèle, il affiche des informations sur les champs de formulaire que l'utilisateur doit Modifier pour rendre le formulaire valide).

[HandleModelStateException]
public ActionResult AddComment(MyModel data)
{
    // check if state is valid
    if (!this.ModelState.IsValid)
    {
        throw new ModelStateException(this.ModelState);
    }
    // get data from store
    return PartialView("Comment", /* store data */ );
}

Cela rend mon code réutilisable avec toutes les erreurs d'état du modèle et celles-ci seront envoyées au client comme elles le devraient.

Un seul problème (est maintenant résolu)

mais il y a encore un problème lié à ce code. Lorsque mon filtre d'action d'erreur définit StatusDescription et que cette chaîne contient des caractères spéciaux comme Č, je reçois des ordures sur le client. Sauf si J'utilise IE (j'utilise la version 8). FF et ch afficher ordures. C'est pourquoi j'ai défini des encodages mais cela ne fonctionne pas. Si quelqu'un a une solution pour cette particularité, je serais plus qu'heureux de les écouter.
Si je retourne un message d'erreur dans le contenu lui-même, tout va bien. L'encodage est correct et je peux afficher ce que je veux.

17
répondu Robert Koritnik 2011-05-14 16:29:25

Essayez ceci.

public ActionResult DoAjaxAction(Entity entity)
{
   if(ModelState.IsValid)
   {
     return PartialView("Valid_View", entity);
   }
   else
   {
     return PartialView("Invalid_View", entity);
   } 

}
3
répondu Amitabh 2010-06-21 08:35:34