2012-07-27 19 views
85

Me preguntaba cómo puedo lograr la validación de modelo con API web de ASP.NET. Tengo mi modelo de este modo:Manejar validación de modelo de estado en ASP.NET Web API

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

que luego tienen una acción Publicar en mi controlador de API:

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

¿Cómo agrego if(ModelState.IsValid) y luego manejar el mensaje de error para transmitir a los usuarios?

Respuesta

155

Para la separación de preocupación, sugeriría que utilice filtro de acción para la validación del modelo, por lo que no es necesario importa mucho cómo hacer la validación en su controlador 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); 
     } 
    } 
} 
+23

Los espacios de nombres necesarios para esto son 'System.Net.Http',' System.Net' 'System.Web.Http.Controllers', y' System.Web.Http.Filters'. –

+9

También hay una implementación similar en la página web oficial ASP.NET Api: http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web -api –

+0

Incluso si no coloca [ValidationActionFilter] arriba de la API web, aún llama al código y me da una mala solicitud. – micronyks

25

Así, por ejemplo:

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

Esto devolverá una respuesta como esta (suponiendo JSON, pero el mismo principio básico para 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."] 

Por supuesto, puede construir su error Objeto/lista de la manera que desee, por ejemplo, agregar nombres de campo, identificador de campo, etc.

Incluso si se trata de una llamada Ajax "de una sola vía" como una POST de una nueva entidad, aún debe devolver algo a la persona que llama, algo que indica si la solicitud fue exitosa o no. Imagine un sitio donde su usuario agregará información acerca de sí mismo a través de una solicitud AJAX POST. ¿Qué ocurre si la información que intentaron ingresar no es válida? ¿Cómo sabrán si su acción de Guardar fue exitosa o no?

La mejor manera de hacerlo es usando Buenos códigos de estado de HTTP como 200 OK y así sucesivamente. De esta forma, su JavaScript puede manejar fallas de forma adecuada utilizando las devoluciones de llamada correctas (error, éxito, etc.).

Aquí hay un buen tutorial en una versión más avanzada de este método, utilizando un ActionFilter y jQuery: http://asp.net/web-api/videos/getting-started/custom-validation

+0

¿Eso solo devuelve mi objeto 'enquiry', pero no dice qué propiedades son inválidas? Entonces, si dejé 'CustomerAccountNumber' vacío, debería decir que el mensaje de validación predeterminado (se requiere el campo CusomterAccountNumber ...) – CallumVass

+0

Edited my answer. –

+0

Veo, ¿entonces esta es la forma "correcta" de manejar Validación del Modelo? Me parece un poco complicado. – CallumVass

16

tal vez no lo que estaba buscando, pero tal vez agradable para alguien que sabe:

Si está usando API .NET web 2 se podía solo haz lo siguiente g:

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

En función de los errores en el modelo, se obtiene el siguiente resultado:

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

No tengo en cuenta cuando Hice esta pregunta Web API 1 acaba de ser lanzada, probablemente se haya movido mucho desde entonces :) – CallumVass

+0

Asegúrese de marcar las propiedades como opcional, de lo contrario obtendrá un mensaje genérico no útil "Ha ocurrido un error". mensaje de error. – bouke

9
1

tuve un problema la aplicación de la accepted solution pattern donde mi ModelStateFilter que siempre devuelven false (y, posteriormente, una 400) para actionContext.ModelState.IsValid para ciertos objetos del modelo:

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

Solo acepto JSON, así que implementé una clase de cuaderno de modelo personalizado:

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

Qué puedo registrar directamente después de mi modelo a través de

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

Aquí puede comprobar para mostrar el error de estado del modelo uno a uno

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

}

2

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

O, si usted está buscando sencilla colección de errores para sus aplicaciones .. aquí está mi aplicación de esta:

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

Mensaje de error La respuesta será como la siguiente:

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