An HTTP operation is idempotent when repeating an identical request has the same effect on the server as sending it once. Clients may rely on that behavior, so choosing an idempotent method is not enough: the action must implement it. GET, HEAD, OPTIONS, TRACE, PUT, and DELETE are classified as idempotent methods. section 5.3.5
Choose the method by checking what a repeated request actually changes:
- Seed board-game data with PUT. The import adds database records only if they do not already exist, so running it again does not add those records again. section 5.3.5
- Use POST for the board-game update shown here. Even with identical input, each call sets LastModifiedDate again, producing a new database effect. section 5.5.1
- Use DELETE for a board game targeted by ID. Once the record is removed, repeating the request cannot remove it again. section 5.5.2
Listing 5.11 BoardGameController’s Delete method
[HttpDelete(Name = "DeleteBoardGame")]
[ResponseCache(NoStore = true)]
public async Task<RestDTO<BoardGame?>> Delete(int id)
{
var boardgame = await _context.BoardGames
.Where(b => b.Id == id)
.FirstOrDefaultAsync();
if (boardgame != null)
{
_context.BoardGames.Remove(boardgame);
await _context.SaveChangesAsync();
};
return new RestDTO<BoardGame?>()
{
Data = boardgame,
Links = new List<LinkDTO>
{
new LinkDTO(
Url.Action(
null,
"BoardGames",
id,
Request.Scheme)!,
"self",
"DELETE"),
}
};
}
The DELETE action looks up the requested ID and calls Remove and SaveChangesAsync only when a record exists. This existence check makes a repeated call leave the database in the same state. section 5.5.2
Check a repeated deletion
In SwaggerUI, call DELETE /BoardGames with ID 181, then execute it again. The first call removes the board game; the second lets you check that repeating the request causes no further deletion. Idempotency concerns the server-side effect, not whether both responses contain the same data. section 5.5.2 section 5.3.5
















