Visual Studio 2022中的Web API开发

目录

新功能的视频概述

开始

Contact.cs

Visual Studio中的脚手架

Visual Studio中的实体框架支持

端点资源管理器

HTTP编辑器

HTTP文件语法

注释

变量

请求行

同一文件中的多个请求

Headers

Visual Studio HTTP编辑器当前不支持

下一步


Visual Studio 2022中,Web开发人员的主要方案之一是使用ASP.NET Core创建Web API。在Visual Studio 2022 17.6的最新预览版中,我们添加了许多更新,以便在开发API时更加高效。在这篇文章中,我们将介绍从头开始开发新API的示例场景,并指出在此过程中的新功能。若要开始,请使用以下链接下载Visual Studio 2022的最新预览版。

下载最新的 Visual Studio 2022 预览版

我们将在这篇文章中介绍的一些新功能包括。

  • 具有集成客户端的HTTP编辑器
  • API端点资源管理器(Endpoints Explorer)
  • 脚手架
  • Visual Studio中的实体框架(EF)工具

在这篇文章中,我们将展示一个完整的端到端,从一个新项目开始,开发一个完整的Web API。以下是这篇文章的简要概述。

  • 开始——创建新的API项目
  • 将模型添加到项目中
  • 使用脚手架生成API
  • 使用端点资源管理器探索API
  • 使用HTTP编辑器练习API

所有代码都可以在 sayedihashimi/RestaurantService:Sample ASP.NET Core Web API(github.com) 中找到。

新功能的视频概述

请看Mads Kristensen的这段视频,该视频涵盖了我们将在这篇博文中介绍的一些新更新。

 https://www.youtube.com/embed/ud0wx5mgniI

开始

若要开始在Visual Studio中开发Web API,第一步是创建一个新项目。在Visual Studio 2022中,可以使用新建项目对话框创建新项目。在这篇文章中,我们将为一家虚构的外卖餐厅创建一个ASP.NET Core Web API。若要按照本教程操作,请创建一个名为 MyRestaurantService 的项目,并在其他信息页中选择以下选项。

在此示例中,我们使用的是API端点,而不是基于ControllerAPI,但您可以按照Controller进行操作。有关基于控制器的API和终结点API之间差异的详细信息,请参阅此文档在基于控制器的API和最小API之间进行选择API端点和基于控制器的API的步骤是相同的,只是在适用时选择了Controller选项而不是Endpoints

现在已经创建了项目,我们要做的第一件事是为我们想要使用API公开的对象添加一些模型类。我们需要将以下类/枚举添加到项目中。在下表中,您将看到要创建的对象列表以及指向每个文件的源的链接。

名字

描述和链接

Contact

此类将捕获表示下订单的客户。源文件

MenuItem

表示餐厅菜单中的菜单项。源文件

MenuItemCategory

表示菜单项的类别的枚举。源文件

MenuItemOrdered

表示已添加到客户订单的菜单项。源文件

OrderStatus

表示已提交订单的状态。源文件

PaymentMethod

表示用于订单的付款方式的枚举。源文件

TogoOrder

表示已下达的订单。源文件

添加的文件要么是标准POCO类,要么是枚举。例如,下面是Contact类的定义方式。

Contact.cs

namespace MyRestaurantApi; 
public class Contact {
    public int Id { get; set; }
    public string? Name { get; set; }
    public string? Email { get; set; }
    public string? Phone { get; set; }
}

这些对象表示一组数据,可用于向最终用户显示菜单以及提交TogoOrder。现在,我们已经添加了这些模型类,下一步是在Visual Studio中使用脚手架来创建API终结点并连接实体框架DbContext。我们需要创建4API端点。

  • Contact
  • MenuItem
  • MenuItemsOrdered
  • TogoOrder

对于每个模型类,我们将使用脚手架来处理生成初始API端点的繁重工作。让我们开始吧。

Visual Studio中的脚手架

若要开始使用脚手架,请右键单击ASP.NET Core Web项目MyRestaurantApi,然后选择添加>新脚手架项。见下图。

调用此菜单项后,将出现脚手架对话框。在此对话框中,我们将选择要运行的脚手架类型,然后对其进行配置。对于此项目,我们将使用实体框架脚手架将API与读/写终结点配合使用。

选择此选项后,您将看到一个新对话框,提示您配置脚手架选项。下表总结了主要选项。

选择

描述

模型类

