Backend Development with .NET
Session 04
HTTP Methods
& Status Codes
Eng. Seif Mansour  ·  Andalusia Academy
Week 2  ·  2 hours
Session Goals

By the end of this session, you will be able to:

  • Choose the correct HTTP method for any given operation
  • Return the correct status code for every possible outcome
  • Explain idempotency and safety — and why clients depend on them
  • Identify and fix common HTTP misuse patterns found in real-world APIs
  • Test all method and status code scenarios confidently in Postman
Session Agenda
Time Segment Type Duration
0:00HTTP methods — the full pictureTheory30 min
0:30Idempotency & safety explainedTheory15 min
0:45HTTP status codes — by familyTheory25 min
1:10Live demo — wrong codes in the wildDemo15 min
1:25Lab — fix the broken APILab25 min
1:50Wrap-upDiscussion10 min
HTTP Methods
Every request you make to an API starts with a method. Picking the right one is not style — it is a contract between your API and every client, browser, proxy, and CDN that talks to it.
In this section
  • The five methods you will use daily
  • Decision guide — how to choose
  • Common mistakes to avoid
The Five Core HTTP Methods
Method Use case Request body? Idempotent? Safe?
GET Retrieve a resource No Yes Yes
POST Create a new resource Yes No No
PUT Replace a resource entirely Yes Yes No
PATCH Partially update a resource Yes No No
DELETE Remove a resource No Yes No
Safe vs Idempotent
Safe means no side effects. Idempotent means calling it multiple times gives the same result. These are covered fully in the next section.
Which Method to Use?

Ask yourself what the operation is doing, not what it sounds like.

Reading data — nothing changes on the server GET
Creating a new resource — server assigns the ID POST
Replacing the entire object — client sends the full representation PUT
Updating only a few fields — partial change, rest stays as-is PATCH
Removing a resource permanently DELETE
Common Method Mistakes
  • The RPC trap — using POST for everything because it is easy. POST /tasks/complete/5 should be PATCH /tasks/5 with a body.
  • GET with a body — using a body on GET to pass filters is non-standard and breaks many HTTP clients and proxies. Use query parameters instead: GET /tasks?status=open.
  • Confusing PUT and PATCHPUT requires the full object. If you only send the fields you want to change, the missing fields get set to null or default. Use PATCH for partial updates.
  • Ignoring idempotency on PUT — if your PUT handler creates the resource when it does not exist, make sure it does not create a second copy on retry.
Idempotency & Safety
Networks fail. Clients retry. Whether your API handles a retry gracefully or silently creates duplicate data depends entirely on whether you chose the right method.
In this section
  • Safe operations defined
  • Idempotent operations defined
  • Why clients depend on both
  • The POST retry problem
Safe vs Idempotent — Defined
  • Safe — the operation produces no side effects on the server. Calling it once or a hundred times leaves the state unchanged. Only GET, HEAD, and OPTIONS are safe.
  • Idempotent — calling the operation multiple times with the same input produces the same result as calling it once. GET, PUT, and DELETE are idempotent.
Key distinction
Safe implies idempotent. Idempotent does not imply safe. DELETE is idempotent (second call still returns success) but it is not safe — the first call changed the server state.
Method Safe Idempotent
GET Yes Yes
POST No No
PUT No Yes
PATCH No No
DELETE No Yes
Why Clients Depend on This
  • HTTP clients (Axios, fetch, .NET HttpClient) automatically retry requests on network timeout — but only if the method is idempotent.
  • A retried POST that creates a new record each time will produce duplicate data with no error.
  • A retried PUT simply sets the same value again — harmless.
  • CDNs and load balancers also use these properties to decide whether to cache or replay requests.
POST — not idempotent Client timeout Server retry Result: 2 records created PUT — idempotent Client timeout Server retry Result: same state — safe to retry
HTTP Status Codes
The three-digit number in every response is a machine-readable signal. Browsers, monitoring tools, load balancers, and frontend frameworks all read it before your body is even parsed.
In this section
  • The four code families
  • 2xx — success codes
  • 4xx — client error codes
  • 3xx & 5xx — briefly
  • The 401 vs 403 distinction
Status Code Families
2xx
Success
The request was received, understood, and accepted. Always return one of these when things go right.
3xx
Redirection
Further action is needed. The resource has moved or the client should use a cached version.
4xx
Client Error
The request is wrong. The client sent bad data, is not authenticated, or is not allowed. The client must fix the request.
5xx
Server Error
The server failed. The request may have been valid but something broke on the server side. The client can try again later.
2xx — Success Codes
Code Name When to return it
200 OK The default success response — use for GET, PUT, and PATCH when returning a body
201 Created A POST that created a new resource. Include a Location header pointing to the new resource URL
204 No Content Success with no response body — use for DELETE, or PATCH when nothing is returned
In ASP.NET Core
Return Ok(resource) for 200, Created(location, resource) for 201, NoContent() for 204.
4xx — Client Error Codes
Code Name When to return it
400 Bad Request Malformed request body, missing required fields, failed model validation
401 Unauthorized No token provided, or the token is invalid / expired
403 Forbidden Valid token, but the user does not have permission for this action
404 Not Found The requested resource does not exist — wrong ID, deleted record
409 Conflict State conflict — duplicate email on registration, concurrent edit collision
422 Unprocessable Entity Request is well-formed but fails a business rule (e.g. closing a task that is already closed)
429 Too Many Requests Rate limit exceeded — covered in Session 16
3xx & 5xx — Briefly

