Mastering HTTP Methods
The Library Story
Imagine you are the manager of a library. Your customers (the clients) can interact with your books in exactly four ways:
- Read a book: You find it on the shelf and read it. (GET)
- Donate a new book: You add a brand new book to the shelves. (POST)
- Fix a torn page: You replace the damaged page with a new one. (PUT)
- Throw away a ruined book: You remove it from the library forever. (DELETE)
In REST APIs, these 4 actions are called HTTP Methods. They are the "verbs" that tell the server exactly what you want to do with the data.
Connecting to C#
If you were writing a basic C# program, you would create methods like this:
public Book GetBook(int id) { ... }
public void AddBook(Book newBook) { ... }
public void UpdateBook(int id, Book updatedBook) { ... }
public void DeleteBook(int id) { ... }
In the web world, APIs use specific **HTTP attributes** to represent these methods. Let's look at each one in deep detail.
The 4 Main HTTP Methods
GET is used to retrieve data from the server. It is the most common method on the internet—every time you open a webpage or an image, your browser is sending a GET request.
Golden Rule: A GET request must never change any data on the server. It is safe, read-only, and idempotent (calling it 1 time or 100 times gives the same result).
[HttpGet]
public IActionResult GetAllProducts()
{
// 1. Ask the database for a list of products
var products = _db.Products.ToList();
// 2. Return the data with a 200 OK status
return Ok(products);
}
Real-World Example
When you open Instagram and scroll through your feed, the app sends a GET /api/posts request to fetch the latest pictures. It doesn't create a new post; it just reads the data.
POST is used to send new data to the server to create a brand new resource.
Golden Rule: POST is NOT idempotent. If you send the exact same POST request 5 times, you will create 5 new items (e.g., accidentally clicking "Pay Now" 5 times creates 5 orders!).
[HttpPost]
public IActionResult CreateProduct(Product product)
{
// 1. Add the new product to the database
_db.Products.Add(product);
_db.SaveChanges();
// 2. Return a 201 Created status and the new product's details
return CreatedAtAction(nameof(GetAllProducts), product);
}
Real-World Example
When you fill out a sign-up form and click "Register", the website sends a POST /api/users request. It sends your name and password to create a new account in the database.
PUT is used to completely replace an existing record with a new version.
The Golden Rule of PUT: PUT is idempotent. If you send a PUT request to update Product #1 100 times with the same data, the product will only be updated once, and the result is always the same.
Imagine you have a Product with an ID, Name, and Price. If you use PUT, you must send all the fields (ID, Name, Price), even if you only want to change the price. If you leave out the name, it might be erased (set to null)!
[HttpPut("{id}")]
public IActionResult UpdateProduct(int id, Product product)
{
// 1. Check if the URL ID matches the body ID
if (id != product.Id)
return BadRequest("ID mismatch"); // Returns 400 Bad Request
// 2. Update the entire record in the database
_db.Products.Update(product);
_db.SaveChanges();
// 3. Return 204 No Content (Success, but nothing to send back)
return NoContent();
}
PUT vs PATCH: What's the difference?
If a product has a Name and a Price:
• PUT (Replace): You must send both the Name and the Price. You are replacing the whole object.
• PATCH (Partial Update): You only send the Price. The server only updates the Price and leaves the Name alone.
DELETE does exactly what it sounds like: it permanently removes a resource from the server.
Golden Rule: DELETE is idempotent. Whether you delete an item once or try to delete it again, the end result is the same (the item is gone). If the item doesn't exist, you usually return a 404 Not Found or a 204 No Content.
[HttpDelete("{id}")]
public IActionResult DeleteProduct(int id)
{
// 1. Find the item
var product = _db.Products.Find(id);
// 2. If it doesn't exist, return 404
if (product == null)
return NotFound("Product not found.");
// 3. Remove it and save
_db.Products.Remove(product);
_db.SaveChanges();
// 4. Return 204 Success
return NoContent();
}
Key Takeaway (CRUD)
Use GET to Read, POST to Create, PUT to Update, and DELETE to Remove. Think of them as the CRUD (Create, Read, Update, Delete) operations of the web!
Build Your Own: Library Manager API
Let's put all 4 HTTP methods to the test! We will build a complete API in Visual Studio to manage a library. No database needed.
Create the Project
Open Visual Studio and create a new project.
LibraryAPI. Keep the default settings and click Create.
Create the Book Model
We need a C# class to define what a "Book" looks like.
Book.cs. Paste this inside:
namespace LibraryAPI
{
public class Book
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
}
}
Create the Controller
This is where we write all 4 HTTP methods. Notice how the URL stays the same (/api/books), but the HTTP Method changes!
BooksController.cs.
Replace the code with this complete implementation:
using Microsoft.AspNetCore.Mvc;
namespace LibraryAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
{
// Fake in-memory database
private static List<Book> _books = new List<Book>
{
new Book { Id = 1, Title = "Clean Code", Author = "Robert Martin" },
new Book { Id = 2, Title = "The Pragmatic Programmer", Author = "Andrew Hunt" }
};
// 1. GET: api/books (Read all)
[HttpGet]
public IActionResult GetAllBooks()
{
return Ok(_books);
}
// 2. POST: api/books (Create new)
[HttpPost]
public IActionResult CreateBook(Book newBook)
{
newBook.Id = _books.Count + 1;
_books.Add(newBook);
return CreatedAtAction(nameof(GetAllBooks), newBook);
}
// 3. PUT: api/books/1 (Update existing)
[HttpPut("{id}")]
public IActionResult UpdateBook(int id, Book updatedBook)
{
var book = _books.FirstOrDefault(b => b.Id == id);
if (book == null) return NotFound("Book not found.");
book.Title = updatedBook.Title;
book.Author = updatedBook.Author;
return NoContent(); // 204 Success
}
// 4. DELETE: api/books/1 (Remove)
[HttpDelete("{id}")]
public IActionResult DeleteBook(int id)
{
var book = _books.FirstOrDefault(b => b.Id == id);
if (book == null) return NotFound("Book not found.");
_books.Remove(book);
return NoContent(); // 204 Success
}
}
}
Run and Test All 4 Methods
Press F5 to run it. Swagger UI will open. Let's test the CRUD operations!
Click on GET /api/books → Try it out → Execute.
Result: You will see the JSON list of the 2 default books!
Click on POST /api/books → Try it out. Paste this JSON:
{ "title": "C# in Depth", "author": "Jon Skeet" }
Click Execute.
Result: 201 Created. The new book is added!
Click on PUT /api/books/1 → Try it out. Paste this JSON to fix the author's name:
{ "id": 1, "title": "Clean Code", "author": "Robert C. Martin" }
Click Execute.
Result: 204 No Content. The book was updated successfully!
Click on DELETE /api/books/2 → Try it out → Execute.
Result: 204 No Content. The book is deleted!
Mastered!
You just built and tested a complete CRUD API. You used all 4 HTTP methods to Read, Create, Update, and Delete data from a real API.