将用于脚手架的模型类。脚手架将生成API来读取/写入所选模型类的值。

终结点类

应将新终结点写入到的类。如果选择现有类,终结点将添加到该类中。您还可以使用+按钮创建一个新类。

DbContext 类

实体框架DbContext,用于管理对数据库的读/写操作。可以使用+按钮创建新的DbContext。

数据库提供程序

要使用的数据库提供程序,这将由要存储此数据的数据库确定。

我们将从接触模型的脚手架开始。在本例中,我们将Contact类指定为模型类,创建一个新的端点类以及一个新的DbContext。对于此示例,我们将使用Sqlite,但您可以根据需要选择列出的任何提供程序。下面是配置了这些选项的对话框。

填充后,按钮用于创建新的终结点类以及DbContext。您可以使用在单击这两个按钮的 + 按钮时提供的默认值。下一步是单击添加按钮。单击添加后,将使用NuGet安装脚手架工具,然后调用它来生成新文件。下面列出了添加或修改的文件。

项目文件——修改项目文件以添加支持更改所需的包引用以及脚手架工具本身的包引用。

Program.cs——修改Program.cs文件以添加EF DbContext以及注册新的终结点类。

appSettings.json——修改appSettings.json文件以添加连接字符串。

ContactEndpoints.cs——这是添加的处理API请求/响应的新类。

下面是已生成到ContactEndpoints类的代码。

using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.OpenApi;
using MyRestaurantApi.Data;
namespace MyRestaurantApi;

public static class ContactEndpoints
{
    public static void MapContactEndpoints (this IEndpointRouteBuilder routes)
    {
        var group = routes.MapGroup("/api/Contact").WithTags(nameof(Contact));

        group.MapGet("/", async (MyRestaurantApiContext db) =>
        {
            return await db.Contact.ToListAsync();
        })
        .WithName("GetAllContacts")
        .WithOpenApi();

        group.MapGet("/{id}", async Task<Results<Ok<Contact>, NotFound>> (int id, MyRestaurantApiContext db) =>
        {
            return await db.Contact.AsNoTracking()
                .FirstOrDefaultAsync(model => model.Id == id)
                is Contact model
                    ? TypedResults.Ok(model)
                    : TypedResults.NotFound();
        })
        .WithName("GetContactById")
        .WithOpenApi();

        group.MapPut("/{id}", async Task<Results<Ok, NotFound>> (int id, Contact contact, MyRestaurantApiContext db) =>
        {
            var affected = await db.Contact
                .Where(model => model.Id == id)
                .ExecuteUpdateAsync(setters => setters
                  .SetProperty(m => m.Id, contact.Id)
                  .SetProperty(m => m.Name, contact.Name)
                  .SetProperty(m => m.Email, contact.Email)
                  .SetProperty(m => m.Phone, contact.Phone)
                );

            return affected == 1 ? TypedResults.Ok() : TypedResults.NotFound();
        })
        .WithName("UpdateContact")
        .WithOpenApi();

        group.MapPost("/", async (Contact contact, MyRestaurantApiContext db) =>
        {
            db.Contact.Add(contact);
            await db.SaveChangesAsync();
            return TypedResults.Created($"/api/Contact/{contact.Id}",contact);
        })
        .WithName("CreateContact")
        .WithOpenApi();

        group.MapDelete("/{id}", async Task<Results<Ok, NotFound>> (int id, MyRestaurantApiContext db) =>
        {
            var affected = await db.Contact
                .Where(model => model.Id == id)
                .ExecuteDeleteAsync();

            return affected == 1 ? TypedResults.Ok() : TypedResults.NotFound();
        })
        .WithName("DeleteContact")
        .WithOpenApi();
    }
}

这里使用ASP.NET Core最小API支持,如前所述,如果您更喜欢基于ControllerAPI,则新的Controller将添加到Controllers文件夹中。

现在,我们已经为Contact类生成了API端点,我们需要对其余三个模型类执行相同的操作。我们将重复相同的步骤,但有一个区别。我们将选择在此步骤中创建的DbContext以重用于其他三个DbContext,而不是选择创建新的DbContext。由于要选择现有的DbContext,因此将禁用数据库提供程序选项,因为它已在DbContext中配置。现在,您可以为类MenuItemMenuItemsOrderedTogoOrder搭建API端点的脚手架。

完成这三个文件的脚手架后,解决方案资源管理器应如下图所示。