3xx — Redirection

CodeNameWhen to use
301 Moved Permanently A URL has changed permanently — tell clients to update their bookmarks
304 Not Modified Response has not changed since the client's cached version (ETag / conditional requests)

5xx — Server Error

CodeNameWhen to use
500 Internal Server Error Unhandled exception — let the middleware produce this; never return it manually
503 Service Unavailable Overloaded or down — typically produced by the load balancer, not your code
Key Distinction — Memorise This
401 = "I don't know who you are."
403 = "I know exactly who you are — and the answer is no."
401 means no token or an invalid token. 403 means the token is valid but the user lacks the required role or permission. Returning 401 when you mean 403 tells the client to re-authenticate — which will not help, because the token is fine.
Demo & Lab
Theory lands differently when you see broken code in the wild. First we look at real examples of wrong status codes, then you fix a pre-written controller that has five deliberate errors.
In this section
  • Live demo — wrong codes in real APIs
  • Why wrong codes break clients
  • Lab — five bugs to find and fix
Wrong Codes in the Wild

Real APIs get this wrong more often than you would expect. Here is what to look for in Postman:

  • 200 with an error body — response is 200 OK but the JSON reads {"error": "user not found"}. Frontend error handlers never fire; the error is silently ignored.
  • 200 on failed login{"success": false, "message": "wrong password"} with status 200. Monitoring tools report 100% uptime even during an outage.
  • 200 on a create — a POST that creates a resource returns 200 instead of 201. The client has no Location header to follow; the UI must do an extra GET to find the new resource's ID.
Why this matters
Monitoring, alerting, load balancers, browser caching, and every HTTP client library all key off the status code — not the body. A wrong code silently breaks all of them.
Lab — Fix the Broken API

Find and fix these five bugs in the provided controller:

Bug 1 A GET endpoint that modifies data on the server
Bug 2 A POST returning 200 instead of 201
Bug 3 A DELETE returning 200 with a body instead of 204
Bug 4 A GET by ID that returns null instead of 404
Bug 5 A validation failure returning 500 manually — should be 400
Controllers/TasksController.cs (broken)
// Bug 1: GET that modifies data
[HttpGet("complete/{id}")]
public IActionResult MarkComplete(int id)
{
    var task = _tasks.First(t => t.Id == id);
    task.IsComplete = true;          // side effect!
    return Ok(task);
}

// Bug 2: POST returning 200
[HttpPost]
public IActionResult Create(TaskItem task)
{
    _tasks.Add(task);
    return Ok(task);                 // should be Created(...)
}

// Bug 3: DELETE returning body
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
    var task = _tasks.First(t => t.Id == id);
    _tasks.Remove(task);
    return Ok(task);                 // should be NoContent()
}

// Bug 4: Missing 404
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
    var task = _tasks.FirstOrDefault(t => t.Id == id);
    return Ok(task);                 // null if not found!
}

// Bug 5: Manual 500 for validation
[HttpPut("{id}")]
public IActionResult Update(int id, TaskItem task)
{
    if (string.IsNullOrEmpty(task.Title))
        return StatusCode(500, "Title required"); // should be 400
    return Ok(task);
}
Summary
  • GET never modifies data — if your operation has a side effect, use a different method
  • POST returns 201 with a Location header pointing to the created resource
  • DELETE returns 204 — no body, no exception for an already-deleted resource on a second call
  • 401 = who are you? / 403 = I know you, but no — never confuse them
  • Never return 200 with an error in the body — the status code is the signal; the body is the detail
  • Idempotency is a client contract — HTTP clients use it to decide whether to retry automatically
What's Next

Session 05 — Error Handling & Clean Error Responses

  • Now that you know which status code to return, Session 05 covers how to return a consistent, structured error body alongside it
  • We will build a global exception handler using ASP.NET Core middleware so your controllers never need a try-catch block
  • You will implement the RFC 7807 Problem Details standard — the same format used by .NET's built-in ValidationProblemDetails
  • After Session 05 you will be able to combine what you learned today (correct codes) with a clean, predictable error response body that frontend developers can rely on
Before next session
Complete today's assignment. When you return 404, what should the response body look like? Think about it — Session 05 answers that question.
Assignment

Build a ProductsController with in-memory storage and full HTTP method + status code compliance.

  • GET /api/products returns 200 with the full list
  • GET /api/products/{id} returns 200 with the product or 404 if not found
  • POST /api/products creates the product and returns 201 with the new resource in the body
  • PUT /api/products/{id} replaces the full product and returns 200, or 404 if it does not exist
  • DELETE /api/products/{id} removes the product and returns 204 with no body

Acceptance criteria:

  • No endpoint ever returns 200 when an error occurred
  • The POST response body includes the newly created product with its assigned ID
  • Test all endpoints in Postman and verify the status code on each response
Bonus
Add a PATCH endpoint that updates only the Name field. Return 400 if Name is empty or missing from the request body.
Questions?
Session 04 — HTTP Methods & Status Codes