Gestire la convalida ModelState nell’API Web ASP.NET

Mi chiedevo come ottenere la convalida del modello con l’API Web ASP.NET. Ho il mio modello in questo modo:

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

Quindi ho un’azione Post nel mio controller API:

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

Come aggiungo if(ModelState.IsValid) e poi gestisco il messaggio di errore per passare all’utente?

Per la separazione delle preoccupazioni, ti suggerirei di utilizzare il filtro azione per la convalida del modello, quindi non devi preoccuparti di come convalidare il tuo controller 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 : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { var modelState = actionContext.ModelState; if (!modelState.IsValid) actionContext.Response = actionContext.Request .CreateErrorResponse(HttpStatusCode.BadRequest, modelState); } } } 

Ad esempio, ad esempio:

 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); } } 

Ciò restituirà una risposta come questa (assumendo JSON, ma lo stesso principio di base per 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."] 

Ovviamente puoi build il tuo object / lista degli errori come preferisci, ad esempio aggiungendo nomi di campi, ID di campo ecc.

Anche se si tratta di una chiamata Ajax “a senso unico” come un POST di una nuova quadro, si dovrebbe comunque restituire qualcosa al chiamante, cosa che indica se la richiesta ha avuto successo. Immagina un sito in cui il tuo utente aggiungerà alcune informazioni su se stesso tramite una richiesta POST AJAX. Cosa succede se le informazioni che hanno tentato di inserire non sono valide – come faranno a sapere se la loro azione di salvataggio ha avuto esito positivo o no?

Il modo migliore per farlo è utilizzare i codici di stato HTTP Good Good come 200 OK e così via. In questo modo il tuo JavaScript può gestire correttamente i guasti utilizzando i callback corretti (errore, successo, ecc.).

Ecco un bel tutorial su una versione più avanzata di questo metodo, utilizzando ActionFilter e jQuery: http://asp.net/web-api/videos/getting-started/custom-validation

Forse non quello che stavi cercando, ma forse bello per qualcuno da sapere:

Se stai usando .net Web Api 2 puoi semplicemente fare quanto segue:

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

A seconda degli errori del modello, ottieni questo risultato:

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

È ansible utilizzare gli attributi dallo spazio System.ComponentModel.DataAnnotations nomi System.ComponentModel.DataAnnotations per impostare le regole di convalida. Refer Validation Model – Di Mike Wasson per i dettagli.

Consulta anche video API Web ASP.NET, parte 5: Convalida personalizzata – Jon Galloway

Altre referenze

  1. Fai una passeggiata sul lato client con WebAPI e WebForms
  2. In che modo l’API Web ASP.NET associa i messaggi HTTP ai modelli di dominio e come utilizzare i formati multimediali nell’API Web.
  3. Dominick Baier – Protezione delle API Web ASP.NET
  4. Aghook della convalida AngularJS alla convalida dell’API Web ASP.NET
  5. Visualizzazione degli errori ModelState con AngularJS in ASP.NET MVC
  6. Come rendere errori al client? AngularJS / WebApi ModelState
  7. Validazione iniettata dalla dipendenza nell’API Web

Oppure, se stai cercando una semplice raccolta di errori per le tue app, ecco la mia implementazione di questo:

 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 risposta al messaggio di errore sarà simile a:

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

Qui puoi controllare per mostrare l’errore di stato del modello uno per uno

  public HttpResponseMessage CertificateUpload(employeeModel emp) { if (!ModelState.IsValid) { string errordetails = ""; var errors = new List(); foreach (var state in ModelState) { foreach (var error in state.Value.Errors) { string 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 ValidateModelAttribute : ActionFilterAttribute { 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.stringify(data), error: function (xhr, status, err) { if (xhr.status == 400) { DisplayModelStateErrors(xhr.responseJSON.ModelState); } }, .... function DisplayModelStateErrors(modelState) { var message = ""; var propStrings = Object.keys(modelState); $.each(propStrings, function (i, propString) { var propErrors = modelState[propString]; $.each(propErrors, function (j, propError) { message += propError; }); message += "\n"; }); alert(message); }; 

È inoltre ansible generare eccezioni come documentate qui: http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx

Nota, per fare ciò che suggerisce questo articolo, ricorda di includere System.Net.Http

Ho ModelStateFilter un problema nell’implementazione del modello di soluzione accettato in cui il mio ModelStateFilter restituiva sempre false (e successivamente un 400) per actionContext.ModelState.IsValid per determinati oggetti del modello:

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

Accetto solo JSON, quindi ho implementato una class di raccoglitore modello personalizzata:

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

Che registro subito dopo la mia modella via

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