此时,最好构建解决方案以确保没有问题。在此步骤中,我们使用Visual Studio脚手架创建一组连接到实体框架数据库的API终结点。下一步是设置数据库。

现在我们有了DbContext,我们想要向数据库添加一些初始数据,这通常称为数据种子设定。在我们的例子中,我们需要填充客户可以订购的项目列表(MenuItems)以及联系人表。我们可以通过自定义在脚手架期间生成的DbContext类来做到这一点。我们将添加两个方法来返回数据,并将重写OnModelCreating方法以将数据注册到数据库。我们将添加的用于返回初始数据的方法包括GetSeedDataMenuItemsGetSeedDataContacts。这些方法分别返回MenuItemsContacts的数组。然后,我们在OnModelCreating方法中调用这些方法。代码现在应如下所示。

public class MyRestaurantApiContext : DbContext {
    public MyRestaurantApiContext(DbContextOptions<MyRestaurantApiContext> options)
        : base(options) {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<MenuItem>().HasData(GetSeedDataMenuItems());
        modelBuilder.Entity<Contact>().HasData(GetSeedDataContacts());
    }

    public DbSet<MyRestaurantApi.Contact> Contact { get; set; } = default!;

    public DbSet<MyRestaurantApi.MenuItem> MenuItem { get; set; } = default!;

    public DbSet<MyRestaurantApi.MenuItemOrdered> MenuItemOrdered { get; set; } = default!;

    public DbSet<MyRestaurantApi.TogoOrder> TogoOrder { get; set; } = default!;
    private MenuItem[] GetSeedDataMenuItems() => new MenuItem[] {
        new MenuItem {
            Id = 1,
            Name = "Hamburger",
            Price = (decimal)3.68,
            Description = "It's a cheese burger without the cheese",
            Category = MenuItemCategory.Lunch
        },
        new MenuItem {
            Id = 2,
            Name = "Hamburger - double",
            Price = (decimal)5.70,
            Description = "It's a cheese burger without the cheese, with two beef patties",
            Category = MenuItemCategory.Lunch
        },
        new MenuItem {
            Id = 3,
            Name = "Cheeseburger",
            Price = (decimal)4.09,
            Description = "A hamburger with cheese",
            Category = MenuItemCategory.Lunch
        },
        new MenuItem {
            Id = 4,
            Name = "Cheeseburger - double",
            Price = (decimal)5.09,
            Description = "A hamburger with cheese, with two beef patties",
            Category = MenuItemCategory.Lunch
        },
        new MenuItem {
            Id = 5,
            Name = "Mushroom & Swiss burger",
            Price = (decimal)4.59,
            Description = "Mushroom & Swiss burger",
            Category = MenuItemCategory.Lunch
        },
        new MenuItem {
            Id = 6,
            Name = "Mushroom & Swiss burger - double",
            Price = (decimal)6.09,
            Description = "Mushroom & Swiss burger, with two beef patties",
            Category = MenuItemCategory.Lunch
        }
    };
    private Contact[] GetSeedDataContacts() => new Contact[] {
        new Contact {
            Id = 1,
            Name = "Sayed Hashimi",
            Email = "sayed@example.com",
            Phone = "555-111-2222"
        },
        new Contact {
            Id=2,
            Name = "Mads Kristensen",
            Email = "mads@example.com",
            Phone = "555-111-3333"
        },
        new Contact {
            Id=3,
            Name = "Eline Barstad",
            Email = "elineb@example.com",
            Phone = "555-111-4444"
        },
        new Contact {
            Id=4,
            Name = "Theodore Lamy",
            Email = "theol@example.com",
            Phone = "555-111-5555"
        },
        new Contact {
            Id=5,
            Name = "María Zelaya",
            Email = "mariaz@example.com",
            Phone = "555-111-6666"
        },
        new Contact {
            Id=6,
            Name = "Kubanychbek Sagynbek",
            Email = "kubans@example.com",
            Phone = "555-111-7777"
        },
        new Contact {
            Id=7,
            Name = "Denise Bourgeois",
            Email = "deniseb@example.com",
            Phone = "555-111-8888"
        },
        new Contact {
            Id=8,
            Name = "Robin Danielsen",
            Email = "robind@example.com",
            Phone = "555-111-9999"
        }
    };
}

