2012-07-27 111 views
85

我想知道如何使用ASP.NET Web API实现模型验证。我有我的模型,像这样:在ASP.NET Web API中处理ModelState验证

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

然后我有一个邮政行动在我的API控制器:

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

如何添加if(ModelState.IsValid)然后处理错误消息向下传递给用户?

回答

155

关注的分离,我会建议你使用行为过滤模型验证,所以你不需要关心太多怎么办确认在您的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

这需要命名空间是'System.Net.Http','System.Net'' System.Web.Http.Controllers'和'System.Web.Http.Filters'。 – 2013-01-15 22:38:26

+9

在官方的ASP.NET Web Api页面上也有类似的实现:http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web -api – 2013-07-04 14:08:24

+0

即使不把[ValidationActionFilter]放在web api上面,它仍然会调用代码并给我错误的请求。 – micronyks 2015-05-26 14:48:01

25

这样,例如:

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

这将返回这样的响应(假设JSON,但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."] 

当然你也可以建立你的错误对象/列出你喜欢的任何方式,例如添加字段名,字段的ID等

即使它是一个“单向” Ajax调用像一个新的实体的POST,你还是应该返回的东西给调用者 - 指示请求是否成功。想象一下你的用户将通过AJAX POST请求添加一些关于他们自己的信息的网站。如果他们试图输入的信息无效 - 他们如何知道他们的保存操作是否成功?

执行此操作的最佳方法是使用良好的旧的HTTP状态码,如200 OK等。这样,你的JavaScript就可以使用正确的回调(错误,成功等)正确处理失败。

下面是关于这种方法的更高级版本的一个很好的教程,用一个ActionFilter和jQuery:http://asp.net/web-api/videos/getting-started/custom-validation

+0

这只是返回我的'enquiry'对象,它没有说哪些属性是无效的,但?所以如果我将'CustomerAccountNumber'留空,它应该说默认的验证消息(CusomterAccountNumber字段是必需的。) – CallumVass 2012-07-27 11:26:24

+0

编辑我的答案。 – 2012-07-27 11:30:48

+0

我明白了,那么这是处理模型验证的“正确”方式吗?似乎有点混乱给我.. – CallumVass 2012-07-27 11:34:01

16

也许不是你要找的,但也许是不错的人知道:

如果您使用的是.NET网络API 2你可以只需做下面的事情G:

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

根据模型的错误,你会得到这样的结果:

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

当我问这个问题Web API 1刚刚发布,它可能从那以后大量移动:) – CallumVass 2014-05-16 15:06:27

+0

一定要将这些属性标记为可选,否则您将得到一个无用的通用“发生错误。”错误信息。 – bouke 2015-11-09 17:31:58

9
1

我不得不实施一个问题accepted solution pattern在我的ModelStateFilter总是返回false(以及随后400),用于actionContext.ModelState.IsValid某些模型对象:

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

我只接受JSON,所以我实现了一个自定义模型绑定器类:

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

这一点我通过

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

这里我的模型后,直接注册,您可以检查一个

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

或者,如果您正在为您的应用程序错误的简单集合..这是我实现这一点:

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

错误消息响应将如下所示:

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