MVC 필터 및 Fluent Validation 패키지로 모델을 자동으로 검증하는 방법

13223 단어
저는 많은 MVC 프로젝트를 볼 기회가 있었고 대부분의 프로젝트에서 모든 컨트롤러의 모든 메서드에서 뷰 모델이 수동으로 검증되는 구조를 발견했습니다.

기껏해야 코드는 Controller 클래스에서 ModelState 속성을 확인하는 데 의존하지만 최악의 경우에는 주석을 달지 않는 것이 좋습니다.

[Route("")]
[HttpPost]
public async Task<IActionResult> CreatePostAsync([FromBody] CreatePost request)
{
    if (ModelState.IsValid)
    {
        return Ok(await _postService.CreateAsync(request));
    }
    return BadRequest(ModelState.ValidationState);
}


따라서 이에 대한 보다 우아한 솔루션이 존재합니다…

이 예에서는 요청이 컨트롤러 PostController에 도달하기 전에 CreatePost라는 뷰 모델의 유효성을 검사하고 그 후에 적절한 서비스에 요청을 보냅니다.

1단계: 너겟 패키지 설치

  • FluentValidation.AspNetCore by Jeremy Skinner

  • 2단계: 유효성 검사기 만들기

    FluentValidation 패키지를 사용하면 모든 유효성 검사기는 뷰 모델인 AbstractValidator에서 상속되는 클래스입니다.

    이 단계에서는 "CreatePost"보기 모델의 "Title"및 "Content"속성이 [Required] 데이터 주석과 같이 비어 있지 않은지 확인합니다.

    public class CreatePostValidator : AbstractValidator<CreatePost>
    {
        public CreatePostValidator()
        {
            RuleFor(x => x.Title).NotEmpty();
            RuleFor(x => x.Content).NotEmpty();
        }
    }
    


    보시다시피 클래스 생성자에서 각 속성에 대한 규칙을 추가하고 필요에 따라 결합할 수 있습니다.

    이것은 보다 유연한 시나리오를 위한 조건부 규칙이 있는 보다 완전한 예입니다. 사용자 이름 또는 이메일로 인증을 허용할 수 있지만 항상 둘 중 하나로 인증을 허용할 수 있습니다.

    public class LoginValidator : AbstractValidator<Login>
    {
        public LoginValidator()
        {
            RuleFor(x => x.UserName)
                .NotEmpty()
                .When(x => !DataTypesHelper.IsEmail(x.Email) && string.IsNullOrEmpty(x.Email))
                .WithMessage("Username or email are required");
    
            RuleFor(x => x.Email)
                .NotEmpty()
                .When(x => string.IsNullOrEmpty(x.UserName))
                .WithMessage("Username or email are required");
    
            RuleFor(x => x.Password)
                .NotEmpty()
                .WithMessage("Password is required");
        }
    }
    


    필요한 경우 Github repository에서 FluentValidation에 대한 전체 설명서를 읽을 수 있습니다.

    3단계: MVC 필터 생성

    에 대한 이 게시물MVC lifecycle에서 볼 수 있듯이 액션 필터는 컨트롤러 전후에 호출되지만 컨트롤러를 호출하기 전에만 모델의 유효성을 검사하는 데 관심이 있으므로 이러한 이유로 OnActionExecuting 메서드를 사용합니다.

    https://www.asp.net/media/4071077/aspnet-web-api-poster.pdf

    이 시점에서 우리는 FluentValidation으로 정의한 규칙을 내부적으로 적용할 ModelState를 확인합니다.

    public class ModelStateFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new BadRequestObjectResult(context.ModelState);
            }
        }
        public void OnActionExecuted(ActionExecutedContext context) { }
    }
    


    코드 예제는 이를 기반으로 함Gist

    4단계: DI 컨테이너에 서비스 등록

    MVC 서비스를 DI 컨테이너에 추가할 때 사용할 작업 필터를 지정한 다음 뷰 모델의 유효성 검사기인 어셈블리를 지정해야 합니다.

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(options =>
            {
                options.Filters.Add(new ModelStateFilter());
            })
            .AddFluentValidation(options =>
            {
                options.RegisterValidatorsFromAssemblyContaining<Startup>();
            });
    }
    


    5단계: 컨트롤러 메서드 만들기

    마지막 단계는 컨트롤러에서 경량화 요구 사항을 충족하는 메서드를 만드는 것입니다.

    [Route("")]
    [HttpPost]
    public async Task<IActionResult> CreatePostAsync([FromBody] CreatePost request)
    {
        return Ok(await _postService.CreateAsync(request));
    }
    


    잘못된 보기 모델로 메서드를 호출하면 오류 요약과 함께 "400 잘못된 요청"과 같은 응답을 받게 됩니다.

    POST /post HTTP/1.1
    Host: localhost:50555
    Content-Type: application/json
    Cache-Control: no-cache
    Postman-Token: c5f7b803-dfe2-a315-eb8f-671b84cb3175
    
    {
        "Title": "",
        "Content": "",
        "Excerpt": ""
    }
    



    {
        "Title": [
            "'Title' should not be empty."
        ],
        "Content": [
            "'Content' should not be empty."
        ]
    }
    


    알림: This post was published originally on Medium platform on may 2, 2018

    좋은 웹페이지 즐겨찾기