Обработка проверки ModelState в веб-интерфейсе ASP.NET

Мне было интересно, как я могу добиться проверки модели с помощью 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; }
}

Затем у меня есть действие Post в моем контроллере API:

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

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

Ответ 1

Для разделения беспокойства я бы предложил использовать фильтр действий для проверки модели, поэтому вам не нужно заботиться о том, как сделать валидацию в вашем контроллере 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);
        }
    }
}

Ответ 2

Подобно этому, например:

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

Ответ 3

Возможно, не то, что вы искали, но, возможно, приятно узнать кого-то:

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

Ответ 4

Followng от Проверка модели - Майком Вассоном

В ASP.NET Web API вы можете использовать атрибуты из пространства имен System.ComponentModel.DataAnnotations, чтобы установить правила проверки свойств для вашей модели.

Web API автоматически не возвращает ошибку клиенту, когда проверка не выполняется. Это зависит от действия контроллера, чтобы проверить состояние модели и отреагировать соответствующим образом.

Вы также можете создать фильтр действий, чтобы проверить состояние модели до вызова действия контроллера.

Если проверка модели не выполняется, этот фильтр возвращает ответ HTTP, содержащий ошибки проверки.

Также см. видео ASP.NET Web API, часть 5: Пользовательская проверка - Jon Galloway

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

Ответ 5

Или, если вы ищете простой сбор ошибок для своих приложений.. вот моя реализация:

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

Ответ 6

Здесь вы можете проверить, чтобы показать ошибку состояния модели по одному

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

}

Ответ 7

С#

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

Ответ 9

У меня возникла проблема с принятым шаблоном решений , где my 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());