Add PATCH endpoint documentation to minimal API tutorial#36350
Add PATCH endpoint documentation to minimal API tutorial#36350
Conversation
Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
|
@copilot there is inconsistent code block language specifiers: In min-web-api8.md, the .http code blocks in the Visual Studio tab lack the http language identifier (just |
Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
Fixed in commit 7fbde84. Updated the .http code blocks in min-web-api8.md to use ```http language identifier for consistency with min-web-api.md. |
|
@copilot, there is a technical bug which undermines the tutorial's correctness for teaching PATCH semantics: The PATCH implementation has incorrect partial update logic for IsComplete: The Todo class has IsComplete as a non-nullable bool. When a client sends only {"name": "run errands"}, the deserialized inputTodo.IsComplete defaults to false, which will incorrectly overwrite true values. The comparison inputTodo.IsComplete != todo.IsComplete doesn't differentiate between "field not provided" and "field explicitly set to false." The analysis in the original issue this PR was to fix, recommended using a nullable bool? IsComplete DTO specifically to address this. Fix this problem for all versions from version 7 and above where needed. |
…ect partial update semantics Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
Fixed in commit dbf273d. Created |
ms.date update
|
The current PR has a compile-error problem for readers following the tutorial step-by-step. In the "Add the API code" section, readers are told to paste the full Solution: Separate The Apply the following changes to all three version-specific files AND their corresponding tutorial markdown files. Part 1: Modify Apply the same structural change to all three files:
What to change in each
Example structure for each #define FINAL // MINIMAL FINAL WITHPATCH TYPEDR
#if MINIMAL
// <snippet_min>
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
// </snippet_min>
#elif FINAL
// <snippet_all>
using Microsoft.EntityFrameworkCore;
// <snippet_DI>
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
// </snippet_DI>
// <snippet_get>
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
// <snippet_getCustom>
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
// </snippet_getCustom>
// </snippet_get>
// <snippet_post>
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
// </snippet_post>
// <snippet_put>
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
// </snippet_put>
// <snippet_delete>
app.MapDelete("/todoitems/{id}", async (int id, TodoDb db) =>
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NotFound();
});
// </snippet_delete>
app.Run();
// </snippet_all>
#elif WITHPATCH
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
app.MapGet("/todoitems", async (TodoDb db) =>
await db.Todos.ToListAsync());
app.MapGet("/todoitems/complete", async (TodoDb db) =>
await db.Todos.Where(t => t.IsComplete).ToListAsync());
app.MapGet("/todoitems/{id}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id)
is Todo todo
? Results.Ok(todo)
: Results.NotFound());
app.MapPost("/todoitems", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todoitems/{todo.Id}", todo);
});
app.MapPut("/todoitems/{id}", async (int id, Todo inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
todo.Name = inputTodo.Name;
todo.IsComplete = inputTodo.IsComplete;
await db.SaveChangesAsync();
return Results.NoContent();
});
// <snippet_patch>
app.MapPatch("/todoitems/{id}", async (int id, TodoPatchDto inputTodo, TodoDb db) =>
{
var todo = await db.Todos.FindAsync(id);
if (todo is null) return Results.NotFound();
if (inputTodo.Name is not null) todo.Name = inputTodo.Name;
if (inputTodo.IsComplete is not null) todo.IsComplete = inputTodo.IsComplete.Value;
await db.SaveChangesAsync();
return Results.NoContent();
});
// </snippet_patch>
app.MapDelete("/todoitems/{id}", async (int id, TodoDb db) =>
{
if (await db.Todos.FindAsync(id) is Todo todo)
{
db.Todos.Remove(todo);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NotFound();
});
app.Run();
#elif TYPEDR
// ... (keep existing TYPEDR block unchanged)
#endifIMPORTANT: Keep the existing Part 2: Update the tutorial markdown files to add a "create TodoPatchDto.cs" step Apply equivalent changes to all three tutorial markdown files:
What to change in each markdown file In the "Examine the PATCH endpoint" section, before the existing line that says: Add the following new paragraph instructing readers to create the For the 9.x file ( Create a file named `TodoPatchDto.cs` with the following code:
:::code language="csharp" source="~/tutorials/min-web-api/samples/9.x/todo/TodoPatchDto.cs":::
The `TodoPatchDto` class uses nullable properties (`string?` and `bool?`) to distinguish between a field that wasn't provided in the request versus a field explicitly set to a value.
The sample app implements a single PATCH endpoint using `MapPatch`:For the 8.x file ( Create a file named `TodoPatchDto.cs` with the following code:
:::code language="csharp" source="~/tutorials/min-web-api/samples/8.x/todo/TodoPatchDto.cs":::
The `TodoPatchDto` class uses nullable properties (`string?` and `bool?`) to distinguish between a field that wasn't provided in the request versus a field explicitly set to a value.
The sample app implements a single PATCH endpoint using `MapPatch`:For the 7.x file ( Create a file named `TodoPatchDto.cs` with the following code:
:::code language="csharp" source="~/tutorials/min-web-api/samples/7.x/todo/TodoPatchDto.cs":::
The `TodoPatchDto` class uses nullable properties (`string?` and `bool?`) to distinguish between a field that wasn't provided in the request versus a field explicitly set to a value.
The sample app implements a single PATCH endpoint using `MapPatch`:Part 3: Update the snippet reference for PATCH in the markdown files In each of the three markdown files listed in Part 2, the "Examine the PATCH endpoint" section currently references the PATCH snippet like this (example for 9.x): [!code-csharp[](~/tutorials/min-web-api/samples/9.x/todo/Program.cs?name=snippet_patch)]This reference is still correct because the Part 4: Do NOT change the The file
Summary of all files to modify
Do NOT modify any other files. Do NOT modify the |
…tion instructions - Removed snippet_patch from FINAL block in all Program.cs files (7.x, 8.x, 9.x) - Added new WITHPATCH block containing complete code with PATCH endpoint - Updated #define comment to include WITHPATCH - Added instructions to create TodoPatchDto.cs file before PATCH section in all markdown files - This ensures readers create the DTO before pasting code that references it Co-authored-by: wadepickett <10985336+wadepickett@users.noreply.github.com>
Fixed in commit b78cb43. Changes made:
The FINAL block (referenced by |
Original prompt
This section details on the original issue you should resolve
<issue_title>Add PATCH to tutorial</issue_title>
<issue_description>### Description
Verbatim:
Page URL
https://learn.microsoft.com/en-us/aspnet/core/tutorials/min-web-api?view=aspnetcore-8.0&tabs=visual-studio
Content source URL
https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/tutorials/min-web-api.md
Document ID
ab408035-6313-a183-9940-32de00e0e977
Article author
@wadepickett</issue_description>
Comments on the Issue (you are @copilot in this section)
@wadepickett AI analysis test run by wadepickett Prompt: verify-issue-set-course-of-action_v.05.md Model: GPT-4 Nov 2023 -------Issue Analysis: Add PATCH to tutorial
Issue Summary
This is a valid issue. The user reports that the minimal API tutorial for ASP.NET Core does not include an example for a PATCH operation, specifically using
MapPatchand (optionally)JsonPatchDocumentin the .NET minimal API template. PATCH support was introduced in .NET 7 but is not demonstrated in the current tutorial.Issue Context and Validation
Article evaluated: Tutorial: Create a minimal API with ASP.NET Core
Source file path:
aspnetcore/tutorials/min-web-api.mdCurrent status:
Recent related/overlapping issues:
No effective workaround or cross-reference: The article does not redirect readers to dedicated PATCH or JsonPatch documentation, so discoverability in the tutorial context is poor.
Has the issue already been addressed?
Affected File(s) and Section(s)
File to update:
aspnetcore/tutorials/min-web-api.mdSections impacted:
Recommended Documentation Update
Add PATCH endpoint to "Overview" Table:
Provide PATCH minimal API sample code:
MapPatchendpoint to the example, updating partial fields for a todo item.JsonPatchDocument<Todo>for advanced partial updates.Update step-by-step instructions:
Add a tip/note block:
[!NOTE] PATCH provides partial updates and can leverage JsonPatch for complex scenarios.Reference dedicated guidance:
Example Code or CLI Snippets (if applicable)
Basic PATCH with direct property assignment (Recommended for tutorial simplicity):