DTO (Data Transfer Object) Pattern ⭐
API request/response এর জন্য আলাদা class — Entity directly expose না করা।
// Entity (DB)
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; } // sensitive!
public DateTime CreatedAt { get; set; }
}
// Request DTO
public class CreateUserDto {
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
// Response DTO
public class UserDto {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
// PasswordHash exclude!
}
Data Annotations Validation
using System.ComponentModel.DataAnnotations;
public class CreateUserDto {
[Required(ErrorMessage = "নাম দিন")]
[StringLength(100, MinimumLength = 2)]
public string Name { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[StringLength(50, MinimumLength = 8)]
[RegularExpression(@"^(?=.*[A-Z])(?=.*[0-9]).+$",
ErrorMessage = "Password must have uppercase + digit")]
public string Password { get; set; }
[Range(18, 100)]
public int Age { get; set; }
[Phone]
public string Phone { get; set; }
[Url]
public string Website { get; set; }
[Compare("Password")]
public string ConfirmPassword { get; set; }
}
Auto Validation ([ApiController])
[ApiController] // ← validation auto trigger
[Route("api/[controller]")]
public class UsersController : ControllerBase {
[HttpPost]
public IActionResult Create(CreateUserDto dto) {
// ModelState invalid হলে আগেই 400 return হয়েছে
// এখানে আসা মানে valid
var user = new User { Name = dto.Name, Email = dto.Email };
// ...
return Created($"/api/users/{user.Id}", user);
}
}
// 400 response আসবে:
// {
// "errors": {
// "Email": ["The Email field is required"],
// "Password": ["Password must have uppercase + digit"]
// }
// }
Manual Validation
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
Custom Validation Attribute
public class FutureDateAttribute : ValidationAttribute {
protected override ValidationResult IsValid(object value, ValidationContext ctx) {
if (value is DateTime date && date > DateTime.Now)
return ValidationResult.Success;
return new ValidationResult("Date must be future");
}
}
public class EventDto {
[FutureDate]
public DateTime EventDate { get; set; }
}
FluentValidation (3rd party) ⭐
# dotnet add package FluentValidation.AspNetCore
public class CreateUserValidator : AbstractValidator<CreateUserDto> {
public CreateUserValidator() {
RuleFor(x => x.Name).NotEmpty().Length(2, 100);
RuleFor(x => x.Email).NotEmpty().EmailAddress();
RuleFor(x => x.Password)
.NotEmpty()
.MinimumLength(8)
.Matches("[A-Z]").WithMessage("Need uppercase")
.Matches("[0-9]").WithMessage("Need digit");
RuleFor(x => x.Age).InclusiveBetween(18, 100);
}
}
// Program.cs
builder.Services.AddValidatorsFromAssemblyContaining<CreateUserValidator>();
AutoMapper (Entity ↔ DTO)
# dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
public class MappingProfile : Profile {
public MappingProfile() {
CreateMap<User, UserDto>();
CreateMap<CreateUserDto, User>();
}
}
// Use
public class UsersController : ControllerBase {
private readonly IMapper _mapper;
public UsersController(IMapper mapper) => _mapper = mapper;
public ActionResult<UserDto> Get(int id) {
var user = _db.Users.Find(id);
return _mapper.Map<UserDto>(user);
}
}