现在,我们已经完成了获取DbContext和数据模型以支持此应用程序所需的所有操作。现在,我们可以继续准备数据库本身,到目前为止,我们只定义了一些代码,但尚未将任何代码应用于任何数据库。我们将在Visual Studio中使用一些对实体框架的新支持来简化此操作。

Visual Studio中的实体框架支持

在执行API端点之前,我们需要配置数据库。在脚手架步骤中,在代码中定义了EF DbContext,但数据库仍未配置为存储DbContext表示的数据。为此,我们需要执行两个操作。

  1. 添加EF迁移
  2. 更新数据库

过去,必须使用 dotnet ef 命令行实用工具来执行这两个步骤。在Visual Studio中,我们添加了支持,因此不需要使用命令行执行这些操作。在连接服务选项卡中添加了对ASP.NET Core项目的支持。若要开始,请在Web项目MyRestaurantApi下双击解决方案资源管理器中的连接服务节点。当您进入该页面时,它将如下所示。

由于我们将脚手架配置为使用Sqlite,因此我们将使用服务依赖项中Sqlite(本地)条目右侧的 ... 菜单中提供的一些选项。当您单击该菜单时,您应该看到以下选项。

 

首先,我们需要创建一个新的实体框架迁移。我们将使用上面所示的添加迁移上下文菜单选项。单击该选项时,将出现实体框架迁移对话框。在进入此菜单之前,请确保项目未运行。该对话框将启动生成,如果项目正在运行,你将收到错误。进入此对话框时,它应类似于下图。

此页上有两个输入,即要创建的迁移的名称以及要使用的DbContext类。迁移始终有一个默认名称可供使用,或者如果您愿意,也可以应用特定名称。这将是添加迁移时生成的文件的名称。对于DbContext,它应自动选择在脚手架步骤中创建的DbContext。对于包含多个DbContext的项目,应选择目标的特定DbContext。现在,我们可以单击完成以添加迁移。单击完成后,将创建迁移并将其添加到项目中。下一步是通过运行该迁移来更新数据库。

要更新数据库,我们将使用更新数据库菜单选项。单击该条目时,将出现一个对话框,您可以在其中选择目标DbContext。在此示例中,它应默认为在前面步骤中创建的上下文。如下图所示。

从这里,我们只需要单击完成,这将在数据库上运行上一步中的迁移。单击完成后,数据库已配置完毕,可供使用。如果使用的是源代码管理,则现在是创建新提交的好时机。对模型类进行任何进一步的更改后,应重复这两个步骤以更新数据库。让我们继续看看我们现在如何使用脚手架上的API。我们将使用新的端点资源管理器开始使用。

端点资源管理器

终结点资源管理器是我们正在开发的一项新的预览功能,使你能够查看解决方案中定义的API终结点并与之交互。由于这是一项预览功能,因此需要启用它才能查看它。若要启用此新的工具窗口,请转到工具>选项”>“环境”>预览功能,然后选中”Web API终结点资源管理器。您可以使用该对话框中的搜索文本框来限制选择。见下图。

启用终结点资源管理器后,可以通过转到查看”>“其他Windows >终结点资源管理器来打开它。打开该窗口后,应会看到类似于下图所示的内容。

 

在此窗口中,可以看到使用Visual Studio中的脚手架生成的所有API终结点。它为我们在脚手架期间使用的每个模型类创建了5个端点(Get/Post/Get specific/Put/Delete)。在此视图中,可以查看解决方案包含的所有终结点。如果将API端点添加到项目中,则可能需要使用端点资源管理器顶部的刷新按钮来刷新视图。在每个请求中,您可以查看处理请求的代码,以及生成对该终结点的新请求。当您单击条目时,您可以在上下文菜单中看到这些选项。这在下一个屏幕截图中显示。

在这里,如果你调用生成请求,HTTP文件将与请求一起生成。对于/api/MenuItem/请求,以下是调用该上下文菜单后的结果。

单击后,发生了以下情况:

  1. 创建一个HTTP文件并将其添加到项目中(如果在生成请求时HTTP文件处于打开状态,则会将其添加到该文件中)。
  2. 将变量添加到包含Web项目的Web地址的HTTP文件。
  3. 将请求添加到文件

终结点资源管理器目前是一项预览功能,因此它不会包含在Visual Studio 2022 17.6的最终公共版本中,但它将包含在17.617.7的预览版本中。我们希望尽快将其纳入GA版本。

HTTP文件是已添加到Visual Studio的新文件类型。现在让我们回顾一下对HTTP文件的支持。

