Controller — [ApiController], [Route], ControllerBase
Muammodan boshlaylik
Do'kon API sida o'nlab endpoint: mahsulotlar, kategoriyalar, buyurtmalar, foydalanuvchilar. Hammasini Program.cs da app.MapGet(...) bilan yozsangiz — yuzlab qatorli fayl. Kerak: bog'liq endpoint larni guruhlash, umumiy sozlamalarni (URL prefiksi, avtorizatsiya) bir joyda berish.
Controller nima?
Controller — bitta resurs (mahsulot, buyurtma) bilan bog'liq HTTP endpoint lar (action lar) to'plangan klass:
using Microsoft.AspNetCore.Mvc;
namespace DokonApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class MahsulotlarController(IMahsulotService service) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<MahsulotDto>>> Hammasi(CancellationToken ct) =>
Ok(await service.HammasiAsync(ct));
[HttpGet("{id:int}")]
public async Task<ActionResult<MahsulotDto>> Ol(int id, CancellationToken ct)
{
var m = await service.OlAsync(id, ct);
return m is null ? NotFound() : Ok(m);
}
}
Qoidalar:
- Klass nomi
...Controllerbilan tugaydi ControllerBasedan meros (API uchun;Controller— MVC View lari bilan)publicmetodlar — action lar- Bog'liqliklar — konstruktor orqali (38-dars)
Program.cs da:
builder.Services.AddControllers();
// ...
app.MapControllers();
Hayotiy o'xshatma
Bank filiallaridagi bo'limlar. "Kreditlar" bo'limi (controller) — kredit bilan bog'liq barcha xizmatlar: ariza berish, holatini bilish, to'lash (action lar). Eshik ustidagi yozuv ([Route]) qaysi bo'lim ekanini ko'rsatadi. Har bir bo'lim o'z ishini biladi va boshqalariga aralashmaydi.
[ApiController] — nima beradi
| Imkoniyat | Ma'nosi |
|---|---|
| Avtomatik 400 | Model validatsiyasidan o'tmasa — action chaqirilmaydi, 400 + xatolar (46-dars) |
| Manba taxmini | Murakkab tur — [FromBody], oddiy — route/query dan |
| ProblemDetails | Xatolar standart formatda (RFC 7807, 65-dars) |
| Attribute routing majburiy | Har bir controller da [Route] bo'lishi kerak |
Barcha API controller larga qo'ying.
[Route] — URL
[Route("api/[controller]")] // api/mahsulotlar
[Route("api/v1/mahsulotlar")] // aniq yozilgan
[Route("api/[controller]/[action]")] // api/mahsulotlar/qidirish — metod nomi bilan (kam ishlatiladi)
[controller] — klass nomidan Controller qismi olib tashlangan (MahsulotlarController → Mahsulotlar; URL lar katta-kichik harfga sezgir emas).
Action darajasidagi route — klassnikiga qo'shiladi:
[Route("api/mahsulotlar")]
public class MahsulotlarController : ControllerBase
{
[HttpGet] // GET api/mahsulotlar
[HttpGet("{id:int}")] // GET api/mahsulotlar/5
[HttpGet("qidiruv")] // GET api/mahsulotlar/qidiruv?q=...
[HttpGet("{id:int}/sharhlar")] // GET api/mahsulotlar/5/sharhlar
[HttpGet("/api/aksiyalar")] // "/" bilan — klass prefiksini e'tiborsiz qoldiradi
}
Route constraint lar
[HttpGet("{id:int}")] // faqat butun son
[HttpGet("{slug:alpha}")] // faqat harflar
[HttpGet("{id:guid}")]
[HttpGet("{sana:datetime}")]
[HttpGet("{sahifa:int:min(1)}")]
/api/mahsulotlar/abc — {id:int} ga mos kelmaydi → 404, action ga noto'g'ri qiymat yetib bormaydi.
ControllerBase yordamchilari
return Ok(natija); // 200
return Created(quot;/api/mahsulotlar/{m.Id}", m);
return CreatedAtAction(nameof(Ol), new { id = m.Id }, m); // 201 + Location
return NoContent(); // 204
return BadRequest("Noto'g'ri so'rov"); // 400
return Unauthorized(); // 401
return Forbid(); // 403
return NotFound(); // 404
return Conflict("Bu email band"); // 409
return Problem("Xato", statusCode: 500); // ProblemDetails
Status kodlar — 48-dars.
User.Identity?.Name // joriy foydalanuvchi (60-dars)
HttpContext // so'rov konteksti
Request.Headers // sarlavhalar
Yupqa controller
[!TIP] Controller — yupqa bo'lsin: so'rovni qabul qilish, servisni chaqirish, HTTP javobini qaytarish. Biznes mantiq (narx hisoblash, qoidalar), baza so'rovlari — servislarda. Shunda mantiqni testlash va boshqa joydan (fon vazifasi, boshqa endpoint) chaqirish oson.
// ❌ hamma narsa controller da
[HttpPost]
public async Task<IActionResult> Yarat(BuyurtmaDto dto)
{
var mahsulot = await _db.Mahsulotlar.FindAsync(dto.MahsulotId);
if (mahsulot.Qoldiq < dto.Soni) return BadRequest();
mahsulot.Qoldiq -= dto.Soni;
var summa = mahsulot.Narx * dto.Soni * (dto.Vip ? 0.9m : 1m);
// ... 40 qator
}
// ✅
[HttpPost]
public async Task<IActionResult> Yarat(BuyurtmaDto dto, CancellationToken ct)
{
var natija = await _buyurtmaService.YaratAsync(dto, ct);
return CreatedAtAction(nameof(Ol), new { id = natija.Id }, natija);
}
Umumiy sozlamalar — atributlar va ota klass
[ApiController]
[Route("api/[controller]")]
[Authorize] // barcha action larga
[Produces("application/json")]
public abstract class ApiControllerBase : ControllerBase { }
public class BuyurtmalarController : ApiControllerBase
{
[AllowAnonymous] // istisno
[HttpGet("holat/{kod}")]
public IActionResult Holat(string kod) => Ok();
}
Tez-tez uchraydigan xatolar
[Route] yo'q:
InvalidOperationException: Action '...' does not have an attribute route. Action methods on controllers annotated with ApiControllerAttribute must be attribute routed.
Ikki action bir xil route va metod:
AmbiguousMatchException: The request matched multiple endpoints.
public yordamchi metod — u ham action deb hisoblanadi; yordamchilarni private qiling yoki [NonAction].
Xulosa
Controller — bitta resurs uchun endpoint lar guruhi: ...Controller nomi, ControllerBase dan meros, public metodlar — action lar. [ApiController] — avtomatik validatsiya (400), manba taxmini, ProblemDetails.
[Route("api/[controller]")] + action route lari ([HttpGet("{id:int}")]) qo'shiladi; constraint lar (:int, :guid) noto'g'ri qiymatni o'tkazmaydi. Yordamchilar: Ok, CreatedAtAction, NoContent, NotFound, BadRequest. Controller yupqa — mantiq servislarda; umumiy sozlamalar — atributlar va ota klass.
Keyingi dars
42-dars: HTTP GET va POST — [HttpGet], [HttpPost], IActionResult