Je me demandais comment obtenir une validation de modèle avec l’API Web ASP.NET. J'ai mon modèle comme suit:
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; }
}
J'ai ensuite une action Post dans mon contrôleur API:
public void Post(Enquiry enquiry)
{
enquiry.EnquiryDate = DateTime.Now;
context.DaybookEnquiries.Add(enquiry);
context.SaveChanges();
}
Comment puis-je ajouter if(ModelState.IsValid)
puis gérer le message d'erreur à transmettre à l'utilisateur?
Pour séparer les préoccupations, nous vous suggérons d'utiliser un filtre d'action pour la validation du modèle. Vous n'avez donc pas à vous soucier beaucoup de la validation dans votre contrôleur d'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);
}
}
}
Comme ceci, 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<string>();
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 ceci (en supposant que JSON, mais le 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 sûr construire votre objet/liste d'erreur comme bon vous semble, par exemple en ajoutant des noms de champs, des identifiants de champs, etc.
Même s'il s'agit d'un appel Ajax "à sens unique" semblable à un POST d'une nouvelle entité, vous devez toujours renvoyer quelque chose à l'appelant - quelque chose qui indique si la demande a abouti ou non. Imaginez un site où votre utilisateur va ajouter des informations sur lui-même via une demande AJAX POST. Demande. Si les informations qu'il a essayé d'entrer ne sont pas valides - comment vont-ils savoir si leur action Save a réussi ou non?
La meilleure façon de faire est d’utiliser de bons vieux codes d’état HTTP comme 200 OK
etc. De cette façon, votre JavaScript peut gérer correctement les échecs en utilisant les rappels appropriés (erreur, succès, etc.).
Voici un bon tutoriel sur une version plus avancée de cette méthode, utilisant un ActionFilter et jQuery: http://asp.net/web-api/videos/getting-started/custom-validation
Peut-être pas ce que vous cherchiez, mais peut-être agréable pour quelqu'un de savoir:
Si vous utilisez .net Web Api 2, vous pouvez simplement procéder comme suit:
if (!ModelState.IsValid)
return BadRequest(ModelState);
En fonction des erreurs de modèle, vous obtenez le résultat suivant:
{
Message: "The request is invalid."
ModelState: {
model.PropertyA: [
"The PropertyA field is required."
],
model.PropertyB: [
"The PropertyB field is required."
]
}
}
Vous pouvez utiliser les attributs du System.ComponentModel.DataAnnotations
espace de noms pour définir les règles de validation. Reportez-vous à validation du modèle - par Mike Wasson pour plus de détails.
Voir également la vidéo API Web ASP.NET, Partie 5: Validation personnalisée - Jon Galloway
Autres références
Ou, si vous recherchez une simple collection d'erreurs pour vos applications .. voici ma mise en œuvre:
public override void OnActionExecuting(HttpActionContext actionContext)
{
var modelState = actionContext.ModelState;
if (!modelState.IsValid)
{
var errors = new List<string>();
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" ] }
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);
};
Ici, vous pouvez vérifier que l’erreur d’état du modèle est affichée une par une
public HttpResponseMessage CertificateUpload(employeeModel emp)
{
if (!ModelState.IsValid)
{
string errordetails = "";
var errors = new List<string>();
foreach (var state in ModelState)
{
foreach (var error in state.Value.Errors)
{
string p = error.ErrorMessage;
errordetails = errordetails + error.ErrorMessage;
}
}
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("error", errordetails);
return Request.CreateResponse(HttpStatusCode.BadRequest, dict);
}
else
{
//do something
}
}
}
J'ai eu un problème lors de la mise en œuvre du modèle de solution accepté où mon ModelStateFilter
retournait toujours false
(puis un 400) pour actionContext.ModelState.IsValid
pour certains objets du modèle:
public class ModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = new HttpResponseMessage { StatusCode = HttpStatusCode.BadRequest};
}
}
}
J'accepte uniquement le format 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.ReadAsStringAsync().Result;
AddressDTO address = JsonConvert.DeserializeObject<AddressDTO>(posted);
if (address != null)
{
// moar val here
bindingContext.Model = address;
return true;
}
return false;
}
}
Que j’enregistre directement après mon modèle via
config.BindParameter(typeof(AddressDTO), new AddressModelBinder());
Vous pouvez également lancer des exceptions comme indiqué ici: http://blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx =
Remarque: pour faire ce que cet article suggère, n'oubliez pas d'inclure System.Net.Http