ASP.NET Web API에서 ModelState 유효성 검사 처리


106

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

그런 다음 API 컨트롤러에 Post 작업이 있습니다.

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

if(ModelState.IsValid)사용자에게 전달할 오류 메시지를 추가 하고 처리하려면 어떻게해야 합니까?

답변:


186

우려 사항을 분리하기 위해 모델 유효성 검사를 위해 작업 필터를 사용하는 것이 좋습니다. 따라서 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);
        }
    }
}

27
이를 위해 필요한 네임 스페이스는 System.Net.Http, System.Net System.Web.Http.Controllers하고 System.Web.Http.Filters.
— Christopher Stevenson

11
공식 ASP.NET 웹 API를 페이지에서 유사한 구현이있다 : asp.net/web-api/overview/formats-and-model-binding/...은
— 에릭 Schierboom

1
웹 API 위에 [ValidationActionFilter]를 넣지 않아도 여전히 코드를 호출하고 나쁜 요청을 보냅니다.
— micronyks

1
반환 된 오류 응답이 IncludeErrorDetailPolicy에 의해 제어된다는 점을 지적 할 가치가 있습니다. 기본적으로 원격 요청에 대한 응답에는 일반 "오류가 발생했습니다."메시지 만 IncludeErrorDetailPolicy.Always포함 되지만이 메시지를 설정 하면 세부 정보가 포함됩니다 (사용자에게 세부 정보가 노출 될 위험이 있음)
— Rob

대신 IAsyncActionFilter 사용을 제안하지 않은 특별한 이유가 있습니까?
— Ravior

30

당신이 찾고 있던 것이 아닐 수도 있지만, 누군가가 알기에 좋은 것일 수도 있습니다.

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


속성을 선택 사항으로 표시해야합니다. 그렇지 않으면 도움이되지 않는 일반 "오류가 발생했습니다."가 표시됩니다. 에러 메시지.
— Bouke

1
메시지를 변경하는 방법이 있습니까?
— saquib adil

29

예를 들면 다음과 같습니다.

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 등을 추가하는 등 원하는 방식으로 오류 개체 / 목록을 구성 할 수 있습니다.

새 엔티티의 POST와 같은 "단방향"Ajax 호출 인 경우에도 호출자에게 요청이 성공했는지 여부를 나타내는 무언가를 반환해야합니다. 사용자가 AJAX POST 요청을 통해 자신에 대한 정보를 추가하는 사이트를 상상해보십시오. 입력하려는 정보가 유효하지 않은 경우 어떻게하면 저장 작업이 성공했는지 여부를 어떻게 알 수 있습니까?

이 작업을 수행하는 가장 좋은 방법은 사용하는 좋은 올드 HTTP 상태 코드를 같은 200 OK등등. 이렇게하면 JavaScript가 올바른 콜백 (오류, 성공 등)을 사용하여 실패를 올바르게 처리 할 수 ​​있습니다.

다음은 ActionFilter 및 jQuery를 사용하는이 방법의 고급 버전에 대한 멋진 자습서입니다. http://asp.net/web-api/videos/getting-started/custom-validation


그것은 단지 내 enquiry개체를 반환 하지만 어떤 속성이 유효하지 않은지는 말하지 않습니까? 내가 왼쪽 그렇다면 CustomerAccountNumber빈, 그것은 기본 검증 메시지 (CusomterAccountNumber 필드가 필요합니다 ..)라고한다
— CallumVass

그렇다면 이것이 모델 유효성 검사를 처리하는 "올바른"방법입니까? 나에게 약간의 혼란을 것 같은데 ..
— CallumVass

jQuery 유효성 검사와 연결하는 것과 같은 다른 방법도 있습니다. 여기에 좋은 마이크로 소프트의 예는 다음과 같습니다 asp.net/web-api/videos/getting-started/custom-validation은
— 앤더스 Arpi

이 방법과 대답으로 선정 된 방법은 기능적으로 동일하므로이 대답은 작업 필터없이 직접 수행 할 수있는 방법을 보여주는 부가 가치가 있습니다.
— Shaun Wilson

이 작업을 수행 하려면 줄 errors.Add(error.ErrorMessage);을 로 변경해야했습니다 errors.Add(error.Exception.Message);.
— Caltor 2017-04-26

9

8

또는 앱에 대한 간단한 오류 모음을 찾고 있다면 여기에 구현 된 내용이 있습니다.

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

5

startup.cs 파일에 아래 코드 추가

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).ConfigureApiBehaviorOptions(options =>
            {
                options.InvalidModelStateResponseFactory = (context) =>
                {
                    var errors = context.ModelState.Values.SelectMany(x => x.Errors.Select(p => new ErrorModel()
                   {
                       ErrorCode = ((int)HttpStatusCode.BadRequest).ToString(CultureInfo.CurrentCulture),
                        ErrorMessage = p.ErrorMessage,
                        ServerErrorMessage = string.Empty
                    })).ToList();
                    var result = new BaseResponse
                    {
                        Error = errors,
                        ResponseCode = (int)HttpStatusCode.BadRequest,
                        ResponseMessage = ResponseMessageConstants.VALIDATIONFAIL,

                    };
                    return new BadRequestObjectResult(result);
                };
           });

3

여기에서 모델 상태 오류를 하나씩 확인할 수 있습니다.

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

}


3

씨#

    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)
    {

자바 스크립트

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

2

내가 구현하는 문제가 있었다 허용 솔루션 패턴 내이 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());

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.