Gérer la validation ModelState dans ASP.NET Web API

Je me demandais comment réaliser une validation de modèle avec ASP.NET Web API. J’ai mon modèle comme ça:

public class Enquiry { [Key] public int EnquiryId { get; set; } [Required] public DateTime EnquiryDate { get; set; } [Required] public ssortingng CustomerAccountNumber { get; set; } [Required] public ssortingng ContactName { get; set; } } 

J’ai ensuite une action Post dans mon API Controller:

 public void Post(Enquiry enquiry) { enquiry.EnquiryDate = DateTime.Now; context.DaybookEnquiries.Add(enquiry); context.SaveChanges(); } 

Comment puis-je append if(ModelState.IsValid) et ensuite gérer le message d’erreur à transmettre à l’utilisateur?

En ce qui concerne la séparation des préoccupations, je vous suggère d’utiliser un filtre d’action pour la validation des modèles, vous n’avez donc pas à vous soucier de la manière de faire la validation dans votre contrôleur api:

 using System.Net; using System.Net.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; namespace System.Web.Http.Filters { public class ValidationActionFilter : ActionFilterAtsortingbute { public override void OnActionExecuting(HttpActionContext actionContext) { var modelState = actionContext.ModelState; if (!modelState.IsValid) actionContext.Response = actionContext.Request .CreateErrorResponse(HttpStatusCode.BadRequest, modelState); } } } 

