Route parametrlari — {id}, [FromRoute], [FromQuery], [FromBody]
Muammodan boshlaylik
Bitta so'rovda ma'lumot har xil joydan keladi:
PUT /api/dokonlar/7/mahsulotlar/42?bildirishnoma=true
Authorization: Bearer eyJ...
X-Til: uz
Content-Type: application/json
{ "narx": 135000, "qoldiq": 8 }
7va42— URL yo'lidabildirishnoma— query string daX-Til— sarlavhada- Narx va qoldiq — body da (JSON)
Har birini Request.Query["..."], Request.Headers["..."] bilan qo'lda o'qib, turga o'girish — takroriy va xatoga moyil.
Model binding nima?
Model binding — ASP.NET Core so'rovning turli qismlaridagi qiymatlarni action parametrlariga avtomatik, kerakli turga o'girib beradi:
[HttpPut("/api/dokonlar/{dokonId:int}/mahsulotlar/{id:int}")]
public IActionResult Yangila(
[FromRoute] int dokonId,
[FromRoute] int id,
[FromQuery] bool bildirishnoma,
[FromHeader(Name = "X-Til")] string? til,
[FromBody] MahsulotYangilaDto dto)
{
// hammasi tayyor, to'g'ri turda
return NoContent();
}
Hayotiy o'xshatma
Pochta posilkasi. Manzil konvert ustida (route), "tez yetkazish" belgisi — yorliqda (query), jo'natuvchi muhri — burchakda (header), asosiy narsa — qutining ichida (body). Pochtachi har birini o'z joyidan o'qiydi — siz hammasini bitta varaqqa ko'chirib yozishingiz shart emas.
Manba atributlari
| Atribut | Qayerdan | Misol |
|---|---|---|
[FromRoute] |
URL yo'li | /mahsulotlar/{id} |
[FromQuery] |
Query string | ?sahifa=2&kategoriya=telefon |
[FromBody] |
Body (JSON) | POST/PUT ma'lumoti |
[FromHeader] |
Sarlavha | X-Til: uz |
[FromForm] |
Forma, fayl yuklash | multipart/form-data |
[FromServices] |
DI konteyner | servis (38-dars) |
[ApiController] taxminlari
Atribut yozilmasa, [ApiController] taxmin qiladi:
- Murakkab tur (klass, record) →
[FromBody] - Route shablonida nomi bor parametr →
[FromRoute] - Qolgan oddiy turlar (
int,string,bool) →[FromQuery] IFormFile→[FromForm]- DI da ro'yxatdan o'tgan tur →
[FromServices]
[HttpGet("{id:int}")]
public IActionResult Ol(int id, bool batafsil) { } // id — route, batafsil — query
[!TIP] Aniqlik uchun murakkab holatlarda atributlarni yozing. Ayniqsa query dan murakkab obyekt olishda:
[FromQuery] MahsulotFiltr filtr— busiz u body dan qidiriladi.
Route parametrlari
[HttpGet("{id:int}")] // majburiy
[HttpGet("{slug}")]
[HttpGet("kategoriya/{kategoriya}/{sahifa:int?}")] // ? — ixtiyoriy
[HttpGet("{*yol}")] // catch-all: qolgan butun yo'l
Constraint lar (41-dars) — :int, :guid, :min(1), :length(3,50). Ular validatsiya emas, marshrut tanlash: mos kelmasa — 404.
Query string
Oddiy
// GET /api/mahsulotlar?kategoriya=telefon&min=1000000&sahifa=2
[HttpGet]
public IActionResult Royxat(string? kategoriya, decimal? min, decimal? max, int sahifa = 1, int hajm = 20)
Klass bilan — ko'p filtrlar
public class MahsulotFiltr
{
public string? Kategoriya { get; init; }
public decimal? Min { get; init; }
public decimal? Max { get; init; }
public string Saralash { get; init; } = "yangi";
[Range(1, int.MaxValue)]
public int Sahifa { get; init; } = 1;
[Range(1, 100)]
public int Hajm { get; init; } = 20;
}
[HttpGet]
public async Task<IActionResult> Royxat([FromQuery] MahsulotFiltr filtr, CancellationToken ct) =>
Ok(await service.RoyxatAsync(filtr, ct));
[Range(1, 100)] — ?hajm=1000000 bilan bazani bitta so'rovda to'liq yuklab olishning oldini oladi.
Massiv
// GET /api/mahsulotlar?id=1&id=5&id=9
[HttpGet]
public IActionResult BirNechta([FromQuery(Name = "id")] int[] idlar)
Body — JSON
[HttpPost]
public IActionResult Yarat([FromBody] MahsulotYaratDto dto)
- Bitta action da faqat bitta
[FromBody]parametr bo'lishi mumkin — butun body bitta ob'ektga - JSON maydon nomlari katta-kichik harfga sezgir emas (
nomi=Nomi) - Noto'g'ri JSON yoki tur mos emas (
"narx": "qimmat") → avtomatik400([ApiController])
Fayl yuklash — [FromForm]
[HttpPost("{id:int}/rasm")]
[RequestSizeLimit(5 * 1024 * 1024)]
public async Task<IActionResult> RasmYukla(int id, IFormFile fayl, CancellationToken ct)
{
if (fayl.Length == 0) return BadRequest("Fayl bo'sh");
if (!new[] { "image/jpeg", "image/png", "image/webp" }.Contains(fayl.ContentType))
return BadRequest("Faqat rasm");
var nom = quot;{Guid.NewGuid()}{Path.GetExtension(fayl.FileName)}";
await using var oqim = System.IO.File.Create(Path.Combine("uploads", nom));
await fayl.CopyToAsync(oqim, ct);
return Ok(new { url = quot;/uploads/{nom}" });
}
[!WARNING] Foydalanuvchi bergan
fayl.FileNameni to'g'ridan-to'g'ri saqlash yo'li sifatida ishlatmang (../../appsettings.json— path traversal). O'zingiz yangi nom yarating (GUID), hajm va turni cheklang. Production da fayllarni server diskida emas — S3/Blob kabi saqlash xizmatida saqlang.
Tez-tez uchraydigan xatolar
Query dan murakkab tur, atributsiz — [ApiController] uni body dan kutadi → 415 Unsupported Media Type yoki 400.
Ikki [FromBody]:
InvalidOperationException: Action '...' has more than one parameter that was specified or inferred as bound from request body.
Ularni bitta DTO ga birlashtiring.
Route dagi nom va parametr nomi mos emas:
[HttpGet("{mahsulotId}")]
public IActionResult Ol(int id) // ❌ id doim 0 — nomlar mos emas
Xulosa
Model binding — so'rov qismlari action parametrlariga avtomatik va to'g'ri turda keladi. Manbalar: [FromRoute] (yo'l), [FromQuery] (query string), [FromBody] (JSON, bitta), [FromHeader], [FromForm] (fayl), [FromServices].
[ApiController] taxmini: murakkab tur — body, route nomi — route, oddiy — query. Ko'p filtrlar — [FromQuery] bilan klass va [Range] cheklovlari. Route parametr nomi action parametri nomi bilan bir xil bo'lsin. Fayl — IFormFile, nom va hajmni o'zingiz nazorat qiling.
Keyingi dars
45-dars: DTO — nima uchun, Entity dan ajratish, request/response DTO