선언적 코드를 사용한 ASP.NET Web API 데이터 검사

일반적으로 선언적(declarative) 코드는 명령형 코드에 비헤 가독성이 높고 테스트하기 쉬우며 코드의 양도 더 적습니다. System.ComponentModel.DataAnnotations 네임스페이스는 데이터를 검사하는 ValidationAttribute 하위 특성(attributes) 집합을 제공하며 이 특성들을 사용하면 데이터의 유효성을 검증하는 코드를 선언적으로 작성할 수 있습니다. ASP.NET Web API 액션 메서드에서도 이 특성들을 사용해 입력 데이터의 유효성을 검사할 수 있습니다.

입력 모델 검사

입력 모델 개체의 속성이 유효성 검증 특성을 가지면 ASP.NET Web API는 특성을 사용해 모델을 검사하고 HttpActionContext 클래스의 ModelState 속성을 통해 결과를 노출합니다. 유효성 검증 특성을 사용한 입력 모델 디자인은 다음과 같습니다.

public class RegisterUserModel
{
    [Required]
    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    [StringLength(50)]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [StringLength(100, MinimumLength = 8)]
    public string Password { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [Compare("Password")]
    public string ConfirmPassword { get; set; }
}

보통의 경우 입력 모델의 상태가 올바르지 않으면 API가 400(Bad Request) 상태를 반환하도록 합니다.

if (ModelState.IsValid == false)
{
    return BadRequest(ModelState);
}

그런데 이 코드는 다른 API 메서드에서도 그대로 반복될 것입니다. ActionFilterAttribute를 사용하면 이 작업을 선언적으로 파이프라인에 추가할 수 있습니다.

public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext context)
    {
        if (context.ModelState.IsValid == false)
        {
            context.Response = context.Request.CreateErrorResponse(
                HttpStatusCode.BadRequest, context.ModelState);
        }
    }
}

ValidateModelAttribute 특성은 다음처럼 API 액션 메서드에 적용할 수 있습니다.

// POST /api/account/register
[ValidateModel]
public async Task<IHttpActionResult> Post(RegisterUserModel model)
{
}

if 문에 비해 코드가 적을 뿐 아니라 컨트롤러 클래스에 추가하면 컨트롤러가 가지는 각 액션 메서드에 ValidateModelAttribute 특성을 하나하나 적용하지 않아도 모든 메서드에 대해 동일하게 동작하게 됩니다. 물론 전역 필터로 적용하는 것도 가능합니다.

그 밖의 매개변수 검사

입력 모델 개체가 가진 속성의 유효성을 선언적 코드를 사용해 검증한 것 처럼 API 액션 메서드가 가지는 그 밖의 매개변수* 역시 특성을 사용해 유효성을 검증할 수 있습니다. 많은 유효성 검증 특성의 적용 대상은 매개변수(AttributeTargets.Parameter)를 포함합니다. 단 ASP.NET Web API는 이를 직접적으로 지원하지는 않기 때문에 별도의 작업을 해주지 않으면 효과가 없습니다. 기타 매개변수의 유효성 검증 특성을 적용하는 코드 역시 ActionFilterAttribute를 사용할 수 있습니다.

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

[AttributeUsage(
    AttributeTargets.Class | AttributeTargets.Method,
    Inherited = true,
    AllowMultiple = false)]
public class ValidateParametersAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext context)
    {
        var action = context.ActionDescriptor;

        foreach (var binding in action.ActionBinding.ParameterBindings)
        {
            var parameter = binding.Descriptor;

            var validations =
                parameter.GetCustomAttributes<ValidationAttribute>() ??
                Enumerable.Empty<ValidationAttribute>();

            foreach (var validation in validations)
            {
                var name = parameter.ParameterName;
                var value = context.ActionArguments[name];

                if (validation.IsValid(value) == false)
                {
                    var message = validation.FormatErrorMessage(name);
                    context.Response = context.Request.CreateErrorResponse(
                        HttpStatusCode.BadRequest, message);
                    return;
                }
            }
        }
    }
}

ValidateParametersAttribute 특성은 액션 메서드의 매개변수가 유효성 검증 특성을 가지는 경우 값을 검사하고 유효하지 않으면 API 응답을 400(Bad Request) 상태로 설정합니다. 예를 들어 RangeAttribute 특성을 사용해 API 액션 메서드의 Int32 형식 매개변수의 유효 범위를 적용하면 다음과 같습니다.

// GET /api/items?take=10
[ValidateParameters]
public async Task<IEnumerable<Item>> Get([Range(1, 100)]int take)
{
}

매개변수 take의 값이 1100 사이에 포함되지 않으면 액션 메서드는 호출되지 않고 API는 400(Bad Request)를 응답합니다.

* 일반적으로 URI 경로 조각이나 쿼리를 통해 바인딩됩니다.

Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중