2012-07-27 108 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" 
    ] 
}