AspNetCore.Docs/aspnetcore/tutorials/min-web-api.md

38 KiB

title author description ms.author ms.date ms.custom monikerRange uid
Tutorial: Create a minimal web API with ASP.NET Core rick-anderson Learn how to build a minimal web API with ASP.NET Core. riande 10/23/2022 engagement-fy23 >= aspnetcore-6.0 tutorials/min-web-api

Tutorial: Create a minimal web API with ASP.NET Core

By Rick Anderson and Tom Dykstra

:::moniker range=">= aspnetcore-7.0"

Minimal APIs are architected to create HTTP APIs with minimal dependencies. They are ideal for microservices and apps that want to include only the minimum files, features, and dependencies in ASP.NET Core.

This tutorial teaches the basics of building a minimal web API with ASP.NET Core. For a tutorial on creating a web API project based on controllers that contains more features, see Create a web API. For a comparison, see Differences between minimal APIs and APIs with controllers later in this tutorial.

Overview

This tutorial creates the following API:

API Description Request body Response body
GET /todoitems Get all to-do items None Array of to-do items
GET /todoitems/complete Get completed to-do items None Array of to-do items
GET /todoitems/{id} Get an item by ID None To-do item
POST /todoitems Add a new item To-do item To-do item
PUT /todoitems/{id} Update an existing item   To-do item None
DELETE /todoitems/{id}     Delete an item     None None

Prerequisites

Visual Studio

[!INCLUDE]

Visual Studio Code

[!INCLUDE]

Visual Studio for Mac

[!INCLUDE]


Create a Web API project

Visual Studio

  • Start Visual Studio 2022 and select Create a new project.

  • In the Create a new project dialog:

    • Enter Empty in the Search for templates search box.
    • Select the ASP.NET Core Empty template and select Next.

    Visual Studio Create a new project

  • Name the project TodoApi and select Next.

  • In the Additional information dialog:

    • Select .NET 7.0
    • Uncheck Do not use top-level statements
    • Select Create

    Additional information

Visual Studio Code

  • Open the integrated terminal.

  • Change directories (cd) to the folder that will contain the project folder.

  • Run the following commands:

    dotnet new web -o TodoApi
    cd TodoApi
    code -r ../TodoApi
    
  • When a dialog box asks if you want to trust the authors, select Yes.

  • When a dialog box asks if you want to add required assets to the project, select Yes.

    The preceding commands create a new web minimal API project and open it in Visual Studio Code.

Visual Studio for Mac

  • In Visual Studio for Mac 2022, select File > New Project....

  • In the Choose a template for your new project dialog:

    • Select Web and Console > App > Empty.
    • Select Continue.

    Visual Studio for Mac Create a new project

  • Make the following selections:

    • Target framework: .NET 7.0 (or later)
    • Configure for HTTPS: Check
    • Do not use top-level statements: Uncheck
    • Select Continue.

    Additional information

  • Enter the following:

    • Project name: TodoApi
    • Solution name: TodoApi
    • Select Create.

Examine the code

The Program.cs file contains the following code:

:::code language="csharp" source="min-web-api/samples/7.x/todo/Program.cs" id="snippet_min":::

The preceding code:

Run the app

Visual Studio

Press Ctrl+F5 to run without the debugger.

[!INCLUDE]

Visual Studio launches the Kestrel web server and opens a browser window.

Visual Studio Code

[!INCLUDE]

Press Ctrl+F5 to run the app. A browser window is opened.

Visual Studio for Mac

Select Debug > Start Debugging to launch the app. Visual Studio for Mac launches a browser and navigates to https://localhost:<port>, where <port> is a randomly chosen port number.


Hello World! is displayed in the browser. The Program.cs file contains a minimal but complete app.

Add NuGet packages

NuGet packages must be added to support the database and diagnostics used in this tutorial.

Visual Studio

  • 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.
  • Follow the preceding instructions to add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore package.