Comme ça, par exemple:

 public HttpResponseMessage Post(Person person) { if (ModelState.IsValid) { PersonDB.Add(person); return Request.CreateResponse(HttpStatusCode.Created, person); } else { // the code below should probably be refactored into a GetModelErrors // method on your BaseApiController or something like that var errors = new List(); foreach (var state in ModelState) { foreach (var error in state.Value.Errors) { errors.Add(error.ErrorMessage); } } return Request.CreateResponse(HttpStatusCode.Forbidden, errors); } } 

Cela retournera une réponse comme celle-ci (en supposant JSON, mais même principe de base pour XML):

 HTTP/1.1 400 Bad Request Content-Type: application/json; charset=utf-8 (some headers removed here) ["A value is required.","The field First is required.","Some custom errorm essage."] 

Vous pouvez bien entendu construire votre object / liste d’erreur comme vous le souhaitez, par exemple en ajoutant des noms de champs, des identifiants de champs, etc.

Même si c’est un appel Ajax “à sens unique” comme un POST d’une nouvelle entité, vous devriez toujours retourner quelque chose à l’appelant – quelque chose qui indique si la requête a réussi ou non. Imaginez un site où votre utilisateur appenda des informations sur lui-même via une requête AJAX POST. Que se passe-t-il si les informations qu’ils ont essayé de saisir ne sont pas valides – comment sauront-ils si leur action de sauvegarde a réussi ou non?

La meilleure façon de procéder consiste à utiliser les anciens codes d’état HTTP tels que 200 OK , etc. De cette façon, votre JavaScript peut gérer correctement les échecs en utilisant les rappels corrects (erreur, réussite, etc.).

Voici un bon tutoriel sur une version plus avancée de cette méthode, utilisant un ActionFilter et un jQuery: http://asp.net/web-api/videos/getting-started/custom-validation

Peut-être pas ce que vous cherchiez, mais peut-être gentil pour quelqu’un de savoir:

Si vous utilisez .net Web Api 2, procédez comme suit:

 if (!ModelState.IsValid) return BadRequest(ModelState); 

En fonction des erreurs du modèle, vous obtenez ce résultat:

 { Message: "The request is invalid." ModelState: { model.PropertyA: [ "The PropertyA field is required." ], model.PropertyB: [ "The PropertyB field is required." ] } } 

Vous pouvez utiliser des atsortingbuts de l’espace de noms System.ComponentModel.DataAnnotations pour définir des règles de validation. Référez-vous à la validation du modèle – Par Mike Wasson pour plus de détails.

Reportez-vous également à la vidéo API Web ASP.NET, Partie 5: Validation personnalisée – Jon Galloway

Autres références

  1. Promenez-vous du côté client avec WebAPI et WebForms
  2. Comment l’API Web ASP.NET lie les messages HTTP aux modèles de domaine et comment utiliser les formats de support dans l’API Web.
  3. Dominick Baier – Sécurisation des API Web ASP.NET
  4. Accrocher la validation AngularJS à la validation de l’API Web ASP.NET
  5. Affichage des erreurs ModelState avec AngularJS dans ASP.NET MVC
  6. Comment rendre les erreurs au client? AngularJS / WebApi ModelState
  7. Validation par dependency injection dans l’API Web

Ou, si vous recherchez une collection d’erreurs simple pour vos applications, voici mon implémentation:

 public override void OnActionExecuting(HttpActionContext actionContext) { var modelState = actionContext.ModelState; if (!modelState.IsValid) { var errors = new List(); foreach (var state in modelState) { foreach (var error in state.Value.Errors) { errors.Add(error.ErrorMessage); } } var response = new { errors = errors }; actionContext.Response = actionContext.Request .CreateResponse(HttpStatusCode.BadRequest, response, JsonMediaTypeFormatter.DefaultMediaType); } } 

La réponse du message d’erreur ressemblera à ceci:

 { "errors": [ "Please enter a valid phone number (7+ more digits)", "Please enter a valid e-mail address" ] } 

Ici vous pouvez vérifier pour afficher l’erreur d’état du modèle un par un

  public HttpResponseMessage CertificateUpload(employeeModel emp) { if (!ModelState.IsValid) { ssortingng errordetails = ""; var errors = new List(); foreach (var state in ModelState) { foreach (var error in state.Value.Errors) { ssortingng p = error.ErrorMessage; errordetails = errordetails + error.ErrorMessage; } } Dictionary dict = new Dictionary(); dict.Add("error", errordetails); return Request.CreateResponse(HttpStatusCode.BadRequest, dict); } else { //do something } } 

}

C #

  public class ValidateModelAtsortingbute : ActionFilterAtsortingbute { public override void OnActionExecuting(HttpActionContext actionContext) { if (actionContext.ModelState.IsValid == false) { actionContext.Response = actionContext.Request.CreateErrorResponse( HttpStatusCode.BadRequest, actionContext.ModelState); } } } 

  [ValidateModel] public HttpResponseMessage Post([FromBody]AnyModel model) { 

Javascript

 $.ajax({ type: "POST", url: "/api/xxxxx", async: 'false', contentType: "application/json; charset=utf-8", data: JSON.ssortingngify(data), error: function (xhr, status, err) { if (xhr.status == 400) { DisplayModelStateErrors(xhr.responseJSON.ModelState); } }, .... function DisplayModelStateErrors(modelState) { var message = ""; var propSsortingngs = Object.keys(modelState); $.each(propSsortingngs, function (i, propSsortingng) { var propErrors = modelState[propSsortingng]; $.each(propErrors, function (j, propError) { message += propError; }); message += "\n"; }); alert(message); }; 

Vous pouvez également lancer des exceptions telles que documentées ici: http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx

Notez que pour faire ce que cet article suggère, n’oubliez pas d’inclure System.Net.Http

J’avais un problème d’implémentation du modèle de solution accepté où mon ModelStateFilter renverrait toujours false (et par la suite 400) pour actionContext.ModelState.IsValid pour certains objects de modèle:

 public class ModelStateFilter : ActionFilterAtsortingbute { public override void OnActionExecuting(HttpActionContext actionContext) { if (!actionContext.ModelState.IsValid) { actionContext.Response = new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest}; } } } 

J’accepte uniquement le JSON, j’ai donc implémenté une classe de classeur de modèles personnalisée:

 public class AddressModelBinder : System.Web.Http.ModelBinding.IModelBinder { public bool BindModel(HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext) { var posted = actionContext.Request.Content.ReadAsSsortingngAsync().Result; AddressDTO address = JsonConvert.DeserializeObject(posted); if (address != null) { // moar val here bindingContext.Model = address; return true; } return false; } } 

Je m’inscris directement après mon modèle via

 config.BindParameter(typeof(AddressDTO), new AddressModelBinder());