HTTP编辑器

我们在Visual Studio 2022中增加了对.HTTP (.REST)文件的支持。此支持的灵感来自出色的VS Code扩展 REST客户端。此扩展名在VS Code中很流行,并且该文件格式现在在其他工具中也受支持。当我们在Visual Studio中添加对HTTP文件的支持时,我们最初希望直接集成此扩展,但该扩展是通过直接调用VS Code扩展性API来实现的,因此这不是一个选项。我们在Visual Studio中开发了一个新的HTTP编辑器来支持此文件类型。目前,REST客户端扩展的语法在Visual Studio中尚不受支持,但我们正在努力缩小这一差距。现在我们已经讨论了这个新编辑器的起源,让我们来探索一下它的功能。

HTTP编辑器的主要用途是使您能够声明一个或多个HTTP请求,并使您能够查看/检查发送请求时生成的响应。让我们仔细看看为/api/MenuItem/端点生成的代码。代码如下。

@MyRestaurantApi_HostAddress = https://localhost:7020

Get {{MyRestaurantApi_HostAddress}}/api/MenuItem/

###

让我们回顾一下这里的每一行。

@MyRestaurantApi_HostAddress = https://localhost:7020

第一行声明一个名为MyRestaurantApi_HostAddress的变量,并将其值赋给https://localhost:7020。该URL来自将HTTP文件添加到的项目。声明新变量的语法为:

@VariableName = value

这些变量可以在HTTP文件中的任何位置声明。您可以在HTTP文件中使用声明后的值。让我们看下一行,看看如何使用变量。

Get {{MyRestaurantApi_HostAddress}}/api/MenuItem/

这表示HTTP请求。定义HTTP请求时,第一个值是HTTP请求方法,在本例中为GET。后跟URL。在本例中,我们使用上一行中声明的变量。若要获取变量的值,请在HTTP文件中使用语法{{VariableName}}。在此行中,如果需要指定要使用的特定HTTP版本,可以在URL后添加该版本。例如,对于相同的请求,如果我们想使用HTTP 1.1,该行将如下所示。

Get {{MyRestaurantApi_HostAddress}}/api/MenuItem/ HTTP/1.1

现在让我们转到此文件中的最后一行。

###

这表示HTTP请求的结束。之后,您可以向此文件添加其他HTTP请求。现在,让我们看一个示例,该示例还包含请求的正文和一些标头。

我们看到的示例是一个简单的GET请求,没有向请求添加标头或正文。对于HTTP请求来说,拥有正文和标头是很常见的,所以让我们看看它是什么样子的。在这种情况下,我们可以在端点资源管理器中生成请求,然后添加正文和标头。

之前,我们看到我们有一个MenuItem API端点来获取/创建/编辑可供订购的菜单项。假设我们想要添加一个新的MenuItem,为此我们需要创建一个POST请求来添加一个新的MenuItem。我们可以使用端点资源管理器来帮助生成该请求。右键单击/api/MenuItem/POST,然后单击 Generate Request

在当前版本的端点资源管理器中,这将为HTTP文件生成以下内容。

Post {{MyRestaurantApi_HostAddress}}/api/MenuItem/

###

在未来的版本中,我们希望为此请求所需的JSON正文提供存根。从这里开始,我们想要添加JSON正文和标头,以便我们可以演示如何使用它们。我们需要添加到正文的JSON应该表示MenuItemJSON,并相应地设置了值,不包括将由数据库设置的id字段。更新后的请求如下所示。

Post {{MyRestaurantApi_HostAddress}}/api/MenuItem/
Content-Type: application/json
Accept: application/json

{
  "name": "All-beef hotdog and soda",
  "price": 1.50,
  "description": "An all-beef hotdog and soda with a permanent fixed price.",
  "category": 1
}

###

让我们更详细地描述此请求的语法。请求的语法是让第一行是HTTP方法,后跟URL和可选的HTTP版本,如前所述。在请求行之后,您可以在请求行之后的相应行上指定每个标头(中间没有空行)。指定标头时,语法为HeaderName:Value,其中HeaderName是要传入的标头的名称,Value是为其指定的值。在本例中,我们指定了两个标头,即Content-TypeAccept。此请求不需要这些标头,因为这些是默认值,但此处显示它们是为了演示如何传入标头。在标头或请求行之后,如果没有标头,则应添加一个空行,后跟请求正文。最后,我们在末尾添加###以指示请求的结束。

