PUT
PUT replaces an item with the one in the request body, its id taken from the address. The DAO updates the row and hands back the category as it was saved, and the action answers 200 OK with it, or 404 Not Found when there's no such item.
Journeyman · ASP.NET Core · REST API · Updated
After this lesson you can
- Write a PUT action that updates an item and answers 200 OK with the updated item, or 404 Not Found when it isn't there
Key takeaways
- A
PUTto an item's address replaces it: the id comes from the address, everything else from the body. OUTPUT INSERTEDhands back the row as theUPDATEsaved it, and Dapper'sQuerySingleOrDefaultreads it, or returnsnullwhen no row matched.- Return
Ok(updated)for200 OKwith the updated item, orNotFound()for404when no row was updated. - Like a
POST, aPUTanswers with the saved item, so the client sees exactly what the database now holds. PUTsends the whole item: leave the description out of the body, and it becomesnull.
A client updates an item with a PUT to that item's address, sending the whole changed item as JSON in the body. The id comes from the address; everything else comes from the body. The server saves it and answers with the item as it's now stored, just as a POST does.
The DAO: update, and hand back what was saved
In POST, OUTPUT INSERTED.CategoryId handed back the new id from the INSERT. An UPDATE can use OUTPUT too: here it hands back the whole row, as it looks after the change. Dapper's QuerySingleOrDefault reads that row into a Category:
public Category? Update(int categoryId, Category category)
{
string sql = """
UPDATE Categories
SET CategoryName = @CategoryName, Description = @Description
OUTPUT INSERTED.CategoryId, INSERTED.CategoryName, INSERTED.Description
WHERE CategoryId = @CategoryId;
""";
using SqlConnection connection = new SqlConnection(connectionString);
return connection.QuerySingleOrDefault<Category>(sql, new { CategoryId = categoryId, category.CategoryName, category.Description });
}
OUTPUT INSERTED.…lists the columns to hand back. In anUPDATE,INSERTEDmeans the row after the change.- An update of a category that doesn't exist changes no rows, so
OUTPUThands back nothing, andQuerySingleOrDefaultreturnsnull: the samenullyou met inGetById. - The anonymous object takes the id from the parameter, not from the body: the address is the one that says which category to change.
The action
[HttpPut("{id}")]
public ActionResult<Category> Update(int id, Category category)
{
Category? updated = categoryDao.Update(id, category);
if (updated is null)
{
return NotFound();
}
return Ok(updated);
}
[HttpPut("{id}")]answersPUT /api/categories/9.int idcomes from the address,Category categoryfrom the body.ActionResult<Category>, as in POST: either aCategory, or a status code result.Ok(updated)answers200 OKwith the updated category in the body: "done, and here's what it looks like now."NotFound()answers404when no row was updated.
PUT {{HostAddress}}/api/categories/9
Content-Type: application/json
{
"categoryName": "Snacks",
"description": "Chips, nuts, pretzels and popcorn"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"categoryId": 9,
"categoryName": "Snacks",
"description": "Chips, nuts, pretzels and popcorn"
}
The same request to /api/categories/300 answers 404 Not Found. And because the body is a Category, the validation rules apply here too: a PUT with no categoryName gets the same automatic 400 as a POST.
Why send the item back?
The client just sent the category, so why answer with it? Because what the database saved can differ from what was sent: the body above has no categoryId (the address gave it), and in a real app a table often has columns the client never sends, such as a last-changed date the database sets itself. Answering with the saved item means the client shows exactly what's stored, without a second GET. That's why POST and PUT both answer with the item.
(Older APIs, and older tutorials, often answer a PUT with 204 No Content and an empty body. It still works, but answering 200 with the item is the common choice today.)
PUT replaces the whole item
A PUT sends the whole item, not just what changed. Send only a categoryName, and the description becomes null: the update sets every column from the body. That's what replace means, and the answer shows it: its description is null too.
(There's also a PATCH method for changing just some fields, but it's less common and needs more code. Most APIs use PUT.)
Check yourself
1 question to try on the lesson page.
So what
PUT is update-and-show: the address says which item, the body says what it should be now, and the answer is 200 with the item as it's saved, or 404 if there was nothing to update.