2012-07-27 3 views
85

Мне было интересно, как я могу добиться проверки модели с помощью ASP.NET Web API. У меня есть модель, например, так:Управление проверкой ModelState в веб-интерфейсе ASP.NET

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

Я тогда действие Post в моем API контроллера:

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

Как добавить if(ModelState.IsValid), а затем обработать сообщение об ошибке, чтобы передать пользователю?

ответ

155

Для разделения беспокойства, я хотел бы предложить вам использовать фильтр действий для проверки модели, так что вам не нужно заботиться много, как сделать проверку в контроллере апи:

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'. –

+9

Существует также аналогичная реализация на официальной странице ASP.NET Web Api: http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web -api –

+0

Даже если не поставить [ValidationActionFilter] над веб-api, он по-прежнему вызывает код и дает мне плохой запрос. – micronyks

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."] 

Конечно, Вы можете построить свою ошибку объект/список, как вам нравится, например, добавление имен полей, идентификаторов полей и т. д.

Даже если это «один способ» вызова Ajax как POST нового объекта, вы все равно должны возвращать что-то вызывающему абоненту - то, что указывает, был ли запрос успешным. Представьте себе сайт, на котором пользователь добавит некоторую информацию о себе через запрос AJAX POST. Что делать, если информация, которую они пытались ввести, недействительна - как они узнают, успешно ли выполнено действие «Сохранить» или нет?

Лучший способ для этого - использовать Хорошие старые коды статуса HTTP как 200 OK и так далее. Таким образом, ваш JavaScript может правильно обрабатывать сбои, используя правильные обратные вызовы (ошибка, успех и т. Д.).

Вот хороший учебник на более продвинутую версию этого метода, с помощью ActionFilter и JQuery: http://asp.net/web-api/videos/getting-started/custom-validation

+0

Это просто возвращает мой объект «запрос», он не говорит, какие свойства являются недействительными? Поэтому, если я оставил 'CustomerAccountNumber' пустым, он должен сказать сообщение о проверке по умолчанию (поле CusomterAccountNumber не требуется ..) – CallumVass

+0

Отредактировал мой ответ. –

+0

Я вижу, так это «правильный» способ обращения с моделью? Кажется немного беспорядочным для меня .. – CallumVass

16

Может быть, не то, что вы искали, но, возможно, хорошо для кого-то, чтобы знать:

Если вы используете .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." 
     ] 
    } 
} 
+1

Готов иметь в виду, когда я задал этот вопрос, Web API 1 только что был выпущен, вероятно, с тех пор он сильно переместился :) – CallumVass

+0

Обязательно отметьте свойства как необязательные, иначе вы получите не-полезный общий «Произошла ошибка». сообщение об ошибке. – bouke

9

Вы можете использовать атрибуты из пространства имен System.ComponentModel.DataAnnotations для установки правил проверки. Для получения дополнительной информации см. Model Validation - By Mike Wasson.

относятся также видео ASP.NET Web API, Part 5: Custom Validation - Jon Galloway

Другие ссылки

  1. Take a Walk on the Client Side with WebAPI and WebForms
  2. How ASP.NET Web API binds HTTP messages to domain models, and how to work with media formats in Web API.
  3. Dominick Baier - Securing ASP.NET Web APIs
  4. Hooking AngularJS validation to ASP.NET Web API Validation
  5. Displaying ModelState Errors with AngularJS in ASP.NET MVC
  6. How to render errors to client? AngularJS/WebApi ModelState
  7. Dependency-Injected Validation in Web API
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" 
    ] 
} 
Смежные вопросы