diff --git a/aspnetcore/includes/duende-identity-server-10.md b/aspnetcore/includes/duende-identity-server-10.md
new file mode 100644
index 000000000000..2fcf757cf6bc
--- /dev/null
+++ b/aspnetcore/includes/duende-identity-server-10.md
@@ -0,0 +1,16 @@
+ASP.NET Core Identity adds user interface (UI) login functionality to ASP.NET Core web apps. To secure web APIs and SPAs, use one of the following:
+
+* [Microsoft Entra ID](/azure/api-management/api-management-howto-protect-backend-with-aad)
+* [Duende Identity Server](https://docs.duendesoftware.com)
+
+Duende Identity Server is an OpenID Connect and OAuth 2.0 framework for ASP.NET Core. Duende Identity Server enables the following security features:
+
+* Authentication as a Service (AaaS)
+* Single sign-on/off (SSO) over multiple application types
+* Access control for APIs
+* Federation Gateway
+
+> [!IMPORTANT]
+> [Duende Software](https://duendesoftware.com/) might require you to pay a license fee for production use of Duende Identity Server.
+
+For more information, see the [Duende Identity Server documentation (Duende Software website)](https://docs.duendesoftware.com).
diff --git a/aspnetcore/mvc/views/tag-helpers/built-in/anchor-tag-helper.md b/aspnetcore/mvc/views/tag-helpers/built-in/anchor-tag-helper.md
index ad9c9293f962..5989ac979e44 100644
--- a/aspnetcore/mvc/views/tag-helpers/built-in/anchor-tag-helper.md
+++ b/aspnetcore/mvc/views/tag-helpers/built-in/anchor-tag-helper.md
@@ -1,12 +1,15 @@
---
title: Anchor Tag Helper in ASP.NET Core
+ai-usage: ai-assisted
author: pkellner
description: Discover the ASP.NET Core Anchor Tag Helper attributes and the role each attribute plays in extending behavior of the HTML anchor tag.
+monikerRange: '>= aspnetcore-6.0'
ms.author: wpickett
ms.custom: mvc
-ms.date: 10/13/2019
+ms.date: 05/08/2026
uid: mvc/views/tag-helpers/builtin-th/anchor-tag-helper
---
+
# Anchor Tag Helper in ASP.NET Core
By [Peter Kellner](https://peterkellner.net) and [Scott Addie](https://github.com/scottaddie)
@@ -65,9 +68,14 @@ Consider the following controller action:
[!code-csharp[](samples/TagHelpersBuiltIn/Controllers/SpeakerController.cs?name=snippet_SpeakerDetailAction)]
-With a default route template defined in *Startup.Configure*:
-[!code-csharp[](samples/TagHelpersBuiltIn/Startup.cs?name=snippet_UseMvc&highlight=8-10)]
+With a default route template defined in `Program.cs`:
+
+```csharp
+app.MapControllerRoute(
+ name: "default",
+ pattern: "{controller=Home}/{action=Index}/{id?}");
+```
The MVC view uses the model, provided by the action, as follows:
@@ -193,13 +201,9 @@ The generated HTML:
View Sessions
```
+
> [!TIP]
-> To support areas in a Razor Pages app, do one of the following in `Startup.ConfigureServices`:
->
-> * Set the [compatibility version](xref:mvc/compatibility-version) to 2.1 or later.
-> * Set the property to `true`:
->
-> [!code-csharp[](samples/TagHelpersBuiltIn/Startup.cs?name=snippet_AllowAreas)]
+> To support areas in a Razor Pages app, ensure that area routing is enabled in your app's routing configuration. Use the minimal hosting model and configure area routes in *Program.cs* as needed.
#### Usage in MVC
@@ -228,10 +232,15 @@ The generated HTML:
About Blog
```
+
> [!TIP]
-> To support areas in an MVC app, the route template must include a reference to the area, if it exists. That template is represented by the second parameter of the `routes.MapRoute` method call in *Startup.Configure*:
+> To support areas in an MVC app, the route template must include a reference to the area, if it exists. Configure area routes in *Program.cs* using `MapControllerRoute` with an area pattern, for example:
>
-> [!code-csharp[](samples/TagHelpersBuiltIn/Startup.cs?name=snippet_UseMvc&highlight=5)]
+> ```csharp
+> app.MapControllerRoute(
+> name: "mvcAreaRoute",
+> pattern: "{area:exists}/{controller=Home}/{action=Index}");
+> ```
### asp-protocol
diff --git a/aspnetcore/tutorials/first-mongo-app.md b/aspnetcore/tutorials/first-mongo-app.md
index d83269c12ca1..1631335cb69f 100644
--- a/aspnetcore/tutorials/first-mongo-app.md
+++ b/aspnetcore/tutorials/first-mongo-app.md
@@ -6,16 +6,16 @@ description: This tutorial demonstrates how to create an ASP.NET Core web API us
monikerRange: '>= aspnetcore-3.1'
ms.author: wpickett
ms.custom: mvc, sfi-ropc-nochange
-ms.date: 09/17/2025
+ms.date: 05/06/2026
uid: tutorials/first-mongo-app
---
# Create a web API with ASP.NET Core and MongoDB
[!INCLUDE[](~/includes/not-latest-version.md)]
-By [Pratik Khandelwal](https://twitter.com/K2Prk) and [Scott Addie](https://twitter.com/Scott_Addie)
+By [Tim Deschryver](https://timdeschryver.dev)
-:::moniker range=">= aspnetcore-9.0"
+:::moniker range=">= aspnetcore-10.0"
This tutorial creates a web API that runs Create, Read, Update, and Delete (CRUD) operations on a [MongoDB](https://www.mongodb.com/what-is-mongodb) NoSQL database.
@@ -30,16 +30,16 @@ In this tutorial, you learn how to:
## Prerequisites
-* [MongoDB 6.0.5 or later](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/)
+* [MongoDB 8.0.0 or later](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/)
* [MongoDB Shell](https://www.mongodb.com/docs/mongodb-shell/install/)
# [Visual Studio](#tab/visual-studio)
-[!INCLUDE[](~/includes/net-prereqs-vs-9.0.md)]
+[!INCLUDE[](~/includes/net-prereqs-vs-10.md)]
# [Visual Studio Code](#tab/visual-studio-code)
-[!INCLUDE[](~/includes/net-prereqs-vsc-9.0.md)]
+[!INCLUDE[](~/includes/net-prereqs-vsc-10.0.md)]
---
@@ -50,17 +50,17 @@ Enable MongoDB and MongoDB Shell access from anywhere on the development machine
1. Download and Install MongoDB Shell:
* macOS/Linux: Choose a directory to extract the MongoDB Shell to. Add the resulting path for `mongosh` to the `PATH` environment variable.
- * Windows: MongoDB Shell (mongosh.exe) is installed at *C:\\Users\\\\\AppData\\Local\\Programs\\mongosh*. Add the resulting path for `mongosh.exe` to the `PATH` environment variable.
+ * Windows: MongoDB Shell (mongosh.exe) is installed at *C:\\Users\\{USER NAME}\\AppData\\Local\\Programs\\mongosh*. Add the resulting path for `mongosh.exe` to the `PATH` environment variable.
1. Download and Install MongoDB:
* macOS/Linux: Verify the directory that MongoDB was installed at, usually in */usr/local/mongodb*. Add the resulting path for `mongodb` to the `PATH` environment variable.
- * Windows: MongoDB is installed at *C:\\Program Files\\MongoDB* by default. Add *C:\\Program Files\\MongoDB\\Server\\\\\bin* to the `PATH` environment variable.
+ * Windows: MongoDB is installed at *C:\\Program Files\\MongoDB* by default. Add *C:\\Program Files\\MongoDB\\Server\\{VERSION NUMBER}\\bin* to the `PATH` environment variable.
1. Choose a Data Storage Directory: Select a directory on your development machine for storing data. Create the directory if it doesn't exist. The MongoDB Shell doesn't create new directories:
* macOS/Linux: For example, `/usr/local/var/mongodb`.
* Windows: For example, `C:\\BooksData`.
-1. In the OS command shell (not the MongoDB Shell), use the following command to connect to MongoDB on default port 27017. Replace `` with the directory chosen in the previous step.
+1. In the OS command shell (not the MongoDB Shell), use the following command to connect to MongoDB on default port 27017. Replace `{DATA DIRECTORY PATH}` with the directory chosen in the previous step.
```console
- mongod --dbpath
+ mongod --dbpath {DATA DIRECTORY PATH}
```
Use the previously installed MongoDB Shell in the following steps to create a database, make collections, and store documents. For more information on MongoDB Shell commands, see [`mongosh`](https://docs.mongodb.com/mongodb-shell/run-commands/).
@@ -147,8 +147,8 @@ Use the previously installed MongoDB Shell in the following steps to create a da
1. Select the **ASP.NET Core Web API** project type, and select **Next**.
1. Name the project *BookStoreApi*, and select **Next**.
1. In the **Additional information** dialog:
- * Confirm the **Framework** is **.NET 9.0 (Standard Term Support)**.
- * Confirm the checkbox for **Use controllers** is checked.
+ * Confirm the **Framework** is **.NET 10.0 (Long Term Support)**.
+ * Confirm the checkbox for **Use controllers** is unchecked.
* Confirm the checkbox for **Enable OpenAPI support** is checked.
* Select **Create**.
1. In the **Package Manager Console** window, navigate to the project root. Run the following command to install the .NET driver for MongoDB:
@@ -162,13 +162,12 @@ Use the previously installed MongoDB Shell in the following steps to create a da
1. Run the following commands in a command shell:
```dotnetcli
- dotnet new webapi -o BookStoreApi --use-controllers
+ dotnet new webapi -o BookStoreApi
code BookStoreApi
```
The preceding commands generate a new ASP.NET Core web API project and then open the project in Visual Studio Code.
-1. Once the OmniSharp server starts up, a dialog asks **Required assets to build and debug are missing from 'BookStoreApi'. Add them?**. Select **Yes**.
1. Open the **Integrated Terminal** and run the following command to install the .NET driver for MongoDB:
```dotnetcli
@@ -182,7 +181,7 @@ Use the previously installed MongoDB Shell in the following steps to create a da
1. Add a *Models* directory to the project root.
1. Add a `Book` class to the *Models* directory with the following code:
- :::code language="csharp" source="first-mongo-app/samples_snapshot/9.x/Book.cs":::
+ :::code language="csharp" source="first-mongo-app/samples_snapshot/10.x/Book.cs":::
In the preceding class, the `Id` property is:
@@ -196,48 +195,48 @@ Use the previously installed MongoDB Shell in the following steps to create a da
1. Add the following database configuration values to `appsettings.json`:
- :::code language="json" source="first-mongo-app/samples/9.x/BookStoreApi/appsettings.json" highlight="2-6":::
+ :::code language="json" source="first-mongo-app/samples/10.x/BookStoreApi/appsettings.json" highlight="2-6":::
1. Add a `BookStoreDatabaseSettings` class to the *Models* directory with the following code:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs":::
The preceding `BookStoreDatabaseSettings` class is used to store the `appsettings.json` file's `BookStoreDatabase` property values. The JSON and C# property names are named identically to ease the mapping process.
1. Add the following highlighted code to `Program.cs`:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_BookStoreDatabaseSettings" highlight="4-5":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Program.cs" id="snippet_BookStoreDatabaseSettings" highlight="4-5":::
In the preceding code, the configuration instance to which the `appsettings.json` file's `BookStoreDatabase` section binds is registered in the Dependency Injection (DI) container. For example, the `BookStoreDatabaseSettings` object's `ConnectionString` property is populated with the `BookStoreDatabase:ConnectionString` property in `appsettings.json`.
1. Add the following code to the top of `Program.cs` to resolve the `BookStoreDatabaseSettings` reference:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_UsingModels":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Program.cs" id="snippet_UsingModels":::
## Add a CRUD operations service
1. Add a *Services* directory to the project root.
1. Add a `BooksService` class to the *Services* directory with the following code:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Services/BooksService.cs" id="snippet_File":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Services/BooksService.cs" id="snippet_File":::
In the preceding code, a `BookStoreDatabaseSettings` instance is retrieved from DI via constructor injection. This technique provides access to the `appsettings.json` configuration values that were added in the [Add a configuration model](#add-a-configuration-model) section.
1. Add the following highlighted code to `Program.cs`:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_BooksService" highlight="7":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Program.cs" id="snippet_BooksService" highlight="7":::
In the preceding code, the `BooksService` class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because `BooksService` takes a direct dependency on `MongoClient`. Per the official [Mongo Client reuse guidelines](https://mongodb.github.io/mongo-csharp-driver/2.14/reference/driver/connecting/#re-use), `MongoClient` should be registered in DI with a singleton service lifetime.
1. Add the following code to the top of `Program.cs` to resolve the `BooksService` reference:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_UsingServices":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Program.cs" id="snippet_UsingServices":::
The `BooksService` class uses the following `MongoDB.Driver` members to run CRUD operations against the database:
* [MongoClient](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_MongoClient.htm): Reads the server instance for running database operations. The constructor of this class is provided in the MongoDB connection string:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Services/BooksService.cs" id="snippet_ctor" highlight="4-5":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Services/BooksService.cs" id="snippet_ctor" highlight="3-5":::
* [IMongoDatabase](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_IMongoDatabase.htm): Represents the Mongo database for running operations. This tutorial uses the generic [GetCollection\(collection)](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoDatabase_GetCollection__1.htm) method on the interface to gain access to data in a specific collection. Run CRUD operations against the collection after this method is called. In the `GetCollection(collection)` method call:
@@ -251,17 +250,18 @@ The `BooksService` class uses the following `MongoDB.Driver` members to run CRUD
* [InsertOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_InsertOneAsync_1.htm): Inserts the provided object as a new document in the collection.
* [ReplaceOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_ReplaceOneAsync.htm): Replaces the single document matching the provided search criteria with the provided object.
-## Add a controller
+## Create endpoints
-Add a `BooksController` class to the *Controllers* directory with the following code:
+In the `Program.cs` file, replace the weatherforecast example with the following books endpoints:
-:::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Controllers/BooksController.cs":::
+:::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Program.cs" id="snippet_MapEndpoints":::
-The preceding web API controller:
+The preceding code:
+* Uses to help organize all the books related endpoints, and provides a common route prefix of `/books` for all the endpoints within the group.
+* Contains endpoints to support GET, POST, PUT, and DELETE HTTP requests.
* Uses the `BooksService` class to run CRUD operations.
-* Contains action methods to support GET, POST, PUT, and DELETE HTTP requests.
-* Calls in the `Create` action method to return an [HTTP 201](https://www.rfc-editor.org/rfc/rfc9110#status.201) response. Status code 201 is the standard response for an HTTP POST method that creates a new resource on the server. `CreatedAtAction` also adds a `Location` header to the response. The `Location` header specifies the URI of the newly created book.
+* Calls in the `Create` action method to return an [HTTP 201](https://www.rfc-editor.org/rfc/rfc9110#status.201) response. Status code 201 is the standard response for an HTTP POST method that creates a new resource on the server. `Created` also adds a `Location` header to the response. The `Location` header specifies the URI of the newly created book.
## Configure JSON serialization options
@@ -272,21 +272,21 @@ There are two details to change about the JSON responses returned in the [Test t
To satisfy the preceding requirements, make the following changes:
-1. In `Program.cs`, chain the following highlighted code on to the `AddControllers` method call:
+1. In `Program.cs`, add the following highlighted code:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_AddControllers" highlight="10-11":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Program.cs" id="snippet_JsonOptions" highlight="9-13":::
With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the `Book` class's `Author` property serializes as `Author` instead of `author`.
1. In `Models/Book.cs`, annotate the `BookName` property with the [`[JsonPropertyName]`](xref:System.Text.Json.Serialization.JsonPropertyNameAttribute) attribute:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Models/Book.cs" id="snippet_BookName" highlight="2":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Models/Book.cs" id="snippet_BookName" highlight="2":::
The `[JsonPropertyName]` attribute's value of `Name` represents the property name in the web API's serialized JSON response.
-1. Add the following code to the top of `Models/Book.cs` to resolve the `[JsonProperty]` attribute reference:
+1. Add the following code to the top of `Models/Book.cs` to resolve the `[JsonPropertyName]` attribute reference:
- :::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Models/Book.cs" id="snippet_UsingSystemTextJsonSerialization":::
+ :::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Models/Book.cs" id="snippet_UsingSystemTextJsonSerialization":::
1. Repeat the steps defined in the [Test the web API](#test-the-web-api) section. Notice the difference in JSON property names.
@@ -298,15 +298,15 @@ This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use
1. Build and run the app.
-1. In **Endpoints Explorer**, right-click the first **GET** endpoint `/api/books`, and select **Generate request**.
+1. In **Endpoints Explorer**, right-click the first **GET** endpoint `/books`, and select **Generate request**.
The following content is added to the `BookStoreApi.http` file.
If this is the first time that a request is generated, the file is created in the project root.
```
- @BookStoreApi_HostAddress = https://localhost:
+ @BookStoreApi_HostAddress = https://localhost:{PORT}
- GET {{BookStoreApi_HostAddress}}/api/books
+ GET {{BookStoreApi_HostAddress}}/books
###
```
@@ -339,13 +339,13 @@ This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use
]
```
-1. To retrieve a single book, right-click the `/api/books/{id}, params (string id)` **GET** endpoint in the **Endpoints Explorer**, and select **Generate request**.
+1. To retrieve a single book, right-click the `/books/{id}, params (string id)` **GET** endpoint in the **Endpoints Explorer**, and select **Generate request**.
The following content is appended to the `BookStoreApi.http` file:
```
@id=string
- GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
+ GET {{BookStoreApi_HostAddress}}/books/{{id}}
###
```
@@ -354,7 +354,7 @@ This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use
```
@id="61a6058e6c43f32854e51f52"
- GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
+ GET {{BookStoreApi_HostAddress}}/books/{{id}}
###
```
@@ -375,12 +375,12 @@ This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use
}
```
-1. To test the POST endpoint, right-click the `/api/books` **POST** endpoint and select **Generate request**.
+1. To test the POST endpoint, right-click the `/books` **POST** endpoint and select **Generate request**.
The following content is added to the `BookStoreApi.http` file:
```
- POST {{BookStoreApi_HostAddress}}/api/books
+ POST {{BookStoreApi_HostAddress}}/books
Content-Type: application/json
{
@@ -393,7 +393,7 @@ This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use
1. Replace the Book comment with a book object as the JSON request body:
```
- POST {{BookStoreApi_HostAddress}}/api/books
+ POST {{BookStoreApi_HostAddress}}/books
Content-Type: application/json
{
@@ -410,12 +410,12 @@ This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use
The POST request is sent to the app, and the response is displayed in the **Response** pane. The response should include the newly created book with its assigned ID.
-1. Lastly, to delete a book, right-click the `/api/books/{id}, params (string id)` **DELETE** endpoint and select **Generate request**.
+1. Lastly, to delete a book, right-click the `/books/{id}, params (string id)` **DELETE** endpoint and select **Generate request**.
The following content is appended to the `BookStoreApi.http` file:
```
- DELETE {{BookStoreApi_HostAddress}}/api/Books/{{id}}
+ DELETE {{BookStoreApi_HostAddress}}/books/{{id}}
###
```
@@ -423,7 +423,7 @@ This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use
1. Replace the `id` variable with one of the IDs returned from the earlier request, and click **Send request**. For example:
```
- DELETE {{BookStoreApi_HostAddress}}/api/Books/67f417517ce1b36aeab71236
+ DELETE {{BookStoreApi_HostAddress}}/books/67f417517ce1b36aeab71236
###
```
@@ -445,20 +445,20 @@ Because our project is using OpenAPI, we only use the NSwag package to generate
In `Program.cs`, add the following highlighted code:
-:::code language="csharp" source="first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_UseSwagger" highlight="6-9":::
+:::code language="csharp" source="first-mongo-app/samples/10.x/BookStoreApi/Program.cs" id="snippet_UseSwagger" highlight="6-9":::
The previous code enables the Swagger middleware for serving the generated JSON document using the Swagger UI. Swagger is only enabled in a development environment. Enabling Swagger in a production environment could expose potentially sensitive details about the API's structure and implementation.
The app uses the OpenAPI document generated by OpenApi, located at `/openapi/v1.json`, to generate the UI.
-View the generated OpenAPI specification for the `WeatherForecast` API while the project is running by navigating to `https://localhost:/openapi/v1.json` in your browser.
+View the generated OpenAPI specification for the `BookStoreApi` API while the project is running by navigating to `https://localhost:{PORT}/openapi/v1.json` in your browser.
The OpenAPI specification is a document in JSON format that describes the structure and capabilities of your API, including endpoints, request/response formats, parameters, and more. It's essentially a blueprint of your API that can be used by various tools to understand and interact with your API.
1. Build and run the app.
-1. Navigate to `https://localhost:/swagger` in your browser. Swagger provides a UI to test all the API endpoints based on the OpenAPI document.
+1. Navigate to `https://localhost:{PORT}/swagger` in your browser. Swagger provides a UI to test all the API endpoints based on the OpenAPI document.
-1. Expand the **GET /api/books** endpoint and click the **Try it out** button.
+1. Expand the **GET /books** endpoint and click the **Try it out** button.
1. Click the **Execute** button to send the request to the API.
@@ -483,7 +483,7 @@ The OpenAPI specification is a document in JSON format that describes the struct
]
```
-1. Next, expand the **GET /api/books/{id}** endpoint and click **Try it out**.
+1. Next, expand the **GET /books/{id}** endpoint and click **Try it out**.
1. Enter one of the book IDs from the previous response in the **id** field, then click **Execute**.
@@ -499,7 +499,7 @@ The OpenAPI specification is a document in JSON format that describes the struct
}
```
-1. To test creating a new book, expand the **POST /api/books** endpoint and click **Try it out**.
+1. To test creating a new book, expand the **POST /books** endpoint and click **Try it out**.
1. Replace the default request body with a new book object:
@@ -516,24 +516,26 @@ The OpenAPI specification is a document in JSON format that describes the struct
1. The response should have a status code of 201 (Created) and include the newly created book with its assigned ID in the response body.
-1. Lastly, to delete a book record, expand the **DELETE /api/books/{id}** endpoint, click **Try it out**, and enter one of the book IDs from the previous response in the **id** field. Click **Execute** to send the request.
+1. Lastly, to delete a book record, expand the **DELETE /books/{id}** endpoint, click **Try it out**, and enter one of the book IDs from the previous response in the **id** field. Click **Execute** to send the request.
1. The response should have a status code of 204 (No Content), indicating that the book was successfully deleted.
---
## Add authentication support to a web API
-[!INCLUDE[](~/includes/DuendeIdentityServer.md)]
+[!INCLUDE[](~/includes/duende-identity-server-10.md)]
## Additional resources
-* [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/tutorials/first-mongo-app/samples/9.x/BookStoreApi) ([how to download](xref:fundamentals/index#how-to-download-a-sample))
+* [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi) ([how to download](xref:fundamentals/index#how-to-download-a-sample))
*
*
-* [Create a web API with ASP.NET Core](/training/modules/build-web-api-aspnet-core/)
+* [Create a minimal web API with ASP.NET Core](/training/modules/interact-api/)
:::moniker-end
+[!INCLUDE[](~/tutorials/first-mongo-app/includes/first-mongo-app-9.md)]
+
[!INCLUDE[](~/tutorials/first-mongo-app/includes/first-mongo-app8.md)]
[!INCLUDE[](~/tutorials/first-mongo-app/includes/first-mongo-app7.md)]
diff --git a/aspnetcore/tutorials/first-mongo-app/includes/first-mongo-app-9.md b/aspnetcore/tutorials/first-mongo-app/includes/first-mongo-app-9.md
new file mode 100644
index 000000000000..29b08836c6d9
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/includes/first-mongo-app-9.md
@@ -0,0 +1,518 @@
+:::moniker range="= aspnetcore-9.0"
+
+This tutorial creates a web API that runs Create, Read, Update, and Delete (CRUD) operations on a [MongoDB](https://www.mongodb.com/what-is-mongodb) NoSQL database.
+
+In this tutorial, you learn how to:
+
+> [!div class="checklist"]
+> * Configure MongoDB
+> * Create a MongoDB database
+> * Define a MongoDB collection and schema
+> * Perform MongoDB CRUD operations from a web API
+> * Customize JSON serialization
+
+## Prerequisites
+
+* [MongoDB 6.0.5 or later](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/)
+* [MongoDB Shell](https://www.mongodb.com/docs/mongodb-shell/install/)
+
+# [Visual Studio](#tab/visual-studio)
+
+[!INCLUDE[](~/includes/net-prereqs-vs-9.0.md)]
+
+# [Visual Studio Code](#tab/visual-studio-code)
+
+[!INCLUDE[](~/includes/net-prereqs-vsc-9.0.md)]
+
+---
+
+## Configure MongoDB
+
+
+Enable MongoDB and MongoDB Shell access from anywhere on the development machine (Windows/Linux/macOS):
+
+1. Download and Install MongoDB Shell:
+ * macOS/Linux: Choose a directory to extract the MongoDB Shell to. Add the resulting path for `mongosh` to the `PATH` environment variable.
+ * Windows: MongoDB Shell (mongosh.exe) is installed at *C:\\Users\\\\\AppData\\Local\\Programs\\mongosh*. Add the resulting path for `mongosh.exe` to the `PATH` environment variable.
+1. Download and Install MongoDB:
+ * macOS/Linux: Verify the directory that MongoDB was installed at, usually in */usr/local/mongodb*. Add the resulting path for `mongodb` to the `PATH` environment variable.
+ * Windows: MongoDB is installed at *C:\\Program Files\\MongoDB* by default. Add *C:\\Program Files\\MongoDB\\Server\\\\\bin* to the `PATH` environment variable.
+1. Choose a Data Storage Directory: Select a directory on your development machine for storing data. Create the directory if it doesn't exist. The MongoDB Shell doesn't create new directories:
+ * macOS/Linux: For example, `/usr/local/var/mongodb`.
+ * Windows: For example, `C:\\BooksData`.
+1. In the OS command shell (not the MongoDB Shell), use the following command to connect to MongoDB on default port 27017. Replace `` with the directory chosen in the previous step.
+
+ ```console
+ mongod --dbpath
+ ```
+
+Use the previously installed MongoDB Shell in the following steps to create a database, make collections, and store documents. For more information on MongoDB Shell commands, see [`mongosh`](https://docs.mongodb.com/mongodb-shell/run-commands/).
+
+1. Open a MongoDB command shell instance by launching `mongosh.exe`, or by running the following command in the command shell:
+
+ ```console
+ mongosh
+ ```
+
+1. In the command shell connect to the default test database by running:
+
+ ```console
+ use BookStore
+ ```
+
+ A database named *BookStore* is created if it doesn't already exist. If the database does exist, its connection is opened for transactions.
+
+1. Create a `Books` collection using following command:
+
+ ```console
+ db.createCollection('Books')
+ ```
+
+ The following result is displayed:
+
+ ```console
+ { "ok" : 1 }
+ ```
+
+1. Define a schema for the `Books` collection and insert two documents using the following command:
+
+ ```console
+ db.Books.insertMany([{ "Name": "Design Patterns", "Price": 54.93, "Category": "Computers", "Author": "Ralph Johnson" }, { "Name": "Clean Code", "Price": 43.15, "Category": "Computers","Author": "Robert C. Martin" }])
+ ```
+
+ A result similar to the following is displayed:
+
+ ```console
+ {
+ "acknowledged" : true,
+ "insertedIds" : [
+ ObjectId("61a6058e6c43f32854e51f51"),
+ ObjectId("61a6058e6c43f32854e51f52")
+ ]
+ }
+ ```
+
+ > [!NOTE]
+ > The `ObjectId`s shown in the preceding result won't match those shown in the command shell.
+
+1. View the documents in the database using the following command:
+
+ ```console
+ db.Books.find().pretty()
+ ```
+
+ A result similar to the following is displayed:
+
+ ```console
+ {
+ "_id" : ObjectId("61a6058e6c43f32854e51f51"),
+ "Name" : "Design Patterns",
+ "Price" : 54.93,
+ "Category" : "Computers",
+ "Author" : "Ralph Johnson"
+ }
+ {
+ "_id" : ObjectId("61a6058e6c43f32854e51f52"),
+ "Name" : "Clean Code",
+ "Price" : 43.15,
+ "Category" : "Computers",
+ "Author" : "Robert C. Martin"
+ }
+ ```
+
+ The schema adds an autogenerated `_id` property of type `ObjectId` for each document.
+
+## Create the ASP.NET Core web API project
+
+# [Visual Studio](#tab/visual-studio)
+
+1. Go to **File** > **New** > **Project**.
+1. Select the **ASP.NET Core Web API** project type, and select **Next**.
+1. Name the project *BookStoreApi*, and select **Next**.
+1. In the **Additional information** dialog:
+ * Confirm the **Framework** is **.NET 9.0 (Standard Term Support)**.
+ * Confirm the checkbox for **Use controllers** is checked.
+ * Confirm the checkbox for **Enable OpenAPI support** is checked.
+ * Select **Create**.
+1. In the **Package Manager Console** window, navigate to the project root. Run the following command to install the .NET driver for MongoDB:
+
+ ```powershell
+ Install-Package MongoDB.Driver
+ ```
+
+# [Visual Studio Code](#tab/visual-studio-code)
+
+1. Run the following commands in a command shell:
+
+ ```dotnetcli
+ dotnet new webapi -o BookStoreApi --use-controllers
+ code BookStoreApi
+ ```
+
+ The preceding commands generate a new ASP.NET Core web API project and then open the project in Visual Studio Code.
+
+1. Once the OmniSharp server starts up, a dialog asks **Required assets to build and debug are missing from 'BookStoreApi'. Add them?**. Select **Yes**.
+1. Open the **Integrated Terminal** and run the following command to install the .NET driver for MongoDB:
+
+ ```dotnetcli
+ dotnet add package MongoDB.Driver
+ ```
+
+---
+
+## Add an entity model
+
+1. Add a *Models* directory to the project root.
+1. Add a `Book` class to the *Models* directory with the following code:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples_snapshot/9.x/Book.cs":::
+
+ In the preceding class, the `Id` property is:
+
+ * Required for mapping the Common Language Runtime (CLR) object to the MongoDB collection.
+ * Annotated with [`[BsonId]`](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonIdAttribute.htm) to make this property the document's primary key.
+ * Annotated with [`[BsonRepresentation(BsonType.ObjectId)]`](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonRepresentationAttribute.htm) to allow passing the parameter as type `string` instead of an [ObjectId](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_ObjectId.htm) structure. Mongo handles the conversion from `string` to `ObjectId`.
+
+ The `BookName` property is annotated with the [`[BsonElement]`](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Bson_Serialization_Attributes_BsonElementAttribute.htm) attribute. The attribute's value of `Name` represents the property name in the MongoDB collection.
+
+## Add a configuration model
+
+1. Add the following database configuration values to `appsettings.json`:
+
+ :::code language="json" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/appsettings.json" highlight="2-6":::
+
+1. Add a `BookStoreDatabaseSettings` class to the *Models* directory with the following code:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs":::
+
+ The preceding `BookStoreDatabaseSettings` class is used to store the `appsettings.json` file's `BookStoreDatabase` property values. The JSON and C# property names are named identically to ease the mapping process.
+
+1. Add the following highlighted code to `Program.cs`:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_BookStoreDatabaseSettings" highlight="4-5":::
+
+ In the preceding code, the configuration instance to which the `appsettings.json` file's `BookStoreDatabase` section binds is registered in the Dependency Injection (DI) container. For example, the `BookStoreDatabaseSettings` object's `ConnectionString` property is populated with the `BookStoreDatabase:ConnectionString` property in `appsettings.json`.
+
+1. Add the following code to the top of `Program.cs` to resolve the `BookStoreDatabaseSettings` reference:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_UsingModels":::
+
+## Add a CRUD operations service
+
+1. Add a *Services* directory to the project root.
+1. Add a `BooksService` class to the *Services* directory with the following code:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Services/BooksService.cs" id="snippet_File":::
+
+ In the preceding code, a `BookStoreDatabaseSettings` instance is retrieved from DI via constructor injection. This technique provides access to the `appsettings.json` configuration values that were added in the [Add a configuration model](#add-a-configuration-model) section.
+
+1. Add the following highlighted code to `Program.cs`:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_BooksService" highlight="7":::
+
+ In the preceding code, the `BooksService` class is registered with DI to support constructor injection in consuming classes. The singleton service lifetime is most appropriate because `BooksService` takes a direct dependency on `MongoClient`. Per the official [Mongo Client reuse guidelines](https://mongodb.github.io/mongo-csharp-driver/2.14/reference/driver/connecting/#re-use), `MongoClient` should be registered in DI with a singleton service lifetime.
+
+1. Add the following code to the top of `Program.cs` to resolve the `BooksService` reference:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_UsingServices":::
+
+The `BooksService` class uses the following `MongoDB.Driver` members to run CRUD operations against the database:
+
+* [MongoClient](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_MongoClient.htm): Reads the server instance for running database operations. The constructor of this class is provided in the MongoDB connection string:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Services/BooksService.cs" id="snippet_ctor" highlight="4-5":::
+
+* [IMongoDatabase](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_IMongoDatabase.htm): Represents the Mongo database for running operations. This tutorial uses the generic [GetCollection\(collection)](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoDatabase_GetCollection__1.htm) method on the interface to gain access to data in a specific collection. Run CRUD operations against the collection after this method is called. In the `GetCollection(collection)` method call:
+
+ * `collection` represents the collection name.
+ * `TDocument` represents the CLR object type stored in the collection.
+
+`GetCollection(collection)` returns a [MongoCollection](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/T_MongoDB_Driver_MongoCollection.htm) object representing the collection. In this tutorial, the following methods are invoked on the collection:
+
+* [DeleteOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_DeleteOneAsync_1.htm): Deletes a single document matching the provided search criteria.
+* [Find\](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollectionExtensions_Find__1.htm): Returns all documents in the collection matching the provided search criteria.
+* [InsertOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_InsertOneAsync_1.htm): Inserts the provided object as a new document in the collection.
+* [ReplaceOneAsync](https://mongodb.github.io/mongo-csharp-driver/2.14/apidocs/html/M_MongoDB_Driver_IMongoCollection_1_ReplaceOneAsync.htm): Replaces the single document matching the provided search criteria with the provided object.
+
+## Add a controller
+
+Add a `BooksController` class to the *Controllers* directory with the following code:
+
+:::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Controllers/BooksController.cs":::
+
+The preceding web API controller:
+
+* Uses the `BooksService` class to run CRUD operations.
+* Contains action methods to support GET, POST, PUT, and DELETE HTTP requests.
+* Calls in the `Create` action method to return an [HTTP 201](https://www.rfc-editor.org/rfc/rfc9110#status.201) response. Status code 201 is the standard response for an HTTP POST method that creates a new resource on the server. `CreatedAtAction` also adds a `Location` header to the response. The `Location` header specifies the URI of the newly created book.
+
+## Configure JSON serialization options
+
+There are two details to change about the JSON responses returned in the [Test the web API](#test-the-web-api) section:
+
+* The property names' default camel casing should be changed to match the Pascal casing of the CLR object's property names.
+* The `bookName` property should be returned as `Name`.
+
+To satisfy the preceding requirements, make the following changes:
+
+1. In `Program.cs`, chain the following highlighted code on to the `AddControllers` method call:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_AddControllers" highlight="10-11":::
+
+ With the preceding change, property names in the web API's serialized JSON response match their corresponding property names in the CLR object type. For example, the `Book` class's `Author` property serializes as `Author` instead of `author`.
+
+1. In `Models/Book.cs`, annotate the `BookName` property with the [`[JsonPropertyName]`](xref:System.Text.Json.Serialization.JsonPropertyNameAttribute) attribute:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Models/Book.cs" id="snippet_BookName" highlight="2":::
+
+ The `[JsonPropertyName]` attribute's value of `Name` represents the property name in the web API's serialized JSON response.
+
+1. Add the following code to the top of `Models/Book.cs` to resolve the `[JsonPropertyName]` attribute reference:
+
+ :::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Models/Book.cs" id="snippet_UsingSystemTextJsonSerialization":::
+
+1. Repeat the steps defined in the [Test the web API](#test-the-web-api) section. Notice the difference in JSON property names.
+
+## Test the web API
+
+# [Visual Studio](#tab/visual-studio)
+
+This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use-endpoints-explorer) to test the API.
+
+1. Build and run the app.
+
+1. In **Endpoints Explorer**, right-click the first **GET** endpoint `/api/books`, and select **Generate request**.
+
+ The following content is added to the `BookStoreApi.http` file.
+ If this is the first time that a request is generated, the file is created in the project root.
+
+ ```
+ @BookStoreApi_HostAddress = https://localhost:
+
+ GET {{BookStoreApi_HostAddress}}/api/books
+
+ ###
+ ```
+
+ The port number should already be set to the port used by the app, for example, `https://localhost:56874`.
+ If that's not the case you can find your port number in the output window when you launch the app.
+
+1. Select the **Send request** link above the new `GET` request line.
+
+ The GET request is sent to the app and the response is displayed in the **Response** pane.
+
+1. The response body shows the JSON result containing the book entries similar to the following:
+
+ ```json
+ [
+ {
+ "Id": "61a6058e6c43f32854e51f51",
+ "Name": "Design Patterns",
+ "Price": 54.93,
+ "Category": "Computers",
+ "Author": "Ralph Johnson"
+ },
+ {
+ "Id": "61a6058e6c43f32854e51f52",
+ "Name": "Clean Code",
+ "Price": 43.15,
+ "Category": "Computers",
+ "Author": "Robert C. Martin"
+ }
+ ]
+ ```
+
+1. To retrieve a single book, right-click the `/api/books/{id}, params (string id)` **GET** endpoint in the **Endpoints Explorer**, and select **Generate request**.
+
+ The following content is appended to the `BookStoreApi.http` file:
+
+ ```
+ @id=string
+ GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
+
+ ###
+ ```
+
+1. Replace `id` variable with one of the IDs returned from the earlier request, for example:
+
+ ```
+ @id="61a6058e6c43f32854e51f52"
+ GET {{BookStoreApi_HostAddress}}/api/books/{{id}}
+
+ ###
+ ```
+
+1. Select the **Send request** link above the new `GET` request line.
+
+ The GET request is sent to the app and the response is displayed in the **Response** pane.
+
+1. The response body shows JSON similar to the following:
+
+ ```json
+ {
+ "Id": "61a6058e6c43f32854e51f52",
+ "Name": "Clean Code",
+ "Price": 43.15,
+ "Category": "Computers",
+ "Author": "Robert C. Martin"
+ }
+ ```
+
+1. To test the POST endpoint, right-click the `/api/books` **POST** endpoint and select **Generate request**.
+
+ The following content is added to the `BookStoreApi.http` file:
+
+ ```
+ POST {{BookStoreApi_HostAddress}}/api/books
+ Content-Type: application/json
+
+ {
+ //Book
+ }
+
+ ###
+ ```
+
+1. Replace the Book comment with a book object as the JSON request body:
+
+ ```
+ POST {{BookStoreApi_HostAddress}}/api/books
+ Content-Type: application/json
+
+ {
+ "Name": "The Pragmatic Programmer",
+ "Price": 49.99,
+ "Category": "Computers",
+ "Author": "Andy Hunt"
+ }
+
+ ###
+ ```
+
+1. Select the **Send request** link above the `POST` request line.
+
+ The POST request is sent to the app, and the response is displayed in the **Response** pane. The response should include the newly created book with its assigned ID.
+
+1. Lastly, to delete a book, right-click the `/api/books/{id}, params (string id)` **DELETE** endpoint and select **Generate request**.
+
+ The following content is appended to the `BookStoreApi.http` file:
+
+ ```
+ DELETE {{BookStoreApi_HostAddress}}/api/Books/{{id}}
+
+ ###
+ ```
+
+1. Replace the `id` variable with one of the IDs returned from the earlier request, and click **Send request**. For example:
+
+ ```
+ DELETE {{BookStoreApi_HostAddress}}/api/Books/67f417517ce1b36aeab71236
+
+ ###
+ ```
+
+# [Visual Studio Code](#tab/visual-studio-code)
+
+This tutorial uses the [OpenAPI specification (openapi.json) and Swagger UI](xref:tutorials/web-api-help-pages-using-swagger) to test the API.
+
+1. Install Swagger UI by running the following command:
+
+ ```dotnetcli
+ dotnet add package NSwag.AspNetCore
+ ```
+
+The previous command adds the [NSwag.AspNetCore](https://www.nuget.org/packages/NSwag.AspNetCore/) package, which contains tools to generate Swagger documents and UI.
+Because our project is using OpenAPI, we only use the NSwag package to generate the Swagger UI.
+
+1. Configure Swagger middleware
+
+In `Program.cs`, add the following highlighted code:
+
+:::code language="csharp" source="~/tutorials/first-mongo-app/samples/9.x/BookStoreApi/Program.cs" id="snippet_UseSwagger" highlight="6-9":::
+
+The previous code enables the Swagger middleware for serving the generated JSON document using the Swagger UI. Swagger is only enabled in a development environment. Enabling Swagger in a production environment could expose potentially sensitive details about the API's structure and implementation.
+
+The app uses the OpenAPI document generated by OpenApi, located at `/openapi/v1.json`, to generate the UI.
+View the generated OpenAPI specification for the `BookStoreApi` API while the project is running by navigating to `https://localhost:/openapi/v1.json` in your browser.
+
+The OpenAPI specification is a document in JSON format that describes the structure and capabilities of your API, including endpoints, request/response formats, parameters, and more. It's essentially a blueprint of your API that can be used by various tools to understand and interact with your API.
+
+1. Build and run the app.
+
+1. Navigate to `https://localhost:/swagger` in your browser. Swagger provides a UI to test all the API endpoints based on the OpenAPI document.
+
+1. Expand the **GET /api/books** endpoint and click the **Try it out** button.
+
+1. Click the **Execute** button to send the request to the API.
+
+1. The **Response body** section displays a JSON array with books similar to the following:
+
+ ```json
+ [
+ {
+ "Id": "61a6058e6c43f32854e51f51",
+ "Name": "Design Patterns",
+ "Price": 54.93,
+ "Category": "Computers",
+ "Author": "Ralph Johnson"
+ },
+ {
+ "Id": "61a6058e6c43f32854e51f52",
+ "Name": "Clean Code",
+ "Price": 43.15,
+ "Category": "Computers",
+ "Author": "Robert C. Martin"
+ }
+ ]
+ ```
+
+1. Next, expand the **GET /api/books/{id}** endpoint and click **Try it out**.
+
+1. Enter one of the book IDs from the previous response in the **id** field, then click **Execute**.
+
+1. The **Response body** section displays the JSON object for the specified book. For example, the result for the ID `61a6058e6c43f32854e51f52` is similar to the following:
+
+ ```json
+ {
+ "Id": "61a6058e6c43f32854e51f52",
+ "Name": "Clean Code",
+ "Price": 43.15,
+ "Category": "Computers",
+ "Author": "Robert C. Martin"
+ }
+ ```
+
+1. To test creating a new book, expand the **POST /api/books** endpoint and click **Try it out**.
+
+1. Replace the default request body with a new book object:
+
+ ```json
+ {
+ "Name": "The Pragmatic Programmer",
+ "Price": 49.99,
+ "Category": "Computers",
+ "Author": "Andy Hunt"
+ }
+ ```
+
+1. Click **Execute** to send the request.
+
+1. The response should have a status code of 201 (Created) and include the newly created book with its assigned ID in the response body.
+
+1. Lastly, to delete a book record, expand the **DELETE /api/books/{id}** endpoint, click **Try it out**, and enter one of the book IDs from the previous response in the **id** field. Click **Execute** to send the request.
+
+1. The response should have a status code of 204 (No Content), indicating that the book was successfully deleted.
+---
+
+## Add authentication support to a web API
+
+[!INCLUDE[](~/includes/DuendeIdentityServer.md)]
+
+## Additional resources
+
+* [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/tutorials/first-mongo-app/samples/9.x/BookStoreApi) ([how to download](xref:fundamentals/index#how-to-download-a-sample))
+*
+*
+* [Create a web API with ASP.NET Core](/training/modules/build-web-api-aspnet-core/)
+
+:::moniker-end
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/BookStoreApi.csproj b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/BookStoreApi.csproj
new file mode 100644
index 000000000000..1c26a8332302
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/BookStoreApi.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/BookStoreApi.http b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/BookStoreApi.http
new file mode 100644
index 000000000000..d4d8f251a128
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/BookStoreApi.http
@@ -0,0 +1,23 @@
+@BookStoreApi_HostAddress = https://localhost:56874
+
+GET {{BookStoreApi_HostAddress}}/books
+
+###
+
+@id=string
+GET {{BookStoreApi_HostAddress}}/books/{{id}}
+
+###
+
+POST {{BookStoreApi_HostAddress}}/books
+Content-Type: application/json
+
+{
+ //Book
+}
+
+###
+
+DELETE {{BookStoreApi_HostAddress}}/books/{{id}}
+
+###
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Models/Book.cs b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Models/Book.cs
new file mode 100644
index 000000000000..56dbb50b5df8
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Models/Book.cs
@@ -0,0 +1,26 @@
+//
+using System.Text.Json.Serialization;
+//
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace BookStoreApi.Models;
+
+public class Book
+{
+ [BsonId]
+ [BsonRepresentation(BsonType.ObjectId)]
+ public string? Id { get; set; }
+
+ //
+ [BsonElement("Name")]
+ [JsonPropertyName("Name")]
+ public string BookName { get; set; } = null!;
+ //
+
+ public decimal Price { get; set; }
+
+ public string Category { get; set; } = null!;
+
+ public string Author { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs
new file mode 100644
index 000000000000..7611890a4787
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Models/BookStoreDatabaseSettings.cs
@@ -0,0 +1,10 @@
+namespace BookStoreApi.Models;
+
+public class BookStoreDatabaseSettings
+{
+ public string ConnectionString { get; set; } = null!;
+
+ public string DatabaseName { get; set; } = null!;
+
+ public string BooksCollectionName { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Program.cs b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Program.cs
new file mode 100644
index 000000000000..1fc70d75e193
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Program.cs
@@ -0,0 +1,98 @@
+//
+using BookStoreApi.Models;
+//
+//
+using BookStoreApi.Services;
+//
+
+//
+//
+//
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+builder.Services.Configure(
+ builder.Configuration.GetSection("BookStoreDatabase"));
+//
+
+builder.Services.AddSingleton();
+//
+
+builder.Services.Configure(options =>
+{
+ options.SerializerOptions.PropertyNamingPolicy = null;
+});
+//
+
+// Add services to the container.
+// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
+builder.Services.AddOpenApi();
+
+//
+var app = builder.Build();
+
+if (app.Environment.IsDevelopment())
+{
+ app.MapOpenApi();
+ app.UseSwaggerUi(options =>
+ {
+ options.DocumentPath = "/openapi/v1.json";
+ });
+}
+//
+
+//
+var booksGroup = app.MapGroup("/books");
+
+booksGroup.MapGet("/", async (BooksService booksService) =>
+{
+ var books = await booksService.GetAsync();
+ return Results.Ok(books);
+});
+
+booksGroup.MapGet("/{id:length(24)}", async (string id, BooksService booksService) =>
+{
+ var book = await booksService.GetAsync(id);
+
+ return book is null ? Results.NotFound() : Results.Ok(book);
+});
+
+booksGroup.MapPost("/", async (Book newBook, BooksService booksService) =>
+{
+ await booksService.CreateAsync(newBook);
+
+ return Results.Created($"/books/{newBook.Id}", newBook);
+});
+
+booksGroup.MapPut("/{id:length(24)}", async (string id, Book updatedBook, BooksService booksService) =>
+{
+ var book = await booksService.GetAsync(id);
+
+ if (book is null)
+ {
+ return Results.NotFound();
+ }
+
+ updatedBook.Id = book.Id;
+
+ await booksService.UpdateAsync(id, updatedBook);
+
+ return Results.NoContent();
+});
+
+booksGroup.MapDelete("/{id:length(24)}", async (string id, BooksService booksService) =>
+{
+ var book = await booksService.GetAsync(id);
+
+ if (book is null)
+ {
+ return Results.NotFound();
+ }
+
+ await booksService.RemoveAsync(id);
+
+ return Results.NoContent();
+});
+//
+
+app.Run();
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Services/BooksService.cs b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Services/BooksService.cs
new file mode 100644
index 000000000000..c336718109ac
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/Services/BooksService.cs
@@ -0,0 +1,31 @@
+//
+using BookStoreApi.Models;
+using Microsoft.Extensions.Options;
+using MongoDB.Driver;
+
+namespace BookStoreApi.Services;
+
+//
+public class BooksService(IOptions bookStoreDatabaseSettings)
+{
+ private readonly IMongoCollection _booksCollection = new MongoClient(bookStoreDatabaseSettings.Value.ConnectionString)
+ .GetDatabase(bookStoreDatabaseSettings.Value.DatabaseName)
+ .GetCollection(bookStoreDatabaseSettings.Value.BooksCollectionName);
+//
+
+ public async Task> GetAsync() =>
+ await _booksCollection.Find(_ => true).ToListAsync();
+
+ public async Task GetAsync(string id) =>
+ await _booksCollection.Find(x => x.Id == id).FirstOrDefaultAsync();
+
+ public async Task CreateAsync(Book newBook) =>
+ await _booksCollection.InsertOneAsync(newBook);
+
+ public async Task UpdateAsync(string id, Book updatedBook) =>
+ await _booksCollection.ReplaceOneAsync(x => x.Id == id, updatedBook);
+
+ public async Task RemoveAsync(string id) =>
+ await _booksCollection.DeleteOneAsync(x => x.Id == id);
+}
+//
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/appsettings.Development.json b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/appsettings.Development.json
new file mode 100644
index 000000000000..0c208ae9181e
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/appsettings.json b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/appsettings.json
new file mode 100644
index 000000000000..db0fa877639d
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples/10.x/BookStoreApi/appsettings.json
@@ -0,0 +1,14 @@
+{
+ "BookStoreDatabase": {
+ "ConnectionString": "mongodb://localhost:27017",
+ "DatabaseName": "BookStore",
+ "BooksCollectionName": "Books"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/aspnetcore/tutorials/first-mongo-app/samples_snapshot/10.x/Book.cs b/aspnetcore/tutorials/first-mongo-app/samples_snapshot/10.x/Book.cs
new file mode 100644
index 000000000000..97756d7d5d5a
--- /dev/null
+++ b/aspnetcore/tutorials/first-mongo-app/samples_snapshot/10.x/Book.cs
@@ -0,0 +1,20 @@
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+
+namespace BookStoreApi.Models;
+
+public class Book
+{
+ [BsonId]
+ [BsonRepresentation(BsonType.ObjectId)]
+ public string? Id { get; set; }
+
+ [BsonElement("Name")]
+ public string BookName { get; set; } = null!;
+
+ public decimal Price { get; set; }
+
+ public string Category { get; set; } = null!;
+
+ public string Author { get; set; } = null!;
+}