Validation — Data Annotations, ModelState, [Required], [Range]

~20 daq · .NET Backend · Web API — Controller va Routing

Muammodan boshlaylik

[HttpPost]
public async Task<IActionResult> Yarat(MahsulotYaratDto dto)
{
    if (string.IsNullOrWhiteSpace(dto.Nomi)) return BadRequest("Nom majburiy");
    if (dto.Nomi.Length > 200) return BadRequest("Nom juda uzun");
    if (dto.Narx <= 0) return BadRequest("Narx musbat bo'lsin");
    if (dto.Qoldiq < 0) return BadRequest("Qoldiq manfiy bo'lmasin");
    // ... va har bir endpoint da shunday
}

Har bir action boshida o'nlab if. Xatolar birma-bir qaytariladi — mijoz birinchisini tuzatadi, keyingisini oladi, va hokazo. Format har joyda boshqacha.


Data Annotations

Qoidalar — DTO property lariga atribut sifatida (34-dars):

using System.ComponentModel.DataAnnotations;

public record MahsulotYaratDto
{
    [Required(ErrorMessage = "Nom majburiy")]
    [StringLength(200, MinimumLength = 2, ErrorMessage = "Nom 2–200 belgi")]
    public string Nomi { get; init; } = "";

    [Range(0.01, 1_000_000_000, ErrorMessage = "Narx musbat bo'lsin")]
    public decimal Narx { get; init; }

    [Range(0, 100_000)]
    public int Qoldiq { get; init; }

    [Required, Url]
    public string? RasmUrl { get; init; }
}
[HttpPost]
public async Task<IActionResult> Yarat(MahsulotYaratDto dto)   // tekshiruvlar yo'q!
{
    var m = await service.YaratAsync(dto);
    return CreatedAtAction(nameof(Ol), new { id = m.Id }, m);
}

[ApiController] model binding dan keyin validatsiyani avtomatik bajaradi. Xato bo'lsa — action umuman chaqirilmaydi, va 400 qaytadi.


Hayotiy o'xshatma

Aeroportdagi ro'yxatdan o'tish. Har bir yo'lovchini kassir uyda o'zicha tekshirmaydi — kirishda bitta standart tekshiruv: pasport bormi, chipta to'g'rimi, yuk og'irligi chegaradami. Muammo bo'lsa — yo'lovchiga barcha kamchiliklar ro'yxati bir marta aytiladi, samolyotga esa faqat tekshiruvdan o'tganlar chiqadi.


Avtomatik javob — ProblemDetails

POST /api/mahsulotlar
{ "nomi": "A", "narx": -5, "qoldiq": 10 }
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Nomi": ["Nom 2–200 belgi"],
    "Narx": ["Narx musbat bo'lsin"],
    "RasmUrl": ["The RasmUrl field is required."]
  },
  "traceId": "00-4bf92f..."
}

Barcha xatolar birdaniga, maydon bo'yicha guruhlangan, standart formatda (RFC 7807/9457). Frontend har bir xatoni tegishli forma maydoni ostida ko'rsata oladi.


Asosiy atributlar

Atribut Tekshiradi
[Required] Bo'sh emas (null, "")
[StringLength(max, MinimumLength = min)] String uzunligi
[MinLength], [MaxLength] String yoki kolleksiya uzunligi
[Range(min, max)] Son oralig'i
[EmailAddress] Email formati
[Phone], [Url], [CreditCard] Formatlar
[RegularExpression(@"...")] Regex ga moslik
[Compare("Parol")] Boshqa property ga tengligi (parolni takrorlash)
[AllowedValues("a", "b")] (.NET 8+) Ruxsat etilgan qiymatlar
[Length(min, max)] (.NET 8+) Uzunlik oralig'i
public record RoyxatDto
{
    [Required, EmailAddress]
    public string Email { get; init; } = "";

    [Required, MinLength(8)]
    [RegularExpression(@"^(?=.*[A-Za-z])(?=.*\d).+
quot;, ErrorMessage = "Parolda harf va raqam bo'lsin")] public string Parol { get; init; } = ""; [Compare(nameof(Parol), ErrorMessage = "Parollar mos emas")] public string ParolTakror { get; init; } = ""; [RegularExpression(@"^\+998\d{9}
quot;, ErrorMessage = "Telefon +998XXXXXXXXX formatida")] public string? Telefon { get; init; } }

Nullable va [Required]

Nullable reference types (14-dars) yoqilgan bo'lsa, string Nomi (nullable emas) avtomatik majburiy hisoblanadi; string? Telefon — ixtiyoriy.

[!INFO] [Required] qiymat turlari uchun (int, decimal) ishlamaydi — ular hech qachon null emas, JSON da yo'q bo'lsa 0 bo'ladi. "Narx yuborilganmi?" ni tekshirish uchun decimal? + [Required] yoki [Range] bilan 0 ni rad eting.


IValidatableObject — bir nechta maydon

public record AksiyaDto : IValidatableObject
{
    public DateTime Boshlanish { get; init; }
    public DateTime Tugash { get; init; }
    [Range(1, 90)] public int Foiz { get; init; }

    public IEnumerable<ValidationResult> Validate(ValidationContext ctx)
    {
        if (Tugash <= Boshlanish)
            yield return new ValidationResult("Tugash sanasi boshlanishdan keyin bo'lsin", [nameof(Tugash)]);
        if (Boshlanish < DateTime.Today)
            yield return new ValidationResult("O'tgan sana bo'lmaydi", [nameof(Boshlanish)]);
    }
}

Custom atribut — 34-dars (ValidationAttribute).


ModelState — qo'lda

[HttpPost]
public async Task<IActionResult> Yarat(MahsulotYaratDto dto)
{
    if (await service.NomBandmiAsync(dto.Nomi))
    {
        ModelState.AddModelError(nameof(dto.Nomi), "Bu nomli mahsulot allaqachon bor");
        return ValidationProblem(ModelState);       // xuddi shu formatda 400
    }
    // ...
}

Bazaga murojaat qiladigan tekshiruvlar (noyoblik) — servis yoki action ichida, lekin javob formati bir xil.

[ApiController] siz if (!ModelState.IsValid) return BadRequest(ModelState); ni har bir action da qo'lda yozish kerak bo'lardi.

[!TIP] Validatsiyaning ikki darajasi: kiruvchi ma'lumot shakli (format, uzunlik, oraliq) — DTO atributlarida yoki FluentValidation da; biznes qoidalari (qoldiq yetarlimi, foydalanuvchi bloklanmaganmi) — servis qatlamida. Atributlarga bazaga murojaat va biznes mantiq qo'shmang.


Tez-tez uchraydigan xatolar

Atributlar entity da, DTO da emas — API kirishi tekshirilmaydi.

[Required] int da — hech qachon ishlamaydi; int? yoki [Range].

[ApiController] siz avtomatik validatsiyani kutish — ModelState.IsValid ni tekshirish kerak.


Xulosa

Data Annotations — validatsiya qoidalari DTO property larida: [Required], [StringLength], [Range], [EmailAddress], [RegularExpression], [Compare]. [ApiController] ularni avtomatik tekshiradi; xato bo'lsa action chaqirilmaydi va 400 ProblemDetails — barcha xatolar maydon bo'yicha.

Nullable bo'lmagan reference turlar — avtomatik majburiy; qiymat turlari uchun [Required] ishlamaydi. Bir nechta maydonli qoidalar — IValidatableObject; qo'lda — ModelState.AddModelError + ValidationProblem. Biznes qoidalari — servisda.


Keyingi dars

47-dars: FluentValidation — AbstractValidator, kuchli validatsiya qoidalari