在此请求中,我们将添加一个新的MenuItem,该MenuItem将可供订购。我们正在添加一个新的全牛肉热狗和苏打水组合。首先,我们希望通过从Visual Studio启动API来确保API正在运行。启动Web API后,可以使用请求行左侧的绿色播放按钮发送请求。结果应类似于Visual Studio中的以下内容。

在这里我们可以看到,添加全牛肉热狗和苏打水的请求已成功添加到数据库中。它从数据库中分配了id7。现在,我们可以创建一个请求来添加新的TogoOrder。在提交新的TogoOrder之前,我们需要修改处理POST方法的TogoOrderEndpoints类。提交TogoOrder时,如果OrderCreated值为null,则应将其设置为当前日期/时间,并且我们还希望从MenuItem值填充MenuItemOrdered的详细信息。下面是TogoOrder后处理程序的更新代码。更新后的代码如下。

group.MapPost("/", async (TogoOrder togoOrder, MyRestaurantApiContext db) =>
{
    togoOrder.Customer = await db.Contact.FindAsync(togoOrder.Customer!.Id);

    if (togoOrder.OrderCreated == null) {
        togoOrder.OrderCreated = DateTime.Now;
    }
    if (togoOrder.ItemsOrdered != null && togoOrder.ItemsOrdered.Count > 0) {
        foreach (var item in togoOrder.ItemsOrdered) {
            var menuItem = await db.MenuItem.FindAsync(item.MenuItemId);
            item.Name = menuItem!.Name;
            if (item.Price is null || !item.Price.HasValue || item.Price.Value < 0) {
                item.Price = menuItem.Price!.Value;
            }
            if (item.Category is null || !item.Category.HasValue) {
                item.Category = menuItem.Category!.Value;
            }
        }
    }
    db.TogoOrder.Add(togoOrder);
    await db.SaveChangesAsync();
    return TypedResults.Created($"/api/TogoOrder/{togoOrder.Id}",togoOrder);
})
.WithName("CreateTogoOrder")
.WithOpenApi();

需要添加的代码是出现在db之前的代码。TogoOrder.Add(togoOrder)。这是不言自明的。提交请求时,如果OrderCreated的值为空,则将填充该值,然后从相应的MenuItemId填充MenuItem中缺少的任何值。MenuItemId是将要创建的每个MenuItemOrdered的唯一必需值。

首先,我们将使用端点资源管理器生成存根请求,方法是右键单击POST /api/TogoOrder/并选择Generate Request。然后,我们需要将JSON正文添加到请求中。添加JSON正文后,请求应如下所示。

Post {{MyRestaurantApi_HostAddress}}/api/TogoOrder/
Content-Type: application/json

{
  "itemsOrdered": [
    {
      "menuItemId":1
    }
  ],
  "subtotal": 3.50,
  "tax": 0.8,
  "total": 4.30,
  "paymentMethod": 1,
  "customer": {
    "id": 2
  }
}
###

在此请求中,我们使用POST方法添加新的TogoOrder条目。如前所述,我们可以列出订购的项目,唯一必填的字段是menuItemId。我们添加到Post方法的代码将填充缺少的字段。我们还在TogoOrder对象中分配美元金额。我们可以通过按请求行左侧的绿色播放按钮来发送此请求。调用该响应后,响应应类似于以下内容。

{
  "id": 1,
  "orderCreated": "2023-04-27T14:14:48.3785278-04:00",
  "itemsOrdered": [
    {
      "id": 1,
      "menuItemId": 1,
      "togoOrderId": 1,
      "name": "Hamburger",
      "price": 3.68,
      "category": 1
    }
  ],
  "subtotal": 3.5,
  "tax": 0.8,
  "total": 4.3,
  "paymentMethod": 1,
  "customer": {
    "id": 2,
    "name": "Mads Kristensen",
    "email": "mads@example.com",
    "phone": "555-111-3333"
  }
}

现在,我们已经将TogoOrder添加到数据库中,让我们通过发出GET请求来列出数据库中的所有TogoOrders,以验证我们是否获得了正确的结果。我们可以通过在GET /api/TogoOrder/终结点的上下文菜单中选择生成请求,使用Endpoints Explorer生成该请求。调用该请求后,以下请求将添加到HTTP文件中。

Get {{MyRestaurantApi_HostAddress}}/api/TogoOrder/

