[C#] ASP.NET Core FluentValidation.AspNetCore .NET 사용법
- C#
- 2022. 4. 21. 05:52
참조
목적
- ASP.NET Core 프로젝트를 생성 후, .NET 6 버전에서
FluentValidation.AspNetCore
사용 방법에 대하여 내용 정리합니다.
ASP.NET Core Web API 프로젝트 생성
- 먼저 ASP.NET Core Web API 프로젝트를 하나 생성합니다.
FluentValidation.AspNetCore 패키지 설치
- 다음으로 NuGet Package 에서
FluentValidation.AspNetCore
를 입력하여 해당 NuGet Package를 설치 진행합니다. - 만약,
Package Manager
에서 CLI 명령어로 설치를 할 경우에는 다음 명령어를 입력하시면 됩니다.
> Install-Package FluentValidation.AspNetCore
FluentValidation 구성
- 설치가 완료 되었으면,
Program.cs
에 아래와 같이 FluentValidation.AspNetCore 관련 코드를 추가해 줍니다.
using FluentValidation.AspNetCore;
using System.Reflection;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers()
.AddFluentValidation(options =>
{
// Validate child properties and root collection elements
options.ImplicitlyValidateChildProperties = true; // 옵션 1
options.ImplicitlyValidateRootCollectionElements = true; // 옵션 2
// Automatic registration of validators in assembly
options.RegisterValidatorsFromAssembly(Assembly.GetExecutingAssembly()); //옵션 3
});
...생략
- 위 코드는 컨트롤러의 요청 파이프라인에
FluentValidation
을 추가한 것입니다. 아래는 설명입니다.옵션 1
: 자식 속성의 유효성을 검사할 수 있습니다. 일치하는 유효성 검사기를 찾을 수 있는 경우 하위 속성 여부를 사용하도록 설정하는 옵션은 암시적으로 유효성을 검사해야 합니다. 기본적으로 false 로 설정되어 있으므로 원하는 경우 이 옵션을 활성화해야 합니다.옵션 2
: 루트 요소의 유효성 검사가 암시적으로 유효성이 검사되어야 합니다. 이것은 루트 모델이 컬렉션이고 요소 유형에 대해 일치하는 유효성 검사기를 찾을 수 있는 경우에만 발생합니다.옵션 3
: 어셈블리에서 유효성 검사기를 자동으로 등록합니다.System.Reflection
을 사용하여 실행 어셈블리를 얻습니다.
모델에 유효성 검사 추가하기
- Models 라는 폴더와 Validation 이라는 폴더를 추가하였습니다.
- 모델 내부에 두 개의 새로운 클래스
Customer.cs
및Address.cs
를 추가하여 애플리케이션에서 들어오고 나가는 데이터에 대한 모델을 지정했습니다. - 아래는 각 모델과 해당 요소를 나타낸 것입니다.
Customer.cs
namespace FluentValidationSample.Models
{
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int Phone { get; set; }
public Address Address { get; set; }
}
}
Address.cs
namespace FluentValidationSample.Models
{
public class Address
{
public int Id { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
}
}
- 다음으로
Validation
폴더 안에 두개의 클래스를 추가합니다.- CustomerValidator.cs
- AddressValidator.cs
- 위 2개의 클래스 내용은 다음과 같습니다.
CustomerValidator.cs
using FluentValidation;
using FluentValidationSample.Models;
namespace FluentValidationSample.Validation
{
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
// Check name is not null, empty and is between 1 and 250 characters
RuleFor(customer => customer.FirstName).NotNull().NotEmpty().Length(1, 250);
RuleFor(customer => customer.LastName).NotNull().NotEmpty().Length(1, 250);
// Validate Phone with a custom error message
RuleFor(customer => customer.Phone).NotEmpty().WithMessage("Please add a phone number");
// Validate Age for submitted customer has to be between 21 and 100 years old.
RuleFor(customer => customer.Age).InclusiveBetween(21, 100);
// Validate the address (it's a complex property)
RuleFor(customer => customer.Address).InjectValidator();
}
}
}
AddressValidator.cs
using FluentValidation;
using FluentValidationSample.Models;
namespace FluentValidationSample.Validation
{
public class AddressValidator : AbstractValidator<Address>
{
public AddressValidator()
{
// Validate address is not longer than 60 chars as many APIs for carriers doesn't allow
// longer address as they cannot be at the shipment label
RuleFor(address => address.Street1).NotNull().NotEmpty().Length(1, 60);
RuleFor(address => address.Street2).Length(1, 60);
// Validate Country
RuleFor(address => address.Country).NotNull().NotEmpty().WithMessage("Please add the destination country");
// Validate City and ZIP
RuleFor(address => address.PostalCode).NotNull().NotEmpty().WithMessage("Please add reciever postcode");
RuleFor(address => address.PostalCode).Must(ValidPostCode).WithMessage("Postalcode is not valid");
RuleFor(address => address.City).NotNull().NotEmpty().WithMessage("Please add the reciever city");
}
private bool ValidPostCode(string postalCode)
{
// Add logic for validating postalcode here...
return true;
}
}
}
- 보시다시피 FluentValidation에서
AbstractValidator<T>
를 상속 받았습니다. - 여기서
<T>
는 계속 진행하기 전에 유효성을 검사하려는 클래스/모델 입니다. - 유효성 검사 규칙은 항상 클래스의 생성자 내부에 정의되어야 합니다.
- 새 유효성 검사 규칙을 만들 때마다
RuleFor
메서드를 사용하고 유효성을 검사할 속성을 지정하는 람다식을 전달하기만 하면 됩니다. - 람다식 후에 내장 유효성 검사기를 사용하거나 직접 생성하여 사용할 수 있습니다.
Customer.cs
에서 주소 클래스/모델의 유효성을 검사하기 위해 다른 유효성 검사기를 주입하고 있습니다.List<T>
와 같은 데이터 컬렉션이 T is Address 인 경우, 복합 유형의 유효성 검사가 필요합니다.- 다음 코드를 추가하여 그렇게 할 수 있습니다.
RuleForEach(x => x.Addresses).SetValidator(new AddressValidator());
- 위와 같이 코드를 작성하게 되면, 주소 유효성 검사기를 사용하여 목록의 각 객체에 대해 유효성 검사를 실행하는 새 규칙이 생성됩니다.
API Endpoints 에서 모델 검증하기
- 이를 쉽게 하기 위해 고객과 관련된 요청을 처리하는 매우 간단한 컨트롤러를 만들었습니다.
CustomerController.cs
컨트롤러를 생성하였고, 다음 코드를 추가하였습니다.참고로, 컨트롤러에서 FluentValidation을 사용할 때 이전에 등록한 미들웨어가 올바른 유효성 검사기를 자동으로 선택하므로 사용하려는 유효성 검사기를 지정할 필요가 없습니다. 유요성 검사를 수행하기 위해 여전히 ModelState를 사용하고 유효성 검사기가 실패하면 ModelState에서 반환됩니다.
using FluentValidationSample.Models;
using Microsoft.AspNetCore.Mvc;
namespace FluentValidationSample.Controllers
{
[ApiController]
[Route("[controller]")]
public class CustomerController : ControllerBase
{
[HttpPost("add")]
public IActionResult Add(Customer model)
{
if(!ModelState.IsValid)
{
return StatusCode(StatusCodes.Status400BadRequest, ModelState);
}
return StatusCode(StatusCodes.Status200OK, "Model is valid!");
}
}
}
- 위와 같이 컨트롤러 코드 작성을 완료 하였다면, 테스트 실행을 위해 애플리케이션을 가동하고 일부 샘플 데이터가 포함된 EndPoint 를 요청합니다.
- 다음은 오류를 생성하는 요청에 대한 몇 가지 샘플 데이터입니다.
{
"id": 0,
"firstName": "string",
"lastName": "string",
"age": 0,
"phone": 0,
"address": {
"id": 0,
"street1": "string",
"street2": "string",
"state": "string",
"country": "string",
"city": "string",
"postalCode": "string"
}
}
- POST 가 위의 데이터로 EndPoint 를 요청할 때 다음과 같은 오류가 발생합니다.
RuleFor 메서드에 WithMessage("
") 를 입력했기 때문에 이제 모델을 검증할 때 반환된 메시를 받고 유효하지 않은 일부 데이터를 보냈습니다. 위의 이미지에서 볼 수 있듯이 미들웨어는 자동으로 올바른 클래스/모델을 선택합니다.
원하는 경우 유효성 검사기를 만들려는 클래스/모델을 명시적으로 지정할 수도 있습니다.
그런 다음 Validate 메서드는 두 가지 속성이 있는 ValidationResult 객체를 반환합니다.
첫 번째는 IsValid 이며 유효성 검사가 성공했는지 여부를 알려주는 Bool 입니다.
두 번째 속성은 오류이며 오류에 대한 세부 정보와 함께 ValidationFailure 객체 컬렉션을 포함합니다.
코드로는 다음과 같이 구현할 수 있습니다.
using FluentValidationSample.Models;
using FluentValidationSample.Validation;
using Microsoft.AspNetCore.Mvc;
namespace FluentValidationSample.Controllers
{
[ApiController]
[Route("[controller]")]
public class CustomerController : ControllerBase
{
[HttpPost("add")]
public IActionResult Add(Customer model)
{
if(!ModelState.IsValid)
{
return StatusCode(StatusCodes.Status400BadRequest, ModelState);
}
return StatusCode(StatusCodes.Status200OK, "Model is valid!");
}
[HttpPut("update")]
public IActionResult Update(Customer model)
{
CustomerValidator customerValidator = new();
var validatorResult = customerValidator.Validate(model);
if(!validatorResult.IsValid)
{
return StatusCode(StatusCodes.Status400BadRequest, validatorResult.Errors);
}
return StatusCode(StatusCodes.Status200OK, "Model is valid for update!");
}
}
}
- 요청에 오류가 있는 업데이트 경로에 대해 PUT 요청을 하면 오류 유효성 검사 결과가 반환힙니다.
728x90
'C#' 카테고리의 다른 글
[C#] FluentFTP 샘플 프로그램 작성 (클린 아키텍처 적용) (0) | 2022.05.03 |
---|---|
[C# 벤치마크] 벤치마크 닷넷 static 메서드 성능 측정하기 (0) | 2022.05.03 |
[C#] Fluent Validation 이란? (0) | 2022.04.21 |
[C#] Polly Nuget 패키지 - Bulkhead Isolation, Rate-Limit (0) | 2022.04.20 |
[C#] Polly Nuget 패키지 - PolicyWrap (0) | 2022.04.20 |
이 글을 공유하기