Controllers, Routing & HTTP Verbs

লেসন ১৭ / ৫০

Basic Controller

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]    // → /api/products
public class ProductsController : ControllerBase {

    [HttpGet]                                       // GET /api/products
    public IActionResult GetAll() {
        return Ok(new[] { "Pen", "Book" });
    }

    [HttpGet("{id}")]                              // GET /api/products/5
    public IActionResult GetById(int id) {
        if (id < 1) return BadRequest("Invalid id");
        return Ok(new { Id = id, Name = "Pen" });
    }

    [HttpPost]                                      // POST /api/products
    public IActionResult Create([FromBody] ProductDto dto) {
        // create logic
        return CreatedAtAction(nameof(GetById), new { id = 1 }, dto);
    }

    [HttpPut("{id}")]                              // PUT /api/products/5
    public IActionResult Update(int id, [FromBody] ProductDto dto) {
        return NoContent();
    }

    [HttpDelete("{id}")]                            // DELETE /api/products/5
    public IActionResult Delete(int id) {
        return NoContent();
    }
}

Routing

// Conventional (Program.cs)
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

// Attribute routing ⭐
[Route("api/v1/users")]
[Route("api/[controller]")]      // [controller] = class name without "Controller"

// Multiple routes
[HttpGet]
[Route("active")]
[Route("status/active")]
public IActionResult GetActive() { ... }

// Route constraints
[HttpGet("{id:int:min(1)}")]
[HttpGet("{slug:alpha:length(3,50)}")]
[HttpGet("{date:datetime}")]

IActionResult vs ActionResult<T>

// IActionResult — generic, less type info
public IActionResult Get() => Ok(user);

// ActionResult<T> ⭐ — both type + status
public ActionResult<User> Get(int id) {
    var user = _db.Users.Find(id);
    if (user == null) return NotFound();
    return user;       // implicit Ok()
}

Response Helpers

return Ok(data);                    // 200
return Created("/api/users/5", user); // 201
return CreatedAtAction(nameof(Get), new { id = 5 }, user);
return NoContent();                  // 204
return BadRequest("Invalid");        // 400
return Unauthorized();               // 401
return Forbid();                     // 403
return NotFound();                   // 404
return Conflict("Duplicate");        // 409
return UnprocessableEntity(errors);  // 422
return StatusCode(500, "Server error");
return File(bytes, "application/pdf");

Parameter Binding

[HttpGet("{id}")]
public IActionResult Get(
    [FromRoute] int id,                           // URL path
    [FromQuery] string search,                    // ?search=xxx
    [FromHeader(Name="X-Api-Key")] string apiKey, // header
    [FromBody] UserDto dto                        // JSON body
) { ... }

Minimal API (.NET 6+)

// Program.cs — Controller-less
app.MapGet("/api/products", () => new[] { "Pen", "Book" });

app.MapGet("/api/products/{id:int}", (int id) => {
    return Results.Ok(new { Id = id });
});

app.MapPost("/api/products", (ProductDto dto) =>
    Results.Created($"/api/products/1", dto));