###

要发送此请求,请单击绿色的播放按钮。执行此操作后,响应视图中的结果应类似于以下内容。

[
  {
    "id": 1,
    "orderCreated": "2023-04-27T14:22:53.2117889",
    "itemsOrdered": null,
    "subtotal": 3.5,
    "tax": 0.8,
    "total": 4.3,
    "paymentMethod": 1,
    "customer": null
  }
]

我们已经成功收到了结果,但这里的itemsOrderednull。如果我们要检查数据库,我们会看到所有正确的条目都已创建,并且所有关系也都是正确的。这里的问题是实体框架没有自动加载相关数据。有关EF如何加载相关数据的详细信息,请参阅文档加载相关数据– EF Core。在这种情况下,当返回TogoOrder时,我们始终希望加载itemsOrdered字段的数据。我们需要对生成的GET请求进行简单的编辑来满足这一需求。在这种情况下,当返回TogoOrder时,我们始终希望加载 ItemsOrdered 字段的数据。我们需要对生成的GET请求进行简单的编辑来满足这一需求。处理此请求的方法位于 TogoOrderEndpoints 类中。我们需要为EF添加一个include语句来冻结ItemsOrdered属性。修改后的方法如下。

group.MapGet("/", async (MyRestaurantApiContext db) =>
{
    return await db.TogoOrder
        .Include(order => order.ItemsOrdered)
        .ToListAsync();
})

添加此项后,我们将重新启动Web API项目并重新发送GET请求以列出所有订单。结果应如以下代码片段所示。

[
  {
    "id": 1,
    "orderCreated": "2023-04-27T14:22:53.2117889",
    "itemsOrdered": [
      {
        "id": 1,
        "menuItemId": 1,
        "togoOrderId": 1,
        "name": "Hamburger",
        "price": 3.68,
        "category": 1
      }
    ],
    "subtotal": 3.5,
    "tax": 0.8,
    "total": 4.3,
    "paymentMethod": 1,
    "customer": null
  }
]

现在我们可以看到,请求已返回TogoOrder对象,其中填充了items ordered值。我们现在已经成功构建了一个ASP.NET Core Web API来处理TogoOrder请求。让我们继续讨论HTTP文件中支持的语法。

HTTP文件语法

前面我们提到过,HTTP编辑器及其相关支持的灵感来自Visual Studio Code REST客户端扩展。该扩展在HTTP文件中支持的语法比Visual Studio目前支持的语法更广泛。随着我们继续前进,我们将增加更多支持以缩小这一差距。在本节中,我们将讨论Visual Studio中当前支持的语法。

在本部分中,请求将使用 httpbin.org,这是每个人都可以使用的免费第三方Web API。您可以向它发送请求,它会将请求回显给您。让我们来探讨一下Visual Studio中当前支持的语法。

注释

注释是以#//开头的行。这些行将被忽略,并且不会包含在请求中。

变量

如前所述,您可以定义可在后续请求中重用的变量。您可以定义任意数量的所需变量。可以使用已定义的其他变量的值来定义变量。例如,请参阅以下代码片段。

@searchTerm = some-search-term
@hostname = httpbin.org
# variable using another variable
@host = https://{{hostname}}
@name = Sayed
@phone = 111-222-3333

在这里,我们定义了几个变量,包括主机变量,它使用另一个名为 hostname 的变量的值。下面是使用这些变量之一的示例请求。

GET {{host}}/anything HTTP/1.1
User-Agent: rest-client
Content-Type: application/json
###

请求行

对于请求行,我们之前提到请求行的格式是

HTTPMethod URL HTTPVersion

这里HTTPMethod是要使用的HTTP方法,例如GETPOSTPUTPATCH等。URL是请求将发送到的URL。此URL还可以包含查询字符串参数,如以下示例请求。可选的 HTTPVersion 是应使用的HTTP版本。以下是一些示例请求。

GET https://httpbin.org/get
###

GET https://httpbin.org/get?name=Sayed?&phone=111-222-3333
###

GET https://httpbin.org/get?name=Sayed?&phone=111-222-3333 HTTP/1.1
###

Visual Studio Code REST客户端扩展支持的一些支持,我们目前不支持。这些包括。

  • 可选的HTTP方法。REST客户端支持不指定HTTP方法。在这些情况下,GET是默认的HTTP方法。
  • 请求URL跨越多行。在Visual Studio的当前实现中,请求行必须位于单行上。

