Section 3 of 6

HTTP Methods

🎯 What You'll Learn

  • Common HTTP methods (GET, POST, PUT, DELETE, PATCH)
  • When to use each method
  • Idempotency and safety
  • RESTful API conventions
  • ASP.NET Core attributes for methods
  • Complete InvenTrack CRUD examples

HTTP Methods Overview

Method Purpose Safe Idempotent
GET Retrieve data βœ… βœ…
POST Create new resource ❌ ❌
PUT Update/replace resource ❌ βœ…
PATCH Partial update ❌ ❌
DELETE Delete resource ❌ βœ…
HEAD Get headers only βœ… βœ…
OPTIONS Get allowed methods βœ… βœ…
πŸ’‘ Key Concepts

Safe: Doesn't modify server state (read-only)
Idempotent: Multiple identical requests have the same effect as one

1. GET - Retrieve Data

GET retrieves data without modifying server state.

Characteristics

  • βœ… Safe (read-only)
  • βœ… Idempotent (same result every time)
  • ❌ No request body
  • βœ… Can be cached
  • βœ… Can be bookmarked
GET Examples C#
// Get all products
[HttpGet]
public async Task<IActionResult> GetProducts()
{
    var products = await _productService.GetAllAsync();
    return Ok(products);
}

// Get single product
[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
    var product = await _productService.GetByIdAsync(id);
    if (product == null)
        return NotFound();
    return Ok(product);
}

// Get with query parameters
[HttpGet("search")]
public async Task<IActionResult> SearchProducts(
    [FromQuery] string name,
    [FromQuery] decimal? minPrice,
    [FromQuery] decimal? maxPrice)
{
    var products = await _productService.SearchAsync(name, minPrice, maxPrice);
    return Ok(products);
}
HTTP Request HTTP
GET /api/products/123 HTTP/1.1
Host: inventrackapp.com

2. POST - Create Resource

POST creates a new resource on the server.

Characteristics

  • ❌ Not safe (modifies state)
  • ❌ Not idempotent (creates new resource each time)
  • βœ… Has request body
  • Returns 201 Created with Location header
POST Example C#
[HttpPost]
public async Task<IActionResult> CreateProduct([FromBody] CreateProductDto dto)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);
    
    var product = await _productService.CreateAsync(dto);
    
    // Return 201 Created with Location header
    return CreatedAtAction(
        nameof(GetProduct),
        new { id = product.Id },
        product);
}
HTTP Request HTTP
POST /api/products HTTP/1.1
Host: inventrackapp.com
Content-Type: application/json

{
  "name": "Laptop",
  "price": 999.99,
  "quantityInStock": 50
}
HTTP Response HTTP
HTTP/1.1 201 Created
Location: /api/products/123
Content-Type: application/json

{
  "id": 123,
  "name": "Laptop",
  "price": 999.99,
  "quantityInStock": 50
}

3. PUT - Update/Replace Resource

PUT replaces an entire resource.

Characteristics

  • ❌ Not safe (modifies state)
  • βœ… Idempotent (same result every time)
  • βœ… Has request body
  • Replaces entire resource
  • Returns 204 No Content or 200 OK
PUT Example C#
[HttpPut("{id}")]
public async Task<IActionResult> UpdateProduct(
    int id,
    [FromBody] UpdateProductDto dto)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);
    
    var success = await _productService.UpdateAsync(id, dto);
    
    if (!success)
        return NotFound();
    
    return NoContent(); // 204
}
HTTP Request HTTP
PUT /api/products/123 HTTP/1.1
Host: inventrackapp.com
Content-Type: application/json

{
  "name": "Laptop Pro",
  "price": 1299.99,
  "quantityInStock": 30
}

4. PATCH - Partial Update

PATCH updates only specific fields of a resource.

Characteristics

  • ❌ Not safe (modifies state)
  • ❌ Not idempotent (depends on implementation)
  • βœ… Has request body
  • Updates specific fields only