Visual Studio Code

  • Run the following commands:

    dotnet add package Microsoft.EntityFrameworkCore.InMemory
    dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
    

Visual Studio for Mac

  • In the Visual Studio for Mac 2022 toolbar, select Project > Manage NuGet Packages....
  • In the search box, enter Microsoft.EntityFrameworkCore.InMemory.
  • In the results window, check Microsoft.EntityFrameworkCore.InMemory.
  • Select Add Package.
  • In the Select Projects window, select Ok.
  • In the License Agreement window, select Agree.
  • Follow the preceding instructions to add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore package.

The model and database context classes

In the project folder, create a file named Todo.cs with the following code:

:::code language="csharp" source="min-web-api/samples/7.x/todoGroup/Todo.cs":::

The preceding code creates the model for this app. A model is a class that represents data that the app manages.

Create a file named TodoDb.cs with the following code:

:::code language="csharp" source="min-web-api/samples/7.x/todoGroup/TodoDb.cs":::

The preceding code defines the database context, which is the main class that coordinates Entity Framework functionality for a data model. This class derives from the xref:Microsoft.EntityFrameworkCore.DbContext?displayProperty=fullName class.

Add the API code

Replace the contents of the Program.cs file with the following code:

[!code-csharp]

The following highlighted code adds the database context to the dependency injection (DI) container and enables displaying database-related exceptions:

[!code-csharp]

The DI container provides access to the database context and other services.

Install Postman to test the app

This tutorial uses Postman to test the API.

  • Install Postman
  • Start the web app.
  • Start Postman.
  • Disable SSL certificate verification
    • For Postman for Windows, Select File > Settings (General tab), disable SSL certificate verification.
    • For Postman for macOS, Select Postman > Preferences (General tab), disable SSL certificate verification.

      [!WARNING] Re-enable SSL certificate verification after testing the sample app.

Test posting data

The following code in Program.cs creates an HTTP POST endpoint /todoitems that adds data to the in-memory database:

[!code-csharp]

Run the app. The browser displays a 404 error because there is no longer a / endpoint.

Use the POST endpoint to add data to the app:

  • Create a new HTTP request.

  • Set the HTTP method to POST.

  • Set the URI to https://localhost:<port>/todoitems. For example: https://localhost:5001/todoitems

  • Select the Body tab.

  • Select raw.

  • Set the type to JSON.

  • In the request body enter JSON for a to-do item:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Select Send.

    Postman with Post request details

Examine the GET endpoints

The sample app implements several GET endpoints by calling MapGet:

API Description Request body Response body
GET /todoitems Get all to-do items None Array of to-do items
GET /todoitems/complete Get all completed to-do items None Array of to-do items
GET /todoitems/{id} Get an item by ID None To-do item

[!code-csharp]

Test the GET endpoints

Test the app by calling the endpoints from a browser or Postman. The following steps are for Postman.

  • Create a new HTTP request.
  • Set the HTTP method to GET.
  • Set the request URI to https://localhost:<port>/todoitems. For example, https://localhost:5001/todoitems.
  • Select Send.

The call to GET /todoitems produces a response similar to the following:

[
  {
    "id": 1,
    "name": "walk dog",
    "isComplete": false
  }
]
  • Set the request URI to https://localhost:<port>/todoitems/1. For example, https://localhost:5001/todoitems/1.
  • Select Send.
  • The response is similar to the following:
    {
      "id": 1,
      "name": "walk dog",
      "isComplete": false
    }
    

This app uses an in-memory database. If the app is restarted, the GET request doesn't return any data. If no data is returned, POST data to the app and try the GET request again.

Return values

ASP.NET Core automatically serializes the object to JSON and writes the JSON into the body of the response message. The response code for this return type is 200 OK, assuming there are no unhandled exceptions. Unhandled exceptions are translated into 5xx errors.

The return types can represent a wide range of HTTP status codes. For example, GET /todoitems/{id} can return two different status values:

Examine the PUT endpoint

The sample app implements a single PUT endpoint using MapPut:

[!code-csharp]

This method is similar to the MapPost method, except it uses HTTP PUT. A successful response returns 204 (No Content). 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.

Test the PUT endpoint

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.

Update the to-do item that has Id = 1 and set its name to "feed fish":

{
  "id": 1,
  "name": "feed fish",
  "isComplete": false
}

Examine and test the DELETE endpoint

The sample app implements a single DELETE endpoint using MapDelete:

[!code-csharp]

Use Postman to delete a to-do item:

  • Set the method to DELETE.
  • Set the URI of the object to delete (for example https://localhost:5001/todoitems/1).
  • Select Send.

Use the MapGroup API

The sample app code repeats the todoitems URL prefix each time it sets up an endpoint. Web APIs often have groups of endpoints with a common URL prefix, and the xref:Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapGroup%2A method is available to help organize such groups. It reduces repetitive code and allows for customizing entire groups of endpoints with a single call to methods like xref:Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization%2A and xref:Microsoft.AspNetCore.Builder.RoutingEndpointConventionBuilderExtensions.WithMetadata%2A.

Replace the contents of Program.cs with the following code:

:::code language="csharp" source="min-web-api/samples/7.x/todoGroup/Program.cs" id="snippet_all":::

The preceding code has the following changes:

  • Adds var todoItems = app.MapGroup("/todoitems"); to set up the group using the URL prefix /todoitems.
  • Changes all the app.Map<HttpVerb> methods to todoItems.Map<HttpVerb>.
  • Removes the URL prefix /todoitems from the Map<HttpVerb> method calls.

Test the endpoints to verify that they work the same.

Use the TypedResults API

The Map<HttpVerb> methods can call route handler methods instead of using lambdas. To see an example, update Program.cs with the following code:

:::code language="csharp" source="min-web-api/samples/7.x/todoTypedResults/Program.cs" id="snippet_all":::

The Map<HttpVerb> code now calls methods instead of lambdas:

:::code language="csharp" source="min-web-api/samples/7.x/todoTypedResults/Program.cs" id="snippet_group":::

These methods return objects that implement xref:Microsoft.AspNetCore.Http.IResult and are defined by xref:Microsoft.AspNetCore.Http.TypedResults:

:::code language="csharp" source="min-web-api/samples/7.x/todoTypedResults/Program.cs" id="snippet_handlers":::

Unit tests can call these methods and test that they return the correct type. For example, if the method is GetAllTodos:

:::code language="csharp" source="min-web-api/samples/7.x/todoTypedResults/Program.cs" id="snippet_getalltodos":::

Unit test code can verify that an object of type Ok<Todo[]> is returned from the handler method. For example:

public async Task GetAllTodos_ReturnsOkOfTodosResult()
{
    // Arrange
    var db = CreateDbContext();

    // Act
    var result = await TodosApi.GetAllTodos(db);

    // Assert: Check for the correct returned type
    Assert.IsType<Ok<Todo[]>>(result);
}

Prevent over-posting

Currently the sample app exposes the entire Todo object. Production apps typically limit the data that's input and returned using a subset of the model. There are multiple reasons behind this and security is a major one. The subset of a model is usually referred to as a Data Transfer Object (DTO), input model, or view model. DTO is used in this article.

A DTO may be used to:

  • Prevent over-posting.
  • Hide properties that clients are not supposed to view.
  • 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 Todo class to include a secret field:

:::code language="csharp" source="min-web-api/samples/7.x/todoDTO/Todo.cs":::

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 file named TodoItemDTO.cs with the following code:

:::code language="csharp" source="min-web-api/samples/7.x/todoDTO/TodoItemDTO.cs":::

Update the code in Program.cs to use this DTO model:

:::code language="csharp" source="min-web-api/samples/7.x/todoDTO/Program.cs" id="snippet_all":::

Verify you can post and get all fields except the secret field.

Differences between minimal APIs and APIs with controllers

Minimal APIs have:

Next steps

Configure JSON serialization options

For information on how to configure JSON serialization in your Minimal API apps, see Configure JSON serialization options.

Handle errors and exceptions

The developer exception page is enabled by default in the development environment for minimal API apps. For information about how to handle errors and exceptions, see Handle errors in ASP.NET Core web APIs.

Test minimal API apps

For an example of testing a minimal API app, see this GitHub sample.

Use OpenAPI (Swagger)

For information on how to use OpenAPI with minimal API apps, see OpenAPI support in minimal APIs.

Publish to Azure

For information on how to deploy to Azure, see Quickstart: Deploy an ASP.NET web app.

Learn more

For more information about minimal API apps, see xref:fundamentals/minimal-apis.

:::moniker-end

:::moniker range="= aspnetcore-6.0"

Minimal APIs are architected to create HTTP APIs with minimal dependencies. They are ideal for microservices and apps that want to include only the minimum files, features, and dependencies in ASP.NET Core.

This tutorial teaches the basics of building a minimal web API with ASP.NET Core. For a tutorial on creating a web API project based on controllers that contains more features, see Create a web API. For a comparison, see Differences between minimal APIs and APIs with controllers in this document.

Overview

This tutorial creates the following API:

API Description Request body Response body
GET / Browser test, "Hello World" None Hello World!
GET /todoitems Get all to-do items None Array of to-do items
GET /todoitems/complete Get completed to-do items None Array of to-do items
GET /todoitems/{id} Get an item by ID None To-do item
POST /todoitems Add a new item To-do item To-do item
PUT /todoitems/{id} Update an existing item   To-do item None
DELETE /todoitems/{id}     Delete an item     None None

Prerequisites

Visual Studio

[!INCLUDE]

VS22 installer workloads

Visual Studio Code

[!INCLUDE]

Visual Studio for Mac

[!INCLUDE]


Create a Web API project

Visual Studio

  • Start Visual Studio 2022 and select Create a new project.

  • In the Create a new project dialog:

    • Enter API in the Search for templates search box.
    • Select the ASP.NET Core Web API template and select Next. Visual Studio Create a new project
  • Name the project TodoApi and select Next.

  • In the Additional information dialog:

    • Select .NET 6.0 (Long-term support)
    • Remove Use controllers (uncheck to use minimal APIs)
    • Select Create

Additional information

Visual Studio Code

  • Open the integrated terminal.

  • Change directories (cd) to the folder that will contain the project folder.

  • Run the following commands:

    dotnet new webapi -minimal -o TodoApi
    cd TodoApi
    code -r ../TodoApi
    
  • When a dialog box asks if you want to trust the authors, select Yes.

  • When a dialog box asks if you want to add required assets to the project, select Yes.

    The preceding command creates a new web minimal API project and opens it in Visual Studio Code.

Visual Studio for Mac

  • Select File > New Project....

macOS New solution

  • In Visual Studio for Mac 2022, select Web and Console > App > API > Next.

macOS API template selection

In the Configure your new API dialog, make the following selections:

  • Target framework: .NET 6.x (or more recent).
  • Configure for HTTPS: Check
  • Use Controllers (uncheck to use minimal APIs): Uncheck
  • Enable OpenAPI Support: Check

Select Next.

Configure Your New API Window 1

  • In the Configure our new API window, enter the following:
  • Project name: TodoApi
  • Solution name: TodoApi

Select Create.

Configure Your New API Window 2

[!INCLUDE]


Examine the code

The Program.cs file contains the following code:

[!code-csharp]

The project template creates a WeatherForecast API with support for Swagger. Swagger is used to generate useful documentation and help pages for web APIs.

The following highlighted code adds support for Swagger:

[!code-csharp]

Run the app

Visual Studio

Press Ctrl+F5 to run without the debugger.

[!INCLUDE]

Visual Studio launches the Kestrel web server.

Visual Studio Code

[!INCLUDE]

Press Ctrl+F5 to run the app. A browser window is opened. Append /swagger to the URL in the browser, for example https://localhost:7122/swagger.

Visual Studio for Mac

Select Debug > Start Debugging to launch the app. Visual Studio for Mac launches a browser and navigates to https://localhost:<port>, where <port> is a randomly chosen port number.


The Swagger page /swagger/index.html is displayed. Select GET > Try it out> Execute. The page displays:

  • The Curl 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.

Copy and paste the Request URL in the browser: https://localhost:<port>/WeatherForecast. JSON similar to the following is returned:

[
  {
    "date": "2021-10-19T14:12:50.3079024-10:00",
    "temperatureC": 13,
    "summary": "Bracing",
    "temperatureF": 55
  },
  {
    "date": "2021-10-20T14:12:50.3080559-10:00",
    "temperatureC": -8,
    "summary": "Bracing",
    "temperatureF": 18
  },
  {
    "date": "2021-10-21T14:12:50.3080601-10:00",
    "temperatureC": 12,
    "summary": "Hot",
    "temperatureF": 53
  },
  {
    "date": "2021-10-22T14:12:50.3080603-10:00",
    "temperatureC": 10,
    "summary": "Sweltering",
    "temperatureF": 49
  },
  {
    "date": "2021-10-23T14:12:50.3080604-10:00",
    "temperatureC": 36,
    "summary": "Warm",
    "temperatureF": 96
  }
]

Update the generated code

This tutorial focuses on creating a web API, so we'll delete the Swagger code and the WeatherForecast code. Replace the contents of the Program.cs file with the following:

[!code-csharp]

The following highlighted code creates a xref:Microsoft.AspNetCore.Builder.WebApplicationBuilder and a xref:Microsoft.AspNetCore.Builder.WebApplication with preconfigured defaults:

[!code-csharp]

The following code creates an HTTP GET endpoint / which returns Hello World!:

app.MapGet("/", () => "Hello World!");

app.Run(); runs the app.

Remove the two "launchUrl": "swagger", lines from the Properties/launchSettings.json file. When the launchUrl isn't specified, the web browser requests the / endpoint.

Run the app. Hello World! is displayed. The updated Program.cs file contains a minimal but complete app.

Add NuGet packages

NuGet packages must be added to support the database and diagnostics used in this tutorial.

Visual Studio

  • From the Tools menu, select NuGet Package Manager > Manage NuGet Packages for Solution.
  • 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.
  • Follow the preceding instructions to add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore package.

Visual Studio Code

  • Run the following commands:

    dotnet add package Microsoft.EntityFrameworkCore.InMemory
    dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
    

Visual Studio for Mac

  • In the Visual Studio for Mac 2022 toolbar, select Project > Manage NuGet Packages...
  • In the search box, enter Microsoft.EntityFrameworkCore.InMemory
  • In the results window, check Microsoft.EntityFrameworkCore.InMemory.
  • Select Add Package
  • In the Select Projects window, select Ok
  • In the License Agreement window, select Agree

Add the API code

Replace the contents of the Program.cs file with the following code:

[!code-csharp]

The model and database context classes

The sample app contains the following model:

[!code-csharp]

A model is a class that represents data that the app manages. The model for this app is the Todo class.

The sample app contains the following database context class:

[!code-csharp]

The database context is the main class that coordinates Entity Framework functionality for a data model. This class is created by deriving from the xref:Microsoft.EntityFrameworkCore.DbContext?displayProperty=fullName class.

The following highlighted code adds the database context to the dependency injection (DI) container and enables displaying database-related exceptions:

[!code-csharp]

The DI container provides access to the database context and other services.

The following code creates an HTTP POST endpoint /todoitems to add data to the in-memory database:

[!code-csharp]

Install Postman to test the app

This tutorial uses Postman to test the API.

  • Install Postman
  • Start the web app.
  • Start Postman.
  • Disable SSL certificate verification
    • From File > Settings (General tab), disable SSL certificate verification.

      [!WARNING] Re-enable SSL certificate verification after testing the controller.

Test posting data

The following instructions post data to the app:

  • Create a new HTTP request.

  • Set the HTTP method to POST.

  • Set the URI to https://localhost:<port>/todoitems. For example: https://localhost:5001/todoitems

  • Select the Body tab.

  • Select raw.

  • Set the type to JSON.

  • In the request body enter JSON for a to-do item:

    {
      "name":"walk dog",
      "isComplete":true
    }
    
  • Select Send. Postman with Post request details

Examine the GET endpoints

The sample app implements several GET endpoints using calls to MapGet:

API Description Request body Response body
GET / Browser test, "Hello World" None Hello World!
GET /todoitems Get all to-do items None Array of to-do items
GET /todoitems/complete Get all completed to-do items None Array of to-do items
GET /todoitems/{id} Get an item by ID None To-do item

[!code-csharp]

Test the GET endpoints

Test the app by calling the two endpoints from a browser or Postman. For example:

  • GET https://localhost:5001/todoitems
  • GET https://localhost:5001/todoitems/1

The call to GET /todoitems produces a response similar to the following:

[
  {
    "id": 1,
    "name": "Item1",
    "isComplete": false
  }
]

Test the GET endpoints with Postman

  • Create a new HTTP request.
  • Set the HTTP method to GET.
  • Set the request URI to https://localhost:<port>/todoitems. For example, https://localhost:5001/todoitems.
  • Select Send.

This app uses an in-memory database. If the app is restarted, the GET request doesn't return any data. If no data is returned, first POST data to the app.

Return values

ASP.NET Core automatically serializes the object to JSON and writes the JSON into the body of the response message. The response code for this return type is 200 OK, assuming there are no unhandled exceptions. Unhandled exceptions are translated into 5xx errors.

The return types can represent a wide range of HTTP status codes. For example, GET /todoitems/{id} can return two different status values:

Examine the PUT endpoint

The sample app implements a single PUT endpoint using MapPut:

[!code-csharp]

This method is similar to the MapPost method, except it uses HTTP PUT. A successful response returns 204 (No Content). 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.

Test the PUT endpoint

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.

Update the to-do item that has Id = 1 and set its name to "feed fish":

{
  "id": 1,
  "name": "feed fish",
  "isComplete": false
}

Examine the DELETE endpoint

The sample app implements a single DELETE endpoint using MapDelete:

[!code-csharp]

Use Postman to delete a to-do item:

  • Set the method to DELETE.
  • Set the URI of the object to delete (for example https://localhost:5001/todoitems/1).
  • Select Send.

Prevent over-posting

Currently the sample app exposes the entire Todo object. Production apps typically limit the data that's input and returned using a subset of the model. There are multiple reasons behind this and security is a major one. The subset of a model is usually referred to as a Data Transfer Object (DTO), input model, or view model. DTO is used in this article.

A DTO may be used to:

  • Prevent over-posting.
  • Hide properties that clients are not supposed to view.
  • 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 Todo class to include a secret field:

[!code-csharp]

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:

[!code-csharp]

Update the code to use TodoItemDTO:

[!code-csharp]

Verify you can't post or get the secret field.

Differences between minimal APIs and APIs with controllers

Use JsonOptions

The following code uses xref:Microsoft.AspNetCore.Http.Json.JsonOptions:

[!code-csharp]

The following code uses xref:System.Text.Json.JsonSerializerOptions:

[!code-csharp]

The preceding code uses web defaults, which converts property names to camel case.

Test minimal API

For an example of testing a minimal API app, see this GitHub sample.

Publish to Azure

For information on deploying to Azure, see Quickstart: Deploy an ASP.NET web app.

Additional resources

:::moniker-end