在ASP.NET Web API中处理ModelStatevalidation
我想知道如何使用ASP.NET Web API实现模型validation。 我有我的模型是这样的:
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控制器中有一个Post操作:
public void Post(Enquiry enquiry) { enquiry.EnquiryDate = DateTime.Now; context.DaybookEnquiries.Add(enquiry); context.SaveChanges(); }
如何添加if(ModelState.IsValid)
,然后处理错误消息传递给用户?
为了分离问题,我build议你使用动作filter进行模型validation,所以你不需要太在意如何在你的api控制器中进行validation:
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); } } }
像这样,例如:
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请求添加一些关于自己的信息。 如果他们试图input的信息无效,他们将如何知道他们的“保存”操作是否成功?
要做到这一点的最佳方法是使用良好的旧的HTTP状态代码,如200 OK
等。 这样,你的JavaScript可以正确地使用正确的callback(错误,成功等)处理失败。
这里有一个关于这个方法更高级版本的好教程,使用ActionFilter和jQuery: http : //asp.net/web-api/videos/getting-started/custom-validation
也许不是你要找的东西,但也许对某个人来说很好:
如果您正在使用.net Web Api 2,则可以执行以下操作:
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." ] } }
Followng来自模型validation – Mike Wasson
在ASP.NET Web API中,可以使用System.ComponentModel.DataAnnotations命名空间中的属性为模型上的属性设置validation规则。
validation失败时,Web API不会自动向客户端返回错误。 控制器的行为是检查模型状态并作出适当的响应。
您还可以创build一个操作筛选器,以在调用控制器操作之前检查模型状态。
如果模型validation失败,则此filter将返回一个包含validation错误的HTTP响应。
另请参阅videoASP.NET Web API,第5部分:自定义validation – Jon Galloway
其他参考
- 用WebAPI和WebForms在客户端散步
- ASP.NET Web API如何将HTTP消息绑定到域模型,以及如何在Web API中使用媒体格式。
- Dominick Baier – 保护ASP.NET Web API
- 将AngularJSvalidation连接到ASP.NET Web APIvalidation
- 在ASP.NET MVC中使用AngularJS显示模型状态错误
- 如何呈现错误到客户端? AngularJS / WebApi ModelState
- Web API中的dependency injectionvalidation
或者,如果你正在寻找简单的错误收集你的应用程序..这里是我的这个实现:
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" ] }
在这里您可以检查一个接一个地显示模型状态错误
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 } }
}
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); };
您也可以在此处loggingexception: http : //blogs.msdn.com/b/youssefm/archive/2012/06/28/error-handling-in-asp-net-webapi.aspx
请注意,要做文章build议,记得包括System.Net.Http
我有一个问题实现了接受的解决scheme模式 ,我的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());