PATCH Example C#
[HttpPatch("{id}/stock")]
public async Task<IActionResult> UpdateStock(
    int id,
    [FromBody] UpdateStockDto dto)
{
    var success = await _productService.UpdateStockAsync(id, dto.QuantityInStock);
    
    if (!success)
        return NotFound();
    
    return NoContent();
}
HTTP Request HTTP
PATCH /api/products/123/stock HTTP/1.1
Host: inventrackapp.com
Content-Type: application/json

{
  "quantityInStock": 75
}
ℹ️ PUT vs PATCH

PUT: Replace entire resource (all fields required)
PATCH: Update specific fields (only changed fields sent)

5. DELETE - Remove Resource

DELETE removes a resource from the server.

Characteristics

  • ❌ Not safe (modifies state)
  • βœ… Idempotent (deleting twice = same result)
  • ❌ No request body
  • Returns 204 No Content or 200 OK
DELETE Example C#
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
    var success = await _productService.DeleteAsync(id);
    
    if (!success)
        return NotFound();
    
    return NoContent(); // 204
}
HTTP Request HTTP
DELETE /api/products/123 HTTP/1.1
Host: inventrackapp.com

RESTful API Conventions

Operation Method URL Success Code
List all GET /api/products 200 OK
Get one GET /api/products/123 200 OK
Create POST /api/products 201 Created
Update (full) PUT /api/products/123 204 No Content
Update (partial) PATCH /api/products/123 204 No Content
Delete DELETE /api/products/123 204 No Content

Complete InvenTrack CRUD Example

Controllers/ProductsController.cs C#
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    // GET: api/products
    [HttpGet]
    public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts()
    {
        var products = await _productService.GetAllAsync();
        return Ok(products);
    }

    // GET: api/products/123
    [HttpGet("{id}")]
    public async Task<ActionResult<ProductDto>> GetProduct(int id)
    {
        var product = await _productService.GetByIdAsync(id);
        
        if (product == null)
            return NotFound();
        
        return Ok(product);
    }

    // POST: api/products
    [HttpPost]
    public async Task<ActionResult<ProductDto>> CreateProduct(
        [FromBody] CreateProductDto dto)
    {
        var product = await _productService.CreateAsync(dto);
        
        return CreatedAtAction(
            nameof(GetProduct),
            new { id = product.Id },
            product);
    }

    // PUT: api/products/123
    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateProduct(
        int id,
        [FromBody] UpdateProductDto dto)
    {
        var success = await _productService.UpdateAsync(id, dto);
        
        if (!success)
            return NotFound();
        
        return NoContent();
    }

    // PATCH: api/products/123/stock
    [HttpPatch("{id}/stock")]
    public async Task<IActionResult> UpdateStock(
        int id,
        [FromBody] UpdateStockDto dto)
    {
        var success = await _productService.UpdateStockAsync(id, dto.QuantityInStock);
        
        if (!success)
            return NotFound();
        
        return NoContent();
    }

    // DELETE: api/products/123
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(int id)
    {
        var success = await _productService.DeleteAsync(id);
        
        if (!success)
            return NotFound();
        
        return NoContent();
    }
}

Key Takeaways

  • GET: Retrieve data (safe, idempotent)
  • POST: Create new resource (not safe, not idempotent)
  • PUT: Replace entire resource (not safe, idempotent)
  • PATCH: Partial update (not safe, not idempotent)
  • DELETE: Remove resource (not safe, idempotent)
  • Safe: Read-only operations (GET, HEAD, OPTIONS)
  • Idempotent: Same result when called multiple times (GET, PUT, DELETE)
  • Use [HttpGet], [HttpPost], etc. attributes
  • Follow RESTful conventions for consistency
  • Return appropriate status codes (200, 201, 204, 404)
🎯 Next Steps

You now understand HTTP methods! In the next section, we'll explore Status Codesβ€”what they mean, when to use them, and how to return the right status code for every situation.