我们希望在未来的版本中增加对这些的支持。

同一文件中的多个请求

如上所示,一个HTTP文件可以列出多个不同的请求。它们需要在每个请求的末尾用###分隔。

Headers

若要添加一个或多个标头,请在请求行之后的紧接(无空行)之后将每个标头添加到其自己的行上(无空行)。以下是几个不同的示例。

GET https://httpbin.org/get?name=Sayed?&phone=111-222-3333 HTTP/1.1
Date: Wed, 27 Apr 2023 07:28:00 GMT
###

GET https://httpbin.org/get?name=Sayed?&phone=111-222-3333
Cache-Control: max-age=604800
Age: 100
###

POST https://httpbin.org/post HTTP/1.1
Content-Type: application/json
Accept-Language: en-US,en;q=0.5

{
    "name": "sample",
    "time": "Wed, 21 Oct 2015 18:27:50 GMT"
}
###

如果要调用可以使用标头进行身份验证的API,也可以在标头中指定这些API。如果执行此操作,请注意不要将任何机密提交到存储库。我们将研究以安全的方式支持机密的方法。

现在,我们已经了解了一些受支持的语法,在下一节中,我们将列出REST客户端具有的Visual Studio当前不支持的一些功能。如果您希望看到对这些功能的支持,请在下面发表评论或向团队发送一些反馈(请参阅下面的结束部分)。此列表排名不分先后。有关下面列出的任何项的详细信息,请参阅Visual Studio Code REST客户端扩展

Visual Studio HTTP编辑器当前不支持

  • 可选的HTTP方法
  • 跨多行的请求行
  • 命名请求
  • 动态变量
  • 环境文件
  • 将文件路径指定为请求正文
  • 使用multipart/form-data时正文的混合格式
  • GraphQL请求
  • cURL请求
  • 复制/粘贴为cURL
  • 请求历史记录
  • 将响应正文保存到文件
  • 基于证书的身份验证
  • 提示变量
  • 系统变量
  • 自定义响应预览
  • 每个请求的设置

现在,我们已经介绍了Visual Studio HTTP编辑器中支持的内容和不支持的内容,让我们继续讨论Visual Studio 2022ASP.NET Core Web API开发人员接下来将要做什么。

下一步

在这篇文章中,我们讨论了开发ASP.NET Core Web API项目的端到端流程,包括为这些开发人员提供的一些新工具,包括端点资源管理器和 HTTP编辑器。对我们来说,这是漫长旅程的开始,我们计划在相当长的一段时间内继续在这个领域进行投资。我们正在研究以添加支持的一些特性和功能包括。下面的列表不按任何特定顺序排列。下面的列表是我们正在探索的一些想法,我们目前不承诺提供所有这些功能。

  • 为请求正文添加存根——对于需要正文的请求,目前Visual Studio不会为请求正文添加任何内容。我们正在研究如何为正文添加存根,以便更轻松地完成该请求。
  • 添加对HTTP文件语法的更多支持——我们仅支持REST客户端支持的语法子集。我们将努力缩小这一差距,我们可能无法支持100%的功能,但我们将尽可能接近。这里想到的一些最重要的项目包括支持;命名请求、环境文件、支持多部分/表单数据请求中的混合响应以及复制/粘贴为cURL。
  • 改进的响应视图——响应视图目前非常基本。我们计划制作一个更结构化的响应视图,以便更轻松地读取来自Web服务器的结果。
  • 在端点资源管理器中显示HTTP文件——目前,端点资源管理器仅显示解决方案中的API终结点。我们还希望显示解决方案中或在Visual Studio中打开的HTTP文件。这将使您能够以更简单的方式检查和浏览这些文件。
  • 请求历史记录——我们希望为您提供支持,以便您能够查看之前发送的请求,然后重新发送这些请求。编辑以前的请求也是我们想要添加支持的一项功能。
  • UX简化发出请求——目前要创建请求,您必须手动输入请求。某些用户更愿意使用图形用户界面来创作该请求。
  • 将响应正文保存到文件——改进响应视图后,我们希望添加将响应正文保存到文件的功能。
  • 测试——我们希望找到一种方法来简化Web API项目的测试。我们这里还没有任何具体的计划,但这对我们来说是一个重要的领域。

https://devblogs.microsoft.com/visualstudio/web-api-development-in-visual-studio-2022/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值