Introduction
Most explanations of HTTP verbs in ASP.NET Core Web API follow a simple mapping:
GETreads dataPOSTcreates dataPUTupdates dataDELETEremoves data
This mapping is a useful starting point, but production APIs often encounter operations that do not fit neatly into a CRUD table.
Consider an HR or Payroll system that manages employee leave requests. When an employee clicks Withdraw Leave, the application may not actually delete the database record. Instead, it may change its status, record who performed the action, store the withdrawal timestamp, and preserve the original request for auditing.
In that situation, calling the operation "delete" from a user-interface perspective does not necessarily mean that the HTTP operation should be DELETE.
The important question is:
What does the API operation actually do to the resource?
The Standard HTTP Verb Mapping
A conventional ASP.NET Core controller might look like this:
[ApiController]
[Route("api/[controller]")]
public class LeaveRequestController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetLeaveRequest(int id)
{
return Ok(employeeDetails);
}
[HttpPost]
public IActionResult CreateLeaveRequest(
[FromBody] LeaveRequestDto request)
{
return Ok(result);
}
[HttpPut("{id}")]
public IActionResult UpdateLeaveRequest(
int id,
[FromBody] LeaveRequestDto request)
{
return Ok(result);
}
[HttpDelete("{id}")]
public IActionResult DeleteLeaveRequest(int id)
{
return Ok(result);
}
}
This follows the familiar CRUD model:
GET -> Read
POST -> Create
PUT -> Replace/Update
DELETE -> Remove
These conventions are useful because clients, developers, API documentation tools, and infrastructure can understand the intended semantics more easily.
However, not every business operation is a simple CRUD operation.
When "Delete" Does Not Mean Physical Deletion
Suppose an employee submits this leave request:
LeaveRequestId: 10231
EmployeeId: EMP1023
FromDate: 2026-09-20
ToDate: 2026-09-22
Status: Pending
The employee later decides to withdraw the request.
A simple implementation might physically remove the record:
DELETE FROM LeaveRequests
WHERE LeaveRequestId = 10231;
But that can be problematic in systems where the request forms part of an audit trail.
Instead, the application might retain the record:
LeaveRequestId: 10231
EmployeeId: EMP1023
FromDate: 2026-09-20
ToDate: 2026-09-22
Status: Withdrawn
The database operation is now an update rather than a deletion.
This distinction matters.
The UI may describe the operation as Delete Leave Request, while the API is actually performing a withdrawal or status transition.
Why Soft Deletes Are Common in Enterprise Systems
Keeping the original record can be useful for several reasons.
Auditability
Organizations may need to know what happened to a request and when.
For example:
Request Created
|
v
Manager Approved
|
v
Employee Withdrawn
|
v
Payroll Processed
Removing the database record makes reconstructing that history more difficult.
Historical Records
HR, attendance, expense, and payroll systems often depend on historical information.
A request that was withdrawn may still be relevant to understanding what happened during a particular payroll period.
Dispute Resolution
If an employee later questions a leave transaction, retaining the original request and its status history can help reconstruct the sequence of events.
For these reasons, many enterprise systems implement soft deletion or, more precisely, a business-state transition rather than physical deletion.
Model the Business State Explicitly
A simple model might be:
public enum LeaveRequestStatus
{
Pending = 0,
Approved = 1,
Withdrawn = 2,
Rejected = 3
}
public class LeaveRequest
{
public int LeaveRequestId { get; set; }
public string EmpId { get; set; } = string.Empty;
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
public LeaveRequestStatus Status { get; private set; }
public void Withdraw()
{
Status = LeaveRequestStatus.Withdrawn;
}
}
Now the application is not pretending that the resource disappeared.
It is explicitly changing its business state.
That leads to an important API-design question:
Should a state transition be represented as
PUT,PATCH, orPOST?
PUT vs. PATCH for State Changes
PUT is generally used when the client provides a complete representation of the resource that should replace the current representation.
For example:
PUT /api/leave-requests/10231
The request might contain the complete leave request representation.
A partial status change is conceptually different:
PATCH /api/leave-requests/10231
For example:
{
"status": "Withdrawn"
}
PATCH is therefore often a natural choice when the operation means:
Change part of the existing resource.
The corresponding ASP.NET Core endpoint could be:
[HttpPatch("{id}/status")]
public IActionResult UpdateStatus(
int id,
[FromBody] UpdateLeaveStatusRequest request)
{
// Validate transition
// Update status
// Record audit information
return Ok();
}
This makes the HTTP semantics clearer than calling a status change a DELETE.
When POST Makes Sense for a "Delete" Button
There are also cases where POST is appropriate.
Suppose the business operation is not simply changing a property but represents a domain action:
Join the conversation! Your thoughts help the community grow.