diff --git a/aspnetcore/tutorials/first-web-api.md b/aspnetcore/tutorials/first-web-api.md index db4401248098..dafddb8f1d43 100644 --- a/aspnetcore/tutorials/first-web-api.md +++ b/aspnetcore/tutorials/first-web-api.md @@ -1,18 +1,18 @@ --- -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/17/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)] -By [Rick Anderson](https://twitter.com/RickAndMSFT) and [Kirk Larkin](https://twitter.com/serpent5) +By [Tim Deschryver](https://timdeschryver.dev/) and [Rick Anderson](https://twitter.com/RickAndMSFT) :::moniker range=">= aspnetcore-9.0" @@ -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,9 +55,9 @@ 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 **Use controllers (uncheck to use minimal APIs)** is checked. * Select **Create**. ## Add a NuGet package @@ -71,7 +71,7 @@ A NuGet package must be added to support the database used in this tutorial. # [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: @@ -94,9 +94,9 @@ A NuGet package must be added to support the database used in this tutorial. [!INCLUDE[](~/includes/package-reference.md)] -### Test the project +### Run the project -The project template creates a `WeatherForecast` API with support for [Swagger](xref:tutorials/web-api-help-pages-using-swagger). +The project template creates a `WeatherForecast` API with support for [OpenAPI](xref:fundamentals/openapi/overview). # [Visual Studio](#tab/visual-studio) @@ -104,7 +104,58 @@ 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 terminal window and displays the URL of the running app. The API is hosted at `https://localhost:`, where `` is a randomly chosen port number set at the project creation. + + ```output + ... + info: Microsoft.Hosting.Lifetime[14] + Now listening on: https://localhost:7260 + info: Microsoft.Hosting.Lifetime[14] + Now listening on: http://localhost:7261 + info: Microsoft.Hosting.Lifetime[0] + Application started. Press Ctrl+C to shut down. + ... + ``` + +Ctrl+*click* the HTTPS URL in the output to test the web app in a browser. 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 `/weatherforecast` to the URL to test the WeatherForecast API. +The browser displays JSON similar to the following example: + +```json +[ + { + "date": "2025-07-16", + "temperatureC": 52, + "temperatureF": 125, + "summary": "Mild" + }, + { + "date": "2025-07-17", + "temperatureC": 36, + "temperatureF": 96, + "summary": "Warm" + }, + { + "date": "2025-07-18", + "temperatureC": 39, + "temperatureF": 102, + "summary": "Cool" + }, + { + "date": "2025-07-19", + "temperatureC": 10, + "temperatureF": 49, + "summary": "Bracing" + }, + { + "date": "2025-07-20", + "temperatureC": -1, + "temperatureF": 31, + "summary": "Chilly" + } +] +``` # [Visual Studio Code](#tab/visual-studio-code) @@ -129,61 +180,94 @@ Run the app: * 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` - -JSON similar to the following example is returned: +* The default browser is launched to `https://localhost:`, 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 `/weatherforecast` to the URL to test the WeatherForecast API. The browser displays JSON similar to the following example: + ```json [ { - "date": "2019-07-16T19:04:05.7257911-06:00", + "date": "2025-07-16", "temperatureC": 52, "temperatureF": 125, "summary": "Mild" }, { - "date": "2019-07-17T19:04:05.7258461-06:00", + "date": "2025-07-17", "temperatureC": 36, "temperatureF": 96, "summary": "Warm" }, { - "date": "2019-07-18T19:04:05.7258467-06:00", + "date": "2025-07-18", "temperatureC": 39, "temperatureF": 102, "summary": "Cool" }, { - "date": "2019-07-19T19:04:05.7258471-06:00", + "date": "2025-07-19", "temperatureC": 10, "temperatureF": 49, "summary": "Bracing" }, { - "date": "2019-07-20T19:04:05.7258474-06:00", + "date": "2025-07-20", "temperatureC": -1, "temperatureF": 31, "summary": "Chilly" } ] ``` + +* After testing the web app using the following instruction, press Ctrl+C in the integrated terminal to close it. + +--- + +### Test the project + +# [Visual Studio](#tab/visual-studio) + +This tutorial uses [Endpoints Explorer and .http files](xref:test/http-files#use-endpoints-explorer) to test the API. + +# [Visual Studio Code](#tab/visual-studio-code) + +## Create API testing UI with Swagger + +There are many available web API testing tools to choose from, and you can follow this tutorial's introductory API test steps with your preferred tool. + +This tutorial utilizes the .NET package [NSwag.AspNetCore](https://www.nuget.org/packages/NSwag.AspNetCore/), which integrates Swagger tools for generating a testing UI adhering to the OpenAPI specification: + +* NSwag: A .NET library that integrates Swagger directly into ASP.NET Core applications, providing middleware and configuration. +* Swagger: A set of open-source tools such as OpenAPIGenerator and SwaggerUI that generate API testing pages that follow the OpenAPI specification. +* OpenAPI specification: A document that describes the capabilities of the API, based on the XML and attribute annotations within the controllers and models. + +For more information on using OpenAPI and NSwag with ASP.NET, see . + +### Install Swagger tooling + +* Run 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. + +### Configure Swagger middleware + +* In `Program.cs`, add the following highlighted code: + +:::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/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. + +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. + +--- ## Add a model class @@ -195,14 +279,16 @@ A *model* is a set of classes that represent the data that the app manages. The * Right-click the `Models` folder and select **Add** > **Class**. Name the class *TodoItem* and select **Add**. * Replace the template code with the following: +:::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoItem.cs"::: + # [Visual Studio Code](#tab/visual-studio-code) * Add a folder named `Models`. * Add a `TodoItem.cs` file to the `Models` folder with the following code: ---- +:::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoItem.cs"::: - [!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoItem.cs)] +--- The `Id` property functions as the unique key in a relational database. @@ -215,16 +301,18 @@ The *database context* is the main class that coordinates Entity Framework funct # [Visual Studio](#tab/visual-studio) * Right-click the `Models` folder and select **Add** > **Class**. Name the class *TodoContext* and click **Add**. +* Enter the following code: + + :::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoContext.cs"::: # [Visual Studio Code](#tab/visual-studio-code) * Add a `TodoContext.cs` file to the `Models` folder. - ---- - * Enter the following code: - [!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Models/TodoContext.cs)] + :::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoContext.cs"::: + +--- ## Register the database context @@ -232,7 +320,7 @@ In ASP.NET Core, services such as the DB context must be registered with the [de 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 language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Program.cs" highlight="1-2,8-9"::: The preceding code: @@ -255,11 +343,14 @@ The preceding code: If the scaffolding operation fails, select **Add** to try scaffolding a second time. +This step adds the `Microsoft.VisualStudio.Web.CodeGeneration.Design` and `Microsoft.EntityFrameworkCore.Tools` NuGet packages to the project. +These packages are required for scaffolding. + # [Visual Studio Code](#tab/visual-studio-code) Make sure that all of your changes so far are saved. -* Control-click the **TodoAPI** project and select **Open in Terminal**. The terminal opens at the `TodoAPI` project folder. +* Right-click (or Command-click on macOS) the **TodoAPI** project and select **Open in Terminal**. The terminal opens at the `TodoAPI` project folder. Run the following commands: ```dotnetcli @@ -314,7 +405,7 @@ When the `[action]` token isn't in the route template, the [action](xref:mvc/con 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 language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs" id="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. @@ -330,31 +421,197 @@ The method: ### Test PostTodoItem -* Press Ctrl+F5 to run the app. -* In the Swagger browser window, select **POST /api/TodoItems**, and then select **Try it out**. -* In the **Request body** input window, update the JSON. For example, +# [Visual Studio](#tab/visual-studio) + +* Select **View** > **Other Windows** > **Endpoints Explorer**. +* Right-click the **POST** endpoint and select **Generate request**. + + ![Endpoints Explorer context menu highlighting Generate Request menu item.](~/tutorials/first-web-api/_static/9/generate-request-vs17.13.0.png) + + A new file is created in the project folder named `TodoApi.http`, with contents similar to the following example: + + ``` + @TodoApi_HostAddress = https://localhost:49738 + + POST {{TodoApi_HostAddress}}/api/todoitems + Content-Type: application/json + + { + //TodoItem + } + + ### + ``` + + * The first line creates a variable that is used for all of the endpoints. + * The next line defines a POST request. + * The lines after the POST request line defines the headers, and a placeholder for the request body. + * The triple hashtag (`###`) line is a request delimiter: what comes after it is for a different request. + +* The POST request expects a `TodoItem`. To define the todo, replace the `//TodoItem` comment with the following JSON: + + ```json + { + "name": "walk dog", + "isComplete": true + } + ``` + + The TodoApi.http file should now look like the following example, but with your port number: + + ``` + @TodoApi_HostAddress = https://localhost:7260 + + Post {{TodoApi_HostAddress}}/api/todoitems + Content-Type: application/json - ```JSON { "name": "walk dog", "isComplete": true } + + ### ``` -* Select **Execute** +* Run the app. + +* Select the **Send request** link that is above the `POST` request line. + + ![.http file window with run link highlighted.](~/tutorials/first-web-api/_static/9/http-file-run-button-vs17.13.0.png) - ![Swagger POST](~/tutorials/first-web-api/_static/7/post.png) + The POST request is sent to the app and the response is displayed in the **Response** pane. + + ![.http file window with response from the POST request.](~/tutorials/first-web-api/_static/9/http-file-window-with-response-vs17.13.0.png) + +# [Visual Studio Code](#tab/visual-studio-code) + +* With the app still running, in the browser, navigate to `https://localhost:/swagger` to display the API testing page generated by Swagger. Click on **TodoItems** to expand the operations. + + ![Swagger generated API testing page](~/tutorials/first-web-api/_static/9/swagger.png) + +* On the Swagger API testing page, select **Post /api/todoitems** > **Try it out**. +* Note that the **Request body** field contains a generated example format reflecting the parameters for the API. +* In the request body enter JSON for a to-do item, without specifying the optional `id`: + + ```json + { + "name": "walk dog", + "isComplete": true + } + ``` + +* Select **Execute**. + + ![Swagger with Post request](~/tutorials/first-web-api/_static/9/swagger-post.png) + +* Swagger provides a **Responses** pane below the **Execute** button. + + ![Swagger with Post response](~/tutorials/first-web-api/_static/9/swagger-post-response.png) + +Note a few of the useful details: + +* cURL: Swagger provides an example cURL command in Unix/Linux syntax, which can be run at the command line with any bash shell that uses Unix/Linux syntax, including Git Bash from [Git for Windows](https://git-scm.com/downloads). +* Request URL: A simplified representation of the HTTP request made by Swagger UI's JavaScript code for the API call. Actual requests can include details such as headers and query parameters and a request body. +* Server response: Includes the response body and headers. The response body shows the `id` was set to `1`. +* Response Code: A 201 `HTTP` status code was returned, indicating that the request was successfully processed and resulted in the creation of a new resource. + +--- ### Test the location header URI -In the preceding POST, the Swagger UI shows the [location header](https://developer.mozilla.org/docs/Web/HTTP/Headers/Location) under **Response headers**. For example, `location: https://localhost:7260/api/TodoItems/1`. The location header shows the URI to the created resource. +# [Visual Studio](#tab/visual-studio) + +Test the app by calling the `GET` endpoints from a browser or by using **Endpoints Explorer**. The following steps are for **Endpoints Explorer**. + +* In **Endpoints Explorer**, right-click the first **GET** endpoint, and select **Generate request**. + + The following content is added to the `TodoApi.http` file: + + ``` + GET {{TodoApi_HostAddress}}/api/todoitems + + ### + ``` + +* Select the **Send request** link that is above the new `GET` request line. + + The GET request is sent to the app and the response is displayed in the **Response** pane. + +* The response body is similar to the following JSON: + + ```json + [ + { + "id": 1, + "name": "walk dog", + "isComplete": true + } + ] + ``` + +* In **Endpoints Explorer**, right-click the `/api/todoitems/{id}` **GET** endpoint and select **Generate request**. + The following content is added to the `TodoApi.http` file: + + ``` + @id=0 + GET {{TodoApi_HostAddress}}/api/todoitems/{{id}} + + ### + ``` + +* Assign `{@id}` to `1` (instead of `0`). + +* Select the **Send request** link that is above the new GET request line. + + The GET request is sent to the app and the response is displayed in the **Response** pane. + +* The response body is similar to the following JSON: + + ```json + { + "id": 1, + "name": "walk dog", + "isComplete": true + } + ``` + +# [Visual Studio Code](#tab/visual-studio-code) -To test the location header: +Test the app by calling the endpoints from a browser or Swagger. -* In the Swagger browser window, select **GET /api/TodoItems/{id}**, and then select **Try it out**. -* Enter `1` in the `id` input box, and then select **Execute**. +* In Swagger select **GET /api/todoitems** > **Try it out** > **Execute**. - ![Swagger GET](~/tutorials/first-web-api/_static/7/get.png) +* Alternatively, call **GET /api/todoitems** from a browser by entering the URI `https://localhost:/api/todoitems`. For example, `https://localhost:7260/api/todoitems` + +The call to `GET /api/todoitems` produces a response similar to the following: + +```json +[ + { + "id": 1, + "name": "walk dog", + "isComplete": true + } +] +``` + +* Call **GET /api/todoitems/{id}** in Swagger to return data from a specific id: + * Select **GET /api/todoitems** > **Try it out**. + * Set the **id** field to `1` and select **Execute**. + +* Alternatively, call **GET /api/todoitems** from a browser by entering the URI `https://localhost:/api/todoitems/1`. For example, `https://localhost:7260/api/todoitems/1` + +* The response is similar to the following: + + ```json + { + "id": 1, + "name": "walk dog", + "isComplete": true + } + ``` + +--- ## Examine the GET methods @@ -375,14 +632,14 @@ 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 language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs" id="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). 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. -[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_GetByID&highlight=1-2)] +:::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs" id="snippet_GetByID" highlight="1-2"::: ## Return values @@ -397,7 +654,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 language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs" id="snippet_Update" ::: `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,30 +662,113 @@ 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). +Use the `PUT` method 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). + +# [Visual Studio](#tab/visual-studio) + +* In **Endpoints Explorer**, right-click the **PUT** endpoint, and select **Generate request**. + + The following content is added to the `TodoApi.http` file: + + ``` + PUT {{TodoApi_HostAddress}}/api/todoitems/{{id}} + Content-Type: application/json + + { + //TodoItem + } + + ### + ``` + +* In the PUT request line, replace `{{id}}` with `1`. + +* Replace the `//TodoItem` placeholder with the following lines: + + ``` + PUT {{TodoApi_HostAddress}}/api/todoitems/1 + Content-Type: application/json + + { + "name": "feed fish", + "isComplete": false + } + ``` + +* Select the **Send request** link that is above the new PUT request line. + + The PUT request is sent to the app and the response is displayed in the **Response** pane. The response body is empty, and the status code is 204. + +# [Visual Studio Code](#tab/visual-studio-code) + +Use Swagger to send a PUT request: + +* Select **Put /api/todoitems/{id}** > **Try it out**. + +* Set the **id** field to `1`. + +* Set the request body to the following JSON: + + ```json + { + "name": "feed fish", + "isComplete": false + } + ``` + +* Select **Execute**. + +--- ## The DeleteTodoItem method Examine the `DeleteTodoItem` method: -[!code-csharp[](~/tutorials/first-web-api/samples/6.0/TodoApi/Controllers/TodoItemsController.cs?name=snippet_Delete)] +:::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/TodoItemsController.cs" id="snippet_Delete" ::: ### 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 `DELETE` method 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). + +# [Visual Studio](#tab/visual-studio) + +* In **Endpoints Explorer**, right-click the **DELETE** endpoint and select **Generate request**. + + A DELETE request is added to `TodoApi.http`. + +* Replace `{{id}}` in the DELETE request line with `1`. The DELETE request should look like the following example: + + ``` + DELETE {{TodoApi_HostAddress}}/api/todoitems/{{id}} + + ### + ``` + +* Select the **Send request** link for the DELETE request. + + The DELETE request is sent to the app and the response is displayed in the **Response** pane. The response body is empty, and the status code is 204. + +# [Visual Studio Code](#tab/visual-studio-code) + +Use Swagger to send a DELETE request: + +* Select **DELETE /api/todoitems/{id}** > **Try it out**. +* Set the **ID** field to `1` and select **Execute**. + + The DELETE request is sent to the app and the response is displayed in the **Responses** pane. The response body is empty, and the **Server response** status code is 204. + +--- ## Test with other tools There are many other tools that can be used to test web APIs, for example: -* [Visual Studio Endpoints Explorer and .http files](xref:test/http-files) * [http-repl](xref:web-api/http-repl) * [curl](https://terminalcheatsheet.com/guides/curl-rest-api). Swagger uses `curl` and shows the `curl` commands it submits. * [Fiddler](https://www.telerik.com/fiddler) For more information, see: -* [Minimal API tutorial: test with .http files and Endpoints Explorer](xref:tutorials/min-web-api) * [Install and test APIs with `http-repl`](xref:tutorials/first-web-api?view=aspnetcore-6.0&preserve-view=true#ihr6) @@ -447,19 +787,19 @@ A DTO may be used to: 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 language="csharp" source="~/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)] +:::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApiDTO/Models/TodoItemDTO.cs" ::: Update the `TodoItemsController` to use `TodoItemDTO`: -[!code-csharp[](~/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs)] +:::code language="csharp" source="~/tutorials/first-web-api/samples/9.0/TodoApiDTO/Controllers/TodoItemsController.cs" ::: Verify you can't post or get the secret field. @@ -491,6 +831,7 @@ For more information, see the following resources: * * +* * * * @@ -502,5 +843,4 @@ For more information, see the following resources: :::moniker-end [!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/generate-request-vs17.13.0.png b/aspnetcore/tutorials/first-web-api/_static/9/generate-request-vs17.13.0.png new file mode 100644 index 000000000000..5ee2b67ec4a6 Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/generate-request-vs17.13.0.png differ diff --git a/aspnetcore/tutorials/first-web-api/_static/9/http-file-run-button-vs17.13.0.png b/aspnetcore/tutorials/first-web-api/_static/9/http-file-run-button-vs17.13.0.png new file mode 100644 index 000000000000..65a85c6855db Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/http-file-run-button-vs17.13.0.png differ diff --git a/aspnetcore/tutorials/first-web-api/_static/9/http-file-window-with-response-vs17.13.0.png b/aspnetcore/tutorials/first-web-api/_static/9/http-file-window-with-response-vs17.13.0.png new file mode 100644 index 000000000000..701a5597760e Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/http-file-window-with-response-vs17.13.0.png differ diff --git a/aspnetcore/tutorials/first-web-api/_static/9/swagger-get.png b/aspnetcore/tutorials/first-web-api/_static/9/swagger-get.png new file mode 100644 index 000000000000..ff1501fbf07f Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/swagger-get.png differ diff --git a/aspnetcore/tutorials/first-web-api/_static/9/swagger-post-response.png b/aspnetcore/tutorials/first-web-api/_static/9/swagger-post-response.png new file mode 100644 index 000000000000..5cd8c2dff3c0 Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/swagger-post-response.png differ diff --git a/aspnetcore/tutorials/first-web-api/_static/9/swagger-post.png b/aspnetcore/tutorials/first-web-api/_static/9/swagger-post.png new file mode 100644 index 000000000000..c82252089af5 Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/swagger-post.png differ diff --git a/aspnetcore/tutorials/first-web-api/_static/9/swagger.png b/aspnetcore/tutorials/first-web-api/_static/9/swagger.png new file mode 100644 index 000000000000..46b1501c51e4 Binary files /dev/null and b/aspnetcore/tutorials/first-web-api/_static/9/swagger.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..d023a04b417d 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 @@ -2,108 +2,111 @@ using Microsoft.EntityFrameworkCore; using TodoApi.Models; -namespace TodoApi.Controllers +namespace TodoApi.Controllers; + +// +[Route("api/[controller]")] +[ApiController] +public class TodoItemsController : ControllerBase +// { - [Route("api/[controller]")] - [ApiController] - public class TodoItemsController : ControllerBase + private readonly TodoContext _context; + + public TodoItemsController(TodoContext context) { - private readonly TodoContext _context; + _context = context; + } - public TodoItemsController(TodoContext context) - { - _context = context; - } + // GET: api/TodoItems + [HttpGet] + public async Task>> GetTodoItems() + { + return await _context.TodoItems.ToListAsync(); + } + + // GET: api/TodoItems/5 + // + [HttpGet("{id}")] + public async Task> GetTodoItem(long id) + { + var todoItem = await _context.TodoItems.FindAsync(id); - // GET: api/TodoItems - [HttpGet] - public async Task>> GetTodoItems() + if (todoItem == null) { - return await _context.TodoItems.ToListAsync(); + return NotFound(); } - // GET: api/TodoItems/5 - // - [HttpGet("{id}")] - public async Task> GetTodoItem(long id) + return 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, TodoItem todoItem) + { + if (id != todoItem.Id) { - var todoItem = await _context.TodoItems.FindAsync(id); + return BadRequest(); + } - if (todoItem == null) - { - return NotFound(); - } + _context.Entry(todoItem).State = EntityState.Modified; - return todoItem; + try + { + await _context.SaveChangesAsync(); } - // - - // 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) + catch (DbUpdateConcurrencyException) { - if (id != todoItem.Id) + if (!TodoItemExists(id)) { - return BadRequest(); - } - - _context.Entry(todoItem).State = EntityState.Modified; - - try - { - await _context.SaveChangesAsync(); + return NotFound(); } - catch (DbUpdateConcurrencyException) + else { - if (!TodoItemExists(id)) - { - return NotFound(); - } - else - { - throw; - } + throw; } - - return NoContent(); } - // - // POST: api/TodoItems - // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 - // - [HttpPost] - public async Task> PostTodoItem(TodoItem todoItem) - { - _context.TodoItems.Add(todoItem); - await _context.SaveChangesAsync(); + return NoContent(); + } + // - // return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem); - return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem); - } - // + // POST: api/TodoItems + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + // + [HttpPost] + public async Task> PostTodoItem(TodoItem todoItem) + { + _context.TodoItems.Add(todoItem); + await _context.SaveChangesAsync(); - // DELETE: api/TodoItems/5 - [HttpDelete("{id}")] - public async Task DeleteTodoItem(long id) + // return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem); + return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem); + } + // + + // DELETE: api/TodoItems/5 + // + [HttpDelete("{id}")] + public async Task DeleteTodoItem(long id) + { + var todoItem = await _context.TodoItems.FindAsync(id); + if (todoItem == null) { - var todoItem = await _context.TodoItems.FindAsync(id); - if (todoItem == null) - { - return NotFound(); - } + 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); } -} +} \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/WeatherForecastController.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/WeatherForecastController.cs index de25d795aa96..459170b08746 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/WeatherForecastController.cs +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/Controllers/WeatherForecastController.cs @@ -1,33 +1,32 @@ using Microsoft.AspNetCore.Mvc; -namespace TodoApi.Controllers +namespace TodoApi.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase { - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase + private static readonly string[] Summaries = new[] { - 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; + private readonly ILogger _logger; - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); - } } -} +} \ 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..e80da9ed3207 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 @@ -4,17 +4,15 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); +builder.Services.AddOpenApi(); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("TodoList")); -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.MapOpenApi(); } app.UseHttpsRedirection(); 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..c3195840eb02 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/TodoApi.http b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/TodoApi.http new file mode 100644 index 000000000000..bce11d0b8309 --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi/TodoApi.http @@ -0,0 +1,33 @@ +@TodoApi_HostAddress = https://localhost:7260 + +Post {{TodoApi_HostAddress}}/api/todoitems +Content-Type: application/json + +{ + "name":"walk dog", + "isComplete":true +} + +### + +GET {{TodoApi_HostAddress}}/api/todoitems + +### + +@id=0 +GET {{TodoApi_HostAddress}}/api/todoitems/{{id}} + +### + +PUT {{TodoApi_HostAddress}}/api/todoitems/{{id}} +Content-Type: application/json + +{ + //TodoItem +} + +### + +DELETE {{TodoApi_HostAddress}}/api/todoitems/{{id}} + +### 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..c6ddbffa3b47 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 @@ -1,13 +1,12 @@ -namespace TodoApi +namespace TodoApi; + +public class WeatherForecast { - public class WeatherForecast - { - public DateOnly Date { get; set; } + public DateOnly Date { get; set; } - public int TemperatureC { get; set; } + public int TemperatureC { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - public string? Summary { get; set; } - } -} \ No newline at end of file + public string? Summary { get; set; } +} 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..459170b08746 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 @@ -1,33 +1,32 @@ using Microsoft.AspNetCore.Mvc; -namespace TodoApi.Controllers +namespace TodoApi.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase { - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase + private static readonly string[] Summaries = new[] { - private static readonly string[] Summaries = new[] - { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - private readonly ILogger _logger; + private readonly ILogger _logger; - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); - } } } \ 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..e80da9ed3207 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 @@ -4,17 +4,15 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); +builder.Services.AddOpenApi(); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("TodoList")); -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.MapOpenApi(); } app.UseHttpsRedirection(); diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/TodoApiDTO.csproj b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/TodoApiDTO.csproj index 910a83962ccc..c3195840eb02 100644 --- a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/TodoApiDTO.csproj +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/TodoApiDTO.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/TodoApiDTO/WeatherForecast.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApiDTO/WeatherForecast.cs index 3053f366c4d3..0d5ca76a281f 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 @@ -1,13 +1,12 @@ -namespace TodoApi +namespace TodoApi; + +public class WeatherForecast { - public class WeatherForecast - { - public DateOnly Date { get; set; } + public DateOnly Date { get; set; } - public int TemperatureC { get; set; } + public int TemperatureC { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - public string? Summary { get; set; } - } + public string? Summary { get; set; } } \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Controllers/TodoItemsController.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Controllers/TodoItemsController.cs new file mode 100644 index 000000000000..9b2d75a6cc40 --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Controllers/TodoItemsController.cs @@ -0,0 +1,108 @@ +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; + + public TodoItemsController(TodoContext context) + { + _context = context; + } + + // GET: api/TodoItems + [HttpGet] + public async Task>> GetTodoItems() + { + return await _context.TodoItems.ToListAsync(); + } + + // GET: api/TodoItems/5 + // + [HttpGet("{id}")] + public async Task> GetTodoItem(long id) + { + var todoItem = await _context.TodoItems.FindAsync(id); + + if (todoItem == null) + { + return NotFound(); + } + + return 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, TodoItem todoItem) + { + if (id != todoItem.Id) + { + return BadRequest(); + } + + _context.Entry(todoItem).State = EntityState.Modified; + + try + { + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!TodoItemExists(id)) + { + return NotFound(); + } + else + { + throw; + } + } + + return NoContent(); + } + // + + // POST: api/TodoItems + // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754 + // + [HttpPost] + public async Task> PostTodoItem(TodoItem todoItem) + { + _context.TodoItems.Add(todoItem); + await _context.SaveChangesAsync(); + + // return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem); + return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem); + } + // + + // DELETE: api/TodoItems/5 + [HttpDelete("{id}")] + public async Task DeleteTodoItem(long id) + { + var todoItem = await _context.TodoItems.FindAsync(id); + if (todoItem == null) + { + return NotFound(); + } + + _context.TodoItems.Remove(todoItem); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + private bool TodoItemExists(long id) + { + return _context.TodoItems.Any(e => e.Id == id); + } +} \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Controllers/WeatherForecastController.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Controllers/WeatherForecastController.cs new file mode 100644 index 000000000000..459170b08746 --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Controllers/WeatherForecastController.cs @@ -0,0 +1,32 @@ +using Microsoft.AspNetCore.Mvc; + +namespace TodoApi.Controllers; + +[ApiController] +[Route("[controller]")] +public class WeatherForecastController : ControllerBase +{ + private static readonly string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + private readonly ILogger _logger; + + public WeatherForecastController(ILogger logger) + { + _logger = logger; + } + + [HttpGet(Name = "GetWeatherForecast")] + public IEnumerable Get() + { + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = Summaries[Random.Shared.Next(Summaries.Length)] + }) + .ToArray(); + } +} \ No newline at end of file diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoContext.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoContext.cs new file mode 100644 index 000000000000..9ec9f6dbe6ca --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoContext.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore; + +namespace TodoApi.Models; + +public class TodoContext : DbContext +{ + public TodoContext(DbContextOptions options) + : base(options) + { + } + + public DbSet TodoItems { get; set; } = null!; +} diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoItem.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoItem.cs new file mode 100644 index 000000000000..6aa52880ee66 --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Models/TodoItem.cs @@ -0,0 +1,8 @@ +namespace TodoApi.Models; + +public class TodoItem +{ + public long Id { get; set; } + public string? Name { get; set; } + public bool IsComplete { get; set; } +} diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Program.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Program.cs new file mode 100644 index 000000000000..af0911653622 --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/Program.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using TodoApi.Models; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); +builder.Services.AddOpenApi(); +builder.Services.AddDbContext(opt => + opt.UseInMemoryDatabase("TodoList")); + +// +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); + app.UseSwaggerUi(options => + { + options.DocumentPath = "/openapi/v1.json"; + }); +} +// + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/TodoApi.csproj b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/TodoApi.csproj new file mode 100644 index 000000000000..96b2febe584d --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/TodoApi.csproj @@ -0,0 +1,21 @@ + + + + net9.0 + enable + enable + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/WeatherForecast.cs b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/WeatherForecast.cs new file mode 100644 index 000000000000..c6ddbffa3b47 --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/WeatherForecast.cs @@ -0,0 +1,12 @@ +namespace TodoApi; + +public class WeatherForecast +{ + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + + public string? Summary { get; set; } +} diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/appsettings.Development.json b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/appsettings.Development.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/appsettings.json b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/appsettings.json new file mode 100644 index 000000000000..10f68b8c8b4f --- /dev/null +++ b/aspnetcore/tutorials/first-web-api/samples/9.0/TodoApi_SwaggerVersion/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +}