diff --git a/aspnetcore/tutorials/first-web-api.md b/aspnetcore/tutorials/first-web-api.md index db4401248098..9739b5504253 100644 --- a/aspnetcore/tutorials/first-web-api.md +++ b/aspnetcore/tutorials/first-web-api.md @@ -1,14 +1,14 @@ --- -title: "Tutorial: Create a web API with ASP.NET Core" +title: "Tutorial: Create a controller-based web API with ASP.NET Core" author: wadepickett -description: Learn how to build a web API with ASP.NET Core. +description: Learn how to build a controller-based web API with ASP.NET Core. ms.author: wpickett ms.custom: mvc, engagement-fy24 -ms.date: 08/04/2024 +ms.date: 02/13/2025 uid: tutorials/first-web-api --- -# Tutorial: Create a web API with ASP.NET Core +# Tutorial: Create a controller-based web API with ASP.NET Core [!INCLUDE[](~/includes/not-latest-version.md)] @@ -38,15 +38,15 @@ The following diagram shows the design of the app. # [Visual Studio](#tab/visual-studio) -[!INCLUDE[](~/includes/net-prereqs-vs-8.0.md)] +[!INCLUDE[](~/includes/net-prereqs-vs-9.0.md)] # [Visual Studio Code](#tab/visual-studio-code) -[!INCLUDE[](~/includes/net-prereqs-vsc-8.0.md)] +[!INCLUDE[](~/includes/net-prereqs-vsc-9.0.md)] --- -## Create a web project +## Create a Web API project # [Visual Studio](#tab/visual-studio) @@ -55,23 +55,36 @@ The following diagram shows the design of the app. * Select the **ASP.NET Core Web API** template and select **Next**. * In the **Configure your new project dialog**, name the project *TodoApi* and select **Next**. * In the **Additional information** dialog: - * Confirm the **Framework** is **.NET 8.0 (Long Term Support)**. - * Confirm the checkbox for **Use controllers(uncheck to use minimal APIs)** is checked. + * Confirm the **Framework** is **.NET 9.0 (Standard Term Support)**. * Confirm the checkbox for **Enable OpenAPI support** is checked. + * Confirm the checkbox for **Do not use top-level statements** is **not** checked. + * Confirm the checkbox for **Use controllers** is checked. * Select **Create**. -## Add a NuGet package +The project template: -A NuGet package must be added to support the database used in this tutorial. +* Creates a `WeatherForecast` API using controllers. +* Adds the `Microsoft.AspNetCore.OpenApi` package for OpenAPI support as a reference in the project file **TodoApi.csproj**. +* Adds OpenAPI services in **Program.cs** to automatically generate OpenAPI JSON documentation for the `WeatherForecast` API. -* From the **Tools** menu, select **NuGet Package Manager > Manage NuGet Packages for Solution**. -* Select the **Browse** tab. -* Enter **Microsoft.EntityFrameworkCore.InMemory** in the search box, and then select `Microsoft.EntityFrameworkCore.InMemory`. -* Select the **Project** checkbox in the right pane and then select **Install**. +## Add NuGet packages + +This tutorial uses the following additional NuGet packages: + +* `Microsoft.EntityFrameworkCore.InMemory` enables Entity Framework Core to work with an in-memory database rather than an external one, simplifying this tutorial. +* `Swashbuckle.AspNetCore.SwaggerUI` provides a user interface for exploring and testing API endpoints interactively through Swagger. + +* Add the following NuGet packages used in this tutorial: + * From the **Tools** menu, select **NuGet Package Manager > Manage NuGet Packages for Solution**. + * Select the **Browse** tab. + * Enter **Microsoft.EntityFrameworkCore.InMemory** in the search box, and then select `Microsoft.EntityFrameworkCore.InMemory`. + * Select the **Project** checkbox in the right pane and then select **Install**. + * Enter **Swashbuckle.AspNetCore.SwaggerUI** in the search box, and then select `Swashbuckle.AspNetCore.SwaggerUI`. + * Select the **Project** checkbox in the right pane and then select **Install**. # [Visual Studio Code](#tab/visual-studio-code) -* Open the [integrated terminal](https://code.visualstudio.com/docs/editor/integrated-terminal). +* Open the [integrated terminal](https://code.visualstudio.com/docs/terminal/basics). * Change directories (`cd`) to the folder that will contain the project folder. * Run the following commands: @@ -79,14 +92,23 @@ A NuGet package must be added to support the database used in this tutorial. dotnet new webapi --use-controllers -o TodoApi cd TodoApi dotnet add package Microsoft.EntityFrameworkCore.InMemory + dotnet add package Swashbuckle.AspNetCore.SwaggerUI code -r ../TodoApi ``` - These commands: +These commands: + +* Create a new web API project and open it in Visual Studio Code. +* Adds NuGet packages that are used in this tutorial: + * `Microsoft.EntityFrameworkCore.InMemory`: Enables Entity Framework Core to work with an in-memory database so a real database won't be required for this tutorial. + * `Swashbuckle.AspNetCore.SwaggerUI`: Provides a user interface for exploring and testing API endpoints interactively through Swagger. +* Open the *TodoApi* folder in the current instance of Visual Studio Code. + + The project template: - * Create a new web API project and open it in Visual Studio Code. - * Add a NuGet package that is needed for the next section. - * Open the *TodoApi* folder in the current instance of Visual Studio Code. + * Creates a `WeatherForecast` API using controllers. + * Adds the `Microsoft.AspNetCore.OpenApi` package for OpenAPI support as a reference in the project file **TodoApi.csproj**. + * Adds OpenAPI services in **Program.cs** to automatically generate OpenAPI JSON documentation for the `WeatherForecast` API. [!INCLUDE[](~/includes/vscode-trust-authors-add-assets.md)] @@ -94,9 +116,7 @@ A NuGet package must be added to support the database used in this tutorial. [!INCLUDE[](~/includes/package-reference.md)] -### Test the project - -The project template creates a `WeatherForecast` API with support for [Swagger](xref:tutorials/web-api-help-pages-using-swagger). +## Run the Project # [Visual Studio](#tab/visual-studio) @@ -104,7 +124,16 @@ Press Ctrl+F5 to run without the debugger. [!INCLUDE[](~/includes/trustCertVS.md)] -Visual Studio launches the default browser and navigates to `https://localhost:/swagger/index.html`, where `` is a randomly chosen port number set at the project creation. + Visual Studio launches a new console window with output messages similar to the following, indicating that the app is running and awaiting requests: + + ```output + info: Microsoft.Hosting.Lifetime[14] + Now listening on: https://localhost:7152 + info: Microsoft.Hosting.Lifetime[14] + Now listening on: http://localhost:5026 + info: Microsoft.Hosting.Lifetime[0] + Application started. Press Ctrl+C to shut down. + ``` # [Visual Studio Code](#tab/visual-studio-code) @@ -118,73 +147,105 @@ Run the app: dotnet run --launch-profile https ``` - The output shows messages similar to the following, indicating that the app is running and awaiting requests: + Visual Studio Code launches a new terminal window displaying output messages similar to the following, indicating that the app is running and awaiting requests: ```output ... info: Microsoft.Hosting.Lifetime[14] - Now listening on: https://localhost:{port} + Now listening on: https://localhost:{port number} ... ``` * Ctrl+*click* the HTTPS URL in the output to test the web app in a browser. -* The default browser is launched to `https://localhost:/swagger/index.html`, where `` is the randomly chosen port number displayed in the output. There's no endpoint at `https://localhost:`, so the browser returns [HTTP 404 Not Found](https://developer.mozilla.org/docs/Web/HTTP/Status/404). Append `/swagger` to the URL, `https://localhost:/swagger`. - -After testing the web app in the following instruction, press Ctrl+C in the integrated terminal to shut it down. - --- -The Swagger page `/swagger/index.html` is displayed. Select **GET** > **Try it out** > **Execute**. The page displays: - -* The [Curl](https://curl.haxx.se/) command to test the WeatherForecast API. -* The URL to test the WeatherForecast API. -* The response code, body, and headers. -* A drop-down list box with media types and the example value and schema. - -If the Swagger page doesn't appear, see [this GitHub issue](https://github.com/dotnet/AspNetCore.Docs/issues/21647). - -Swagger is used to generate useful documentation and help pages for web APIs. This tutorial uses Swagger to test the app. For more information on Swagger, see . - -Copy and paste the **Request URL** in the browser: `https://localhost:/weatherforecast` +* Copy and paste the **Request URL** to the browser: `https://localhost:/weatherforecast`, where `` is the randomly chosen port number set in **Properties/launchSettings.json** as the port for HTTPS, and displayed in the "Now listening" output messages when the app is run. JSON similar to the following example is returned: ```json [ { - "date": "2019-07-16T19:04:05.7257911-06:00", - "temperatureC": 52, - "temperatureF": 125, - "summary": "Mild" + "date":"2025-02-19", + "temperatureC":10, + "temperatureF":49, + "summary":"Scorching" }, { - "date": "2019-07-17T19:04:05.7258461-06:00", - "temperatureC": 36, - "temperatureF": 96, - "summary": "Warm" + "date":"2025-02-20", + "temperatureC":32, + "temperatureF":89, + "summary":"Chilly" }, { - "date": "2019-07-18T19:04:05.7258467-06:00", - "temperatureC": 39, - "temperatureF": 102, - "summary": "Cool" + "date":"2025-02-21", + "temperatureC":-2, + "temperatureF":29, + "summary":"Scorching" }, { - "date": "2019-07-19T19:04:05.7258471-06:00", - "temperatureC": 10, - "temperatureF": 49, - "summary": "Bracing" + "date":"2025-02-22", + "temperatureC":-19, + "temperatureF":-2, + "summary":"Sweltering" }, { - "date": "2019-07-20T19:04:05.7258474-06:00", - "temperatureC": -1, - "temperatureF": 31, - "summary": "Chilly" + "date":"2025-02-23", + "temperatureC":40, + "temperatureF":103, + "summary":"Hot" } ] ``` +* View the generated OpenAPI specification for the `WeatherForecast` API while the project is running by navigating your browser to `https://localhost:/openapi/v1.json`. + +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. + +# [Visual Studio](#tab/visual-studio) + +* Stop the app by selecting the red square **Stop** button in the Visual Studio toolbar, or press Ctrl+C in the console window. + +# [Visual Studio Code](#tab/visual-studio-code) + +* Stop the app by pressing Ctrl+C in the integrated terminal. + +--- + +## Configure the Swagger UI endpoint for the OpenAPI documentation + +To configure [Swagger](xref:tutorials/web-api-help-pages-using-swagger) UI for testing the API, add the following highlighted code to the `Program.cs` file in the **TodoAPI** project: + +[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs?name=snippet_First_Add_SwaggerUI&highlight=16-19)] + +The preceding highlighted code: + +* Adds the Swagger UI as a service to the app with `app.UseSwaggerUI()`. +* Sets the `SwaggerEndpoint()` option to the location of the OpenAPI documentation for this project. +* Ensures the Swagger UI is only available in the app development environment to limit information disclosure and security vulnerability. + +## View the Swagger UI + +* Press Ctrl+F5 to run the app. + +* Navigate a browser to `https://localhost:/swagger/index.html`, where `` is the randomly chosen port number set in **Properties/launchSettings.json** as the port for HTTPS, and displayed in the "Now listening" output messages when the app is run. + +The Swagger page `/swagger/index.html` is displayed. + +* Select **GET** > **Try it out** > **Execute**. + +The page displays: + +* The [Curl](https://curl.haxx.se/) command to test the WeatherForecast API. +* The URL to test the WeatherForecast API. +* The response code, body, and headers. +* A drop-down list box with media types and the example value and schema. + +If the Swagger page doesn't appear, see [this GitHub issue](https://github.com/dotnet/AspNetCore.Docs/issues/21647). + +* Stop the app. + ## Add a model class A *model* is a set of classes that represent the data that the app manages. The model for this app is the `TodoItem` class. @@ -230,15 +291,15 @@ The *database context* is the main class that coordinates Entity Framework funct In ASP.NET Core, services such as the DB context must be registered with the [dependency injection (DI)](xref:fundamentals/dependency-injection) container. The container provides the service to controllers. -Update `Program.cs` with the following highlighted code: +* Update `Program.cs` with the following highlighted code: -[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs?highlight=1-2,7-8)] + [!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs?name=snippet_Final_Add_DBContext&highlight=1-2,9-10)] The preceding code: * Adds `using` directives. * Adds the database context to the DI container. -* Specifies that the database context will use an in-memory database. +* Specifies that the database context uses an in-memory database. ## Scaffold a controller @@ -286,9 +347,9 @@ source ~/.bashrc [!INCLUDE[](~/includes/dotnet-tool-install-arch-options.md)] -Build the project. +* Build the project. -Run the following command: +* Run the following command: ```dotnetcli dotnet aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers @@ -312,9 +373,9 @@ When the `[action]` token isn't in the route template, the [action](xref:mvc/con ## Update the PostTodoItem create method -Update the return statement in the `PostTodoItem` to use the [nameof](/dotnet/csharp/language-reference/operators/nameof) operator: +* In **Controllers/TodoItemsController.cs** update the return statement in the `PostTodoItem` to use the [nameof](/dotnet/csharp/language-reference/operators/nameof) operator: -[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_Create)] + [!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_Create)] The preceding code is an `HTTP POST` method, as indicated by the [`[HttpPost]`](xref:Microsoft.AspNetCore.Mvc.HttpPostAttribute) attribute. The method gets the value of the `TodoItem` from the body of the HTTP request. @@ -343,7 +404,7 @@ The method: * Select **Execute** - ![Swagger POST](~/tutorials/first-web-api/_static/7/post.png) + ![Swagger POST](~/tutorials/first-web-api/_static/9/post.png) ### Test the location header URI @@ -365,7 +426,7 @@ Two GET endpoints are implemented: The previous section showed an example of the `/api/todoitems/{id}` route. -Follow the [POST](#post7) instructions to add another todo item, and then test the `/api/todoitems` route using Swagger. +* Follow the [POST](#post7) instructions to add another todo item, and then test the `/api/todoitems` route using Swagger. This app uses an in-memory database. If the app is stopped and started, the preceding GET request doesn't return any data. If no data is returned, [POST](#post7) data to the app. @@ -375,10 +436,11 @@ The [`[HttpGet]`](xref:Microsoft.AspNetCore.Mvc.HttpGetAttribute) attribute deno * Start with the template string in the controller's `Route` attribute: - [!code-csharp[](~/tutorials/first-web-api/samples/6.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_Route&highlight=1)] + [!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_Route&highlight=1)] * Replace `[controller]` with the name of the controller, which by convention is the controller class name minus the "Controller" suffix. For this sample, the controller class name is **TodoItems**Controller, so the controller name is "TodoItems". ASP.NET Core [routing](xref:mvc/controllers/routing) is case insensitive. -* If the `[HttpGet]` attribute has a route template (for example, `[HttpGet("products")]`), append that to the path. This sample doesn't use a template. For more information, see [Attribute routing with Http[Verb] attributes](xref:mvc/controllers/routing#verb). + +This sample doesn't use a route template with the [HttpGet] attribute. However in applications where an `[HttpGet]` attribute has a route template (for example, `[HttpGet("products")]`), append that to the path. For more information, see [Attribute routing with Http[Verb] attributes](xref:mvc/controllers/routing#verb). In the following `GetTodoItem` method, `"{id}"` is a placeholder variable for the unique identifier of the to-do item. When `GetTodoItem` is invoked, the value of `"{id}"` in the URL is provided to the method in its `id` parameter. @@ -397,7 +459,7 @@ The return type of the `GetTodoItems` and `GetTodoItem` methods is [ActionResult Examine the `PutTodoItem` method: -[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_Update)] +[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_PutTodoItem)] `PutTodoItem` is similar to `PostTodoItem`, except it uses `HTTP PUT`. The response is [204 (No Content)](https://www.rfc-editor.org/rfc/rfc9110#status.204). According to the HTTP specification, a `PUT` request requires the client to send the entire updated entity, not just the changes. To support partial updates, use [HTTP PATCH](xref:Microsoft.AspNetCore.Mvc.HttpPatchAttribute). @@ -405,7 +467,7 @@ Examine the `PutTodoItem` method: This sample uses an in-memory database that must be initialized each time the app is started. There must be an item in the database before you make a PUT call. Call GET to ensure there's an item in the database before making a PUT call. -Using the Swagger UI, use the PUT button to update the `TodoItem` that has Id = 1 and set its name to `"feed fish"`. Note the response is [`HTTP 204 No Content`](https://developer.mozilla.org/docs/Web/HTTP/Status/204). +* Using the Swagger UI, use the PUT button to update the `TodoItem` that has Id = 1 and set its name to `"feed fish"`. Note the response is [`HTTP 204 No Content`](https://developer.mozilla.org/docs/Web/HTTP/Status/204). ## The DeleteTodoItem method @@ -415,7 +477,7 @@ Examine the `DeleteTodoItem` method: ### Test the DeleteTodoItem method -Use the Swagger UI to delete the `TodoItem` that has Id = 1. Note the response is [`HTTP 204 No Content`](https://developer.mozilla.org/docs/Web/HTTP/Status/204). +* Use the Swagger UI to delete the `TodoItem` that has Id = 1. Note the response is [`HTTP 204 No Content`](https://developer.mozilla.org/docs/Web/HTTP/Status/204). ## Test with other tools @@ -445,21 +507,21 @@ A DTO may be used to: * Omit some properties in order to reduce payload size. * Flatten object graphs that contain nested objects. Flattened object graphs can be more convenient for clients. -To demonstrate the DTO approach, update the `TodoItem` class to include a secret field: +* To demonstrate the DTO approach, update the `TodoItem` class to include a secret field: -[!code-csharp[](~/tutorials/first-web-api/samples/6.0/TodoApiDTO/Models/TodoItem.cs?highlight=8)] +[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItem.cs?highlight=8)] The secret field needs to be hidden from this app, but an administrative app could choose to expose it. Verify you can post and get the secret field. -Create a DTO model: +Create a DTO model in a **Models/TodoItemsDTO.cs** file: [!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItemDTO.cs)] -Update the `TodoItemsController` to use `TodoItemDTO`: +* Update the `TodoItemsController` to use `TodoItemDTO`: -[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs)] + [!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs?highlight=25,28,34,43,49,51,62,63,89,93,94,100,124-130)] Verify you can't post or get the secret field. @@ -491,6 +553,7 @@ For more information, see the following resources: * * +( * * * @@ -503,4 +566,4 @@ For more information, see the following resources: [!INCLUDE[](~/tutorials/first-web-api/includes/first-web-api7.md)] -[!INCLUDE[](~/tutorials/first-web-api/includes/first-web-api8.md)] \ No newline at end of file +[!INCLUDE[](~/tutorials/first-web-api/includes/first-web-api8.md)] diff --git a/aspnetcore/tutorials/first-web-api/_static/9/post.png b/aspnetcore/tutorials/first-web-api/_static/9/post.png new file mode 100644 index 000000000000..c141ae4f2174 Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/post.png differ diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs index 499a297c8315..3fda89919fb6 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs @@ -1,12 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using TodoApi.Models; namespace TodoApi.Controllers { + // [Route("api/[controller]")] [ApiController] public class TodoItemsController : ControllerBase + // { private readonly TodoContext _context; @@ -40,7 +47,7 @@ public async Task> GetTodoItem(long id) // PUT: api/TodoItems/5 // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - // + // [HttpPut("{id}")] public async Task PutTodoItem(long id, TodoItem todoItem) { @@ -69,7 +76,7 @@ public async Task PutTodoItem(long id, TodoItem todoItem) return NoContent(); } - // + // // POST: api/TodoItems // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 @@ -86,6 +93,7 @@ public async Task> PostTodoItem(TodoItem todoItem) // // DELETE: api/TodoItems/5 + // [HttpDelete("{id}")] public async Task DeleteTodoItem(long id) { @@ -100,6 +108,7 @@ public async Task DeleteTodoItem(long id) return NoContent(); } + // private bool TodoItemExists(long id) { diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoContext.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoContext.cs index 9ec9f6dbe6ca..ca83dd27b000 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoContext.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoContext.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; @@ -10,4 +10,4 @@ public TodoContext(DbContextOptions options) } public DbSet TodoItems { get; set; } = null!; -} +} \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoItem.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoItem.cs index 6aa52880ee66..b0d8cb2f9d41 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoItem.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoItem.cs @@ -1,8 +1,8 @@ -namespace TodoApi.Models; +namespace TodoApi.Models; public class TodoItem { public long Id { get; set; } public string? Name { get; set; } public bool IsComplete { get; set; } -} +} \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs index 1fff89e0f7fd..e837f2bf4e37 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs @@ -1,20 +1,64 @@ +#define Final_Add_DBContext // First_Add_SwaggerUI +#if First_Add_SwaggerUI +// +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); + + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/openapi/v1.json", "v1"); + }); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); +// +#endif + +#if Final_Add_DBContext +// using Microsoft.EntityFrameworkCore; using TodoApi.Models; var builder = WebApplication.CreateBuilder(args); +// Add services to the container. + builder.Services.AddControllers(); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("TodoList")); -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); + +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); var app = builder.Build(); +// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.MapOpenApi(); + + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/openapi/v1.json", "v1"); + }); } app.UseHttpsRedirection(); @@ -24,3 +68,5 @@ app.MapControllers(); app.Run(); +// +#endif diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/TodoApi.csproj b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/TodoApi.csproj index 910a83962ccc..9fa6acaede07 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/TodoApi.csproj +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/TodoApi.csproj @@ -1,22 +1,20 @@  - net7.0 + net9.0 enable enable - - - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/WeatherForecast.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/WeatherForecast.cs index 3053f366c4d3..b6eef3eb8911 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/WeatherForecast.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/WeatherForecast.cs @@ -10,4 +10,4 @@ public class WeatherForecast public string? Summary { get; set; } } -} \ No newline at end of file +} diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs index e5eec163d94f..a37b777338d8 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs @@ -1,126 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using TodoApi.Models; -namespace TodoApi.Controllers; - -[Route("api/[controller]")] -[ApiController] -public class TodoItemsController : ControllerBase +namespace TodoApi.Controllers { - private readonly TodoContext _context; - - public TodoItemsController(TodoContext context) - { - _context = context; - } - - // GET: api/TodoItems - [HttpGet] - public async Task>> GetTodoItems() - { - return await _context.TodoItems - .Select(x => ItemToDTO(x)) - .ToListAsync(); - } - - // GET: api/TodoItems/5 - // - [HttpGet("{id}")] - public async Task> GetTodoItem(long id) + [Route("api/[controller]")] + [ApiController] + public class TodoItemsController : ControllerBase { - var todoItem = await _context.TodoItems.FindAsync(id); + private readonly TodoContext _context; - if (todoItem == null) + public TodoItemsController(TodoContext context) { - return NotFound(); + _context = context; } - return ItemToDTO(todoItem); - } - // - - // PUT: api/TodoItems/5 - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - // - [HttpPut("{id}")] - public async Task PutTodoItem(long id, TodoItemDTO todoDTO) - { - if (id != todoDTO.Id) + // GET: api/TodoItems + [HttpGet] + public async Task>> GetTodoItems() { - return BadRequest(); + return await _context.TodoItems + .Select(x => ItemToDTO(x)) + .ToListAsync(); } - var todoItem = await _context.TodoItems.FindAsync(id); - if (todoItem == null) + // GET: api/TodoItems/5 + [HttpGet("{id}")] + public async Task> GetTodoItem(long id) { - return NotFound(); - } + var todoItem = await _context.TodoItems.FindAsync(id); - todoItem.Name = todoDTO.Name; - todoItem.IsComplete = todoDTO.IsComplete; + if (todoItem == null) + { + return NotFound(); + } - try - { - await _context.SaveChangesAsync(); + return ItemToDTO(todoItem); } - catch (DbUpdateConcurrencyException) when (!TodoItemExists(id)) + + // PUT: api/TodoItems/5 + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPut("{id}")] + public async Task PutTodoItem(long id, TodoItemDTO todoItemDTO) { - return NotFound(); + if (id != todoItemDTO.Id) + { + return BadRequest(); + } + + var todoItem = await _context.TodoItems.FindAsync(id); + if (todoItem == null) + { + return NotFound(); + } + + todoItem.Name = todoItemDTO.Name; + todoItem.IsComplete = todoItemDTO.IsComplete; + + _context.Entry(todoItem).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!TodoItemExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); } - return NoContent(); - } - // - - // POST: api/TodoItems - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - // - [HttpPost] - public async Task> PostTodoItem(TodoItemDTO todoDTO) - { - var todoItem = new TodoItem + // POST: api/TodoItems + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + [HttpPost] + public async Task> PostTodoItem(TodoItemDTO todoItemDTO) { - IsComplete = todoDTO.IsComplete, - Name = todoDTO.Name - }; + var todoItem = new TodoItem + { + Name = todoItemDTO.Name, + IsComplete = todoItemDTO.IsComplete + }; - _context.TodoItems.Add(todoItem); - await _context.SaveChangesAsync(); + _context.TodoItems.Add(todoItem); + await _context.SaveChangesAsync(); - return CreatedAtAction( - nameof(GetTodoItem), - new { id = todoItem.Id }, - ItemToDTO(todoItem)); - } - // + return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, ItemToDTO(todoItem)); + } - // DELETE: api/TodoItems/5 - [HttpDelete("{id}")] - public async Task DeleteTodoItem(long id) - { - var todoItem = await _context.TodoItems.FindAsync(id); - if (todoItem == null) + // DELETE: api/TodoItems/5 + [HttpDelete("{id}")] + public async Task DeleteTodoItem(long id) { - return NotFound(); - } + var todoItem = await _context.TodoItems.FindAsync(id); + if (todoItem == null) + { + return NotFound(); + } - _context.TodoItems.Remove(todoItem); - await _context.SaveChangesAsync(); + _context.TodoItems.Remove(todoItem); + await _context.SaveChangesAsync(); - return NoContent(); - } + return NoContent(); + } - private bool TodoItemExists(long id) - { - return _context.TodoItems.Any(e => e.Id == id); - } + private bool TodoItemExists(long id) + { + return _context.TodoItems.Any(e => e.Id == id); + } - private static TodoItemDTO ItemToDTO(TodoItem todoItem) => - new TodoItemDTO - { - Id = todoItem.Id, - Name = todoItem.Name, - IsComplete = todoItem.IsComplete - }; + private static TodoItemDTO ItemToDTO(TodoItem todoItem) => + new TodoItemDTO + { + Id = todoItem.Id, + Name = todoItem.Name, + IsComplete = todoItem.IsComplete + }; + } } diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/WeatherForecastController.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/WeatherForecastController.cs index aacc848b90fd..de25d795aa96 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/WeatherForecastController.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/WeatherForecastController.cs @@ -8,8 +8,8 @@ public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; private readonly ILogger _logger; @@ -30,4 +30,4 @@ public IEnumerable Get() .ToArray(); } } -} \ No newline at end of file +} diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoContext.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoContext.cs index 9ec9f6dbe6ca..ca83dd27b000 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoContext.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoContext.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace TodoApi.Models; @@ -10,4 +10,4 @@ public TodoContext(DbContextOptions options) } public DbSet TodoItems { get; set; } = null!; -} +} \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItem.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItem.cs index 329540542975..99d34ed6e30b 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItem.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItem.cs @@ -1,4 +1,4 @@ -namespace TodoApi.Models; +namespace TodoApi.Models; public class TodoItem { diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItemDTO.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItemDTO.cs index 22793811581a..1a1f39b370db 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItemDTO.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItemDTO.cs @@ -1,8 +1,8 @@ -namespace TodoApi.Models; +namespace TodoApi.Models; public class TodoItemDTO { public long Id { get; set; } public string? Name { get; set; } public bool IsComplete { get; set; } -} +} \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Program.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Program.cs index 1fff89e0f7fd..6b5732e05007 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Program.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/Program.cs @@ -1,20 +1,64 @@ +#define Final_Add_DBContext // First_Add_SwaggerUI || Final_Add_DBContext +#if First_Add_SwaggerUI +// +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); + + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/openapi/v1.json", "v1"); + }); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); +// +#endif + +#if Final_Add_DBContext +// using Microsoft.EntityFrameworkCore; using TodoApi.Models; var builder = WebApplication.CreateBuilder(args); +// Add services to the container. + builder.Services.AddControllers(); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("TodoList")); -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); + +// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi +builder.Services.AddOpenApi(); var app = builder.Build(); +// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.MapOpenApi(); + + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/openapi/v1.json", "v1"); + }); } app.UseHttpsRedirection(); @@ -24,3 +68,5 @@ app.MapControllers(); app.Run(); +// +#endif diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/TodoApi.csproj b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/TodoApi.csproj new file mode 100644 index 000000000000..9fa6acaede07 --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/TodoApi.csproj @@ -0,0 +1,20 @@ + + + + net9.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/WeatherForecast.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/WeatherForecast.cs index 3053f366c4d3..b6eef3eb8911 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/WeatherForecast.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/WeatherForecast.cs @@ -10,4 +10,4 @@ public class WeatherForecast public string? Summary { get; set; } } -} \ No newline at end of file +}