HTTP GET va POST — [HttpGet], [HttpPost], IActionResult

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

Muammodan boshlaylik

Mahsulotlar API si uchun ikki asosiy amal:

  • Mahsulotlar ro'yxatini va bitta mahsulotni olish
  • Yangi mahsulot yaratish

Qaysi HTTP metodini ishlatish, javobda qanday status qaytarish, yaratilgan ob'ektni qanday qaytarish? Va action qaytish turi qanday bo'lishi kerak — Mahsulot, IActionResult yoki ActionResult<Mahsulot>?


HTTP metodlari

Metod Vazifa Body Xavfsiz (o'zgartirmaydi) Idempotent
GET Olish Yo'q ✅ ✅
POST Yaratish Bor ❌ ❌
PUT To'liq almashtirish Bor ❌ ✅
PATCH Qisman o'zgartirish Bor ❌ ❌
DELETE O'chirish Odatda yo'q ❌ ✅

Idempotent — bir xil so'rovni bir necha marta yuborish natijasi bir marta yuborish bilan bir xil. POST ikki marta — ikkita mahsulot; PUT ikki marta — bir xil holat. PUT, PATCH, DELETE — 43-dars.


Hayotiy o'xshatma

Kutubxona. GET — katalogdan kitobni qidirib ko'rish: necha marta qarasangiz ham kutubxona o'zgarmaydi. POST — yangi kitobni fondga topshirish: ikki marta topshirsangiz — ikki nusxa paydo bo'ladi.


GET — olish

[ApiController]
[Route("api/[controller]")]
public class MahsulotlarController(IMahsulotService service) : ControllerBase
{
    // GET api/mahsulotlar?kategoriya=telefon&sahifa=1
    [HttpGet]
    public async Task<ActionResult<List<MahsulotDto>>> Royxat(
        [FromQuery] string? kategoriya,
        [FromQuery] int sahifa = 1,
        CancellationToken ct = default)
    {
        var natija = await service.RoyxatAsync(kategoriya, sahifa, ct);
        return Ok(natija);
    }

    // GET api/mahsulotlar/5
    [HttpGet("{id:int}")]
    public async Task<ActionResult<MahsulotDto>> Ol(int id, CancellationToken ct)
    {
        var m = await service.OlAsync(id, ct);
        if (m is null) return NotFound();
        return m;                  // ActionResult<T> — avtomatik Ok(m)
    }
}
  • Ro'yxat — 200 (bo'sh bo'lsa ham [] va 200, 404 emas)
  • Bitta element topilmasa — 404

POST — yaratish

public record MahsulotYaratDto(string Nomi, decimal Narx, int Qoldiq, int KategoriyaId);

[HttpPost]
public async Task<ActionResult<MahsulotDto>> Yarat([FromBody] MahsulotYaratDto dto, CancellationToken ct)
{
    var yangi = await service.YaratAsync(dto, ct);
    return CreatedAtAction(nameof(Ol), new { id = yangi.Id }, yangi);
}

So'rov:

POST /api/mahsulotlar
Content-Type: application/json

{ "nomi": "Choynak", "narx": 120000, "qoldiq": 10, "kategoriyaId": 2 }

Javob:

HTTP/1.1 201 Created
Location: http://localhost:5123/api/mahsulotlar/42
Content-Type: application/json

{ "id": 42, "nomi": "Choynak", "narx": 120000, "qoldiq": 10 }
  • 201 Created — yaratildi (200 emas)
  • Location sarlavhasi — yangi resursning manzili; CreatedAtAction uni Ol action i va id dan avtomatik yasaydi
  • Body da — yaratilgan ob'ekt (server bergan Id bilan)
  • JSON → MahsulotYaratDto — avtomatik (model binding); [ApiController] da [FromBody] taxmin qilinadi

[!WARNING] POST uchun alohida kiruvchi DTO (MahsulotYaratDto) ishlating — entity yoki to'liq modelni emas. Aks holda mijoz Id, YaratilganSana, Tasdiqlangan kabi maydonlarni ham yuborib, o'zi o'rnatishi mumkin (over-posting). DTO — 45-dars.


Qaytish turlari

1. Aniq tur

[HttpGet]
public List<MahsulotDto> Royxat() => service.Royxat();     // doim 200

Faqat bitta natija bo'lsa; NotFound() qaytarib bo'lmaydi.

2. IActionResult

[HttpGet("{id}")]
public IActionResult Ol(int id)
{
    var m = service.Ol(id);
    return m is null ? NotFound() : Ok(m);
}

Har qanday status qaytarish mumkin, lekin javob turi kodda ko'rinmaydi — Swagger uni bilmaydi.

3. ActionResult<T> — tavsiya etiladi

[HttpGet("{id}")]
public ActionResult<MahsulotDto> Ol(int id)
{
    var m = service.Ol(id);
    if (m is null) return NotFound();
    return m;                   // T dan avtomatik 200
}
Aniq tur IActionResult ActionResult<T>
Har xil status kodlar ❌ ✅ ✅
Javob turi ko'rinadi (Swagger) ✅ ❌ (atribut kerak) ✅
return obj; qisqa yozuv ✅ ❌ ✅

[!TIP] Swagger uchun mumkin bo'lgan barcha javoblarni ko'rsating: [ProducesResponseType(StatusCodes.Status201Created)], [ProducesResponseType(StatusCodes.Status400BadRequest)]. Klient dasturchilari va avtomatik generatsiya qilinadigan klient kodi aniq bo'ladi (72-dars).

Async

Baza yoki tarmoq bilan ishlasa — async Task<ActionResult<T>> va CancellationToken (30-dars).


Bir nechta route — bitta action

[HttpGet("{id:int}")]
[HttpGet("kod/{kod}")]          // ikkalasi ham shu action ga
public ActionResult<MahsulotDto> Ol(int? id, string? kod) { ... }

Kam ishlatiladi — odatda alohida action yaxshiroq.


Tez-tez uchraydigan xatolar

POST dan 200 qaytarish — 201 Created + Location.

Body bo'sh keladi:

Content-Type: application/json sarlavhasi yo'q yoki JSON maydon nomlari mos emas → DTO null/standart qiymatlar bilan. [ApiController] da noto'g'ri JSON → avtomatik 400.

GET da body yuborish — GET body si standart bo'yicha ma'nosiz; filtrlar — query string.

Bo'sh ro'yxat uchun 404 — ro'yxat endpoint i bo'sh natijada 200 va [].


Xulosa

HTTP metodlari: GET — olish (xavfsiz, idempotent), POST — yaratish (idempotent emas). GET ro'yxat — 200 va [] ham; bitta topilmasa — 404. POST — 201 Created + Location (CreatedAtAction) + yaratilgan ob'ekt.

Kiruvchi ma'lumot — alohida DTO, over-posting dan himoya. Qaytish turi — ActionResult<T>: har xil status va Swagger uchun aniq tur. Async action lar + CancellationToken; [ProducesResponseType] — hujjat uchun.


Keyingi dars

43-dars: HTTP PUT, PATCH, DELETE — update va o'chirish