Je ne suis pas sûr si vous êtes familier avec l'application NerdDinner. Il ajoute une méthode GetRuleViolations() et une propriété IsValid à l'objet Dinner. Lorsque l'objet est enregistré, il vérifie si l'objet est valide. Si ce n'est pas le cas, il déclenche une exception. Dans le contrôleur, cette exception est interceptée, ModelState de ViewData est rempli avec les violations de règles et la vue est réaffichée. Les aides Html.Validation mettent en évidence les erreurs.ASP.NET MVC: Comment créer ViewData pour un filtre d'exception
Ce que je voudrais faire est de créer un HandleRuleViolationExceptionAttribute, semblable à HandleExceptionAttribute (qui fait partie de MVC Framework). Le problème est que cet attribut doit repeupler le Modelstate de View.
Une vue peut avoir n'importe quel type d'objet pour son modèle. Le code qui génère les remplissages RuleViolationException définit RuleViolationException.Object sur le modèle de la vue.
Je regardai le code pour le HandleExceptionAttribute dans le code source MVC et modifia:
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method, _
Inherited:=True, AllowMultiple:=False)> _
Public Class HandleRuleViolationExceptionAttribute
Inherits FilterAttribute
Implements IExceptionFilter
Private m_View As String
Private m_MasterPage As String
Public Property View() As String
Get
Return m_View
End Get
Set(ByVal value As String)
m_View = value
End Set
End Property
Public Property MasterPage() As String
Get
Return If(m_MasterPage, String.Empty)
End Get
Set(ByVal value As String)
m_MasterPage = value
End Set
End Property
Public Sub OnException(ByVal filterContext As System.Web.Mvc.ExceptionContext) _
Implements System.Web.Mvc.IExceptionFilter.OnException
If filterContext Is Nothing Then
Throw New ArgumentException("filterContext is null")
End If
'Ignore if the error is already handled.
If filterContext.ExceptionHandled Then Return
'Handle only ObjectIsInvalidExceptions.
If Not TypeOf filterContext.Exception Is ObjectIsInvalidException Then
Return
End If
Dim ex As ObjectIsInvalidException = DirectCast(filterContext.Exception, ObjectIsInvalidException)
'If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
'ignore it.
If (New HttpException(Nothing, ex).GetHttpCode()) <> 500 Then Return
Dim actionName As String = CStr(filterContext.RouteData.Values("action"))
Dim viewName As String = If(String.IsNullOrEmpty(View), actionName, View)
Dim viewData = filterContext.Controller.ViewData
viewData.Model = ex.Object
For Each item As String In filterContext.HttpContext.Request.Form.Keys
viewData.Add(item, filterContext.HttpContext.Request.Form.Item(item))
Next
For Each ruleViolation In ex.Object.GetRuleViolations()
viewData.ModelState.AddModelError(ruleViolation.PropertyName, ruleViolation.ErrorMessage)
Next
filterContext.Result = New ViewResult() With _
{ _
.ViewName = viewName, _
.MasterName = MasterPage, _
.ViewData = viewData, _
.TempData = filterContext.Controller.TempData _
}
filterContext.ExceptionHandled = True
filterContext.HttpContext.Response.Clear()
filterContext.HttpContext.Response.StatusCode = 500
'Certain versions of IIS will sometimes use their own error page when
'they detect a server error. Setting this property indicates that we
'want it to try to render ASP.NET MVC's error page instead.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
End Sub
End Class
Pour remplir le modèle I itérer sur les clés de formulaire de la demande de la vue et ajoutez la clé et sa valeur à la ViewData exemple. Cela fonctionne maintenant, cependant, je ne crois pas que ce soit la façon de le faire.
Dans la méthode Action du contrôleur, je pouvais mettre à jour le modèle avec la méthode UpdateModel. Cela met également à jour le viewStates ModelState. Je peux inclure un tableau de chaînes avec les noms de propriétés qui doivent être mis à jour ou, lorsque le modèle est un paramètre Action, je peux utiliser l'attribut Bind pour inclure ou exclure certaines propriétés (comme je le fais dans create-action au dessus). Ma méthode n'adhère pas à cela, ce qui peut entraîner des problèmes de sécurité.
Existe-t-il une meilleure façon de construire l'objet ViewData dans la méthode OnException, qui fonctionne de manière similaire à la méthode UpdateModel du contrôleur? Existe-t-il un moyen d'invoquer la méthode UpdateModel à partir de ExceptionHandlerAttribute?
Merci, Guillaume Hanique
Merci pour votre réponse! L'attribut a en effet une propriété View qui peut être définie, mais si ce n'est pas le cas, le nom de l'action est utilisé comme nom de vue. J'ai eu exactement la même idée que vous: filterContext.Controller.UpdateModel HOWEVER filterContext.Controller est de type ControllerBase qui n'a pas la méthode UpdateModel. D'autres idées? Puis-je transmettre l'instance réelle du contrôleur à cet attribut (le ceci/moi dans l'action du contrôleur) et l'utiliser pour mettre à jour le modèle? –
Bon point. J'avais raté ça. En repensant au code, je pense que vous avez à peu près le minimum de travail requis pour y parvenir. En définissant viewData.Model sur ex.Object, vous obtenez la même chose. Je pense que vous obtiendrez toutes les données de formulaire de l'ex.Object? Peut être pas. –
J'abandonne. Je ne suis même plus sûr si UpdateModel est la méthode à appeler. Et même si c'est le cas, je n'arrive pas à comprendre comment. Est-ce que quelqu'un sait comment mettre à jour le ModelState lors de la définition de la propriété Model de la vue? –