使用 C# 的 .NET 6 的最小 API

本文介绍了微软推出的Minimal API,它删除了Startup.cs并将代码放于Program.cs中,还移除了带操作的控制器。详细说明了创建最小API的步骤,包括映射GET、POST、DELETE端点,以及最小API与依赖注入的结合使用,最后介绍了使API异步的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当您使用 .NET 5 创建 Web API 时,它会搭建一个名为“ Controllers ”的文件夹。在这个文件夹中是包含动作的类。控制器和动作的组合构成了 API 的端点。动作是一种可以做逻辑工作的方法,或者调用另一个处理逻辑的服务类。它看起来像这样:

解决方案资源管理器显示文件夹“ Controllers ”,其中包含文件WeatherForecastController.cs。该文件包含一个具有相同名称和操作的类。它还包含 API 和路由的配置。

还有一个Startup.cs文件,其中包含项目的配置。例如,依赖注入、实体框架、Swagger 等等。

这就是我们长期以来一直这样做的方式。现在,微软推出了Minimal API

最小 API 解释

一个最小的 API 基本上删除了Startup.cs并将该代码放在Program.cs中。带有操作的控制器被删除并作为映射放置在Program.cs中。它允许我们将 API 的编码保持在最低限度。

它对每个 API 调用使用 Lambda 表达式。您可以配置路由和请求类型。它看起来像这样:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/movies"</span>, () => <span style="color:#0000ff">new</span> List<string>() { <span style="color:#800080">"</span><span style="color:#800080">Shrek"</span>, <span style="color:#800080">"</span><span style="color:#800080">The Matrix"</span>, <span style="color:#800080">"</span><span style="color:#800080">Inception"</span>});</span></span>

应用程序Startup.cs中定义,即WebApplicationMapGet是一种将路由“映射”到 lambda 表达式的方法。在上面的示例中,我想映射路线https://localhost:12345/movies并使其返回电影标题列表。

此外,MapGet还有MapPostMapDelete, 和MapPut。这些代表属性HTTPGetHTTPPostHTTPDelete, 和HTTPPut我们用于 .NET 5 API 中的操作。

这是基本的想法。让我们进入一个例子!

创建最小 API

既然我们现在知道了最小 API 背后的基本故事,那么让我们创建一个并看看它是如何工作的。在接下来的章节中,我将向您展示我们开发人员需要完成的最常见的任务。一个简单的映射,一个异步的映射,以及依赖注入的使用。

让我们使用 Visual Studio 2022 创建一个新项目并选择 ASP.NET Core Web API 模板。给它一个好的项目名称。我将调用我的“ MinimalApiExample”,并且解决方案名称是相同的。

附加信息屏幕需要注意。

Visual Studio 2022 中的每个项目模板都有自己的附加信息。有些选项是相同的,比如框架。您在此处看到的大多数选项都很常见,但如果您仔细观察,您会看到两个新的复选框:

  • 使用控制器(取消选中以使用最少的 API)
  • 不要使用顶级语句

对于本文,我不关心那些顶级语句。但我确实关心第一个。这是默认选中的,它将为控制器和操作搭建脚手架,就像我们以“旧”方式习惯的那样。如果取消选中,Visual Studio 将不会创建这些控制器,而这正是我们想要的。让我们取消选中它并按创建按钮。

完成加载和创建基本文件后,您可能会注意到项目的结构。我们缺少文件夹“ Controllers ”,这正是我们想要的。
如果您打开Program.cs,您可能还会注意到一些差异。没有app.MapControllers(),突然之间,有一个app.MapGet(…)。欢迎使用最小 API!

映射端点

使用最少 API 的想法是您不需要创建带有操作的新控制器。这一切都是用 lambda 表达式完成的。通过映射您的端点,您可以告诉 API 用户/客户端可以输入哪些地址。让我们看一下program.cs中的示例:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/weatherforecast"</span>, () =>
{
    <span style="color:#0000ff">var</span> forecast = Enumerable.Range(<span style="color:#000080">1</span>, <span style="color:#000080">5</span>).Select(index =>
        <span style="color:#0000ff">new</span> WeatherForecast
        (
            DateTime.Now.AddDays(index),
            Random.Shared.Next(-20, <span style="color:#000080">55</span>),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    <span style="color:#0000ff">return</span> forecast;
})
.WithName(<span style="color:#800080">"</span><span style="color:#800080">GetWeatherForecast"</span>);</span></span>

此映射用于 (HTTP)GET请求。端点是 (https://localhost:12345)/weatherforecast。该GET方法的返回值是 JSON 格式的一系列天气预报。很简单,对吧?

让我们删除所有示例数据,例如映射、变量摘要和内部记录WeatherForecast()。确保您不会意外删除app.Run(),因为它在示例数据之间有点偷偷摸摸。

让我们创建一个新对象,名为Movie. 将此对象放在新文件中或program.cs的底部。然后创建一个包含电影列表的新变量。请注意,变量 movies 需要先声明并填​​写,然后才能使用它们。我建议将它放在 Swagger 启动下。它看起来像这样:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">List<movie> movies = <span style="color:#0000ff">new</span>()
{
    <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">1</span>, Rating = <span style="color:#000080">5</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Shrek"</span> },
    <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">2</span>, Rating = <span style="color:#000080">1</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Inception"</span> },
    <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">3</span>, Rating = <span style="color:#000080">3</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Jaws"</span> },
    <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">4</span>, Rating = <span style="color:#000080">1</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">The Green Latern"</span> },
    <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">5</span>, Rating = <span style="color:#000080">5</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">The Matrix"</span> },
};

<span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> Movie
{
    <span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> Id { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; } 
    <span style="color:#0000ff">public</span> <span style="color:#0000ff">string</span> Title { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; }
    <span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> Rating { <span style="color:#0000ff">get</span>; <span style="color:#0000ff">set</span>; }
}</span></span>

映射 GET

映射GET请求并不难。我们刚刚删除了这个例子。让我们创建一个返回所有电影的新映射:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/"</span>, () =>
{
    <span style="color:#0000ff">return</span> Results.Ok(movies);
});</span></span>

这将在调用端点 (https://localhost:1234)/api/movies 时返回完整的电影列表。

MapGet表示您要创建GET端点。使用POST这个端点是行不通的。

如果您想通过 id 或其他参数获取特定电影,只需在使用MapGet.

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/"</span>, () =>
{
    <span style="color:#0000ff">return</span> Results.Ok(movies);
});

app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/{id:int}"</span>, (<span style="color:#0000ff">int</span> id) =>
{
    <span style="color:#0000ff">return</span> Results.Ok(movies.<span style="color:#339999">Single</span>(x => x.Id == id));
});</span></span>

通过在端点中添加{id:int},您可以告诉 API 可以预期一个数字。然后,您接下来添加变量声明,您可以在正文中使用它。
您还可以声明id不带类型的变量,如下所示:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/{id:int}"</span>, (id) =>
{
    <span style="color:#0000ff">return</span> Results.Ok(movies.<span style="color:#339999">Single</span>(x => x.Id == id));
});</span></span>

但这不起作用,因为假设第一个参数是HttpContext,而不是您尝试使用的参数。使用HttpContext是管理您自己对任何请求的响应的好方法。但这不是我们想要的。

映射 POST

另一种常用的请求方法是POST. 它允许客户端将数据发送到 API,在那里可以使用和处理数据。创建POST具有最少 API 的 a 与 a 没有太大区别GET,只是您需要引用一个对象,该对象将捕获来自客户端的发布数据。

在 .NET 5 中,我们习惯于在动作的参数中声明一个类型。它的工作原理还是一样的。看看下面的代码:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapPost(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/"</span>, (Movie movie) =>
{
    movies.Add(movie);

    <span style="color:#0000ff">return</span> Results.Ok(movies);
});</span></span>

同样,我使用该应用程序来映射一个新的端点。在这种情况下,我使用MapPost,因为我希望能够将数据发布到 API。然后,我将Movie电影添加到参数列表中。这会导致 API 将传入的数据从主体映射到Movie对象。

在正文中,我将新电影添加到电影列表中。出于演示目的,我返回电影列表,包括新添加的电影。

映射删除

我要向您展示的最后一个映射是DELETE请求或删除。GET其他请求类型与, POST, 或基本相同DEL

删除需要一个键,或者一些独特的东西。否则,您最终删除的内容可能比您实际想要的要多。所以我们需要一个查询参数,就像id我在 - 方法中展示的那样MapGet

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapDelete(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/{id:int}"</span>, (<span style="color:#0000ff">int</span> id) =>
{
    movies.Remove(movies.<span style="color:#339999">Single</span>(x => x.Id == id));

    <span style="color:#0000ff">return</span> movies;
});</span></span>

MapGet它看起来与构造几乎相同,只是MapDelete使用了 。请注意,我没有使用Results.Ok(…). 它不是必需的。像这样返回电影会产生一个 HTTP 200 代码,这很好。

如果您测试 API 并想要删除某些内容,您可以在 URL 中添加要删除的项目的 id。但是,如果您发送GET请求而不是DELETE请求,API 将返回具有该给定 id 的电影并且不会删除它。

最小的 API 和依赖注入

您可能希望在 API 中使用依赖注入,这是一种常见的做法。使用控制器时,您可以通过构造函数注入接口。但是我们没有构造函数,我们只有带有 lambda 表达式的映射。

对于这一部分,我创建了一个带有接口的小类。我还将电影列表移至新类:

C#
收缩▲   
<span style="color:#000000"><span style="background-color:#fbedbb"><span style="color:#0000ff">public</span> <span style="color:#0000ff">interface</span> IMovies
{
    List<Movie> GetAll();
    Movie GetById(<span style="color:#0000ff">int</span> id);
    <span style="color:#0000ff">void</span> Delete(<span style="color:#0000ff">int</span> id);
    <span style="color:#0000ff">void</span> Insert(Movie movie);
}

<span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> Movies: IMovies
{
    <span style="color:#0000ff">private</span> List<Movie> _movies = <span style="color:#0000ff">new</span>()
    {
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">1</span>, Rating = <span style="color:#000080">5</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Shrek"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">2</span>, Rating = <span style="color:#000080">1</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Inception"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">3</span>, Rating = <span style="color:#000080">3</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Jaws"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">4</span>, Rating = <span style="color:#000080">1</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">The Green Latern"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">5</span>, Rating = <span style="color:#000080">5</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">The Matrix"</span> },
    };

    <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> Delete(<span style="color:#0000ff">int</span> id)
    {
        _movies.Remove(_movies.<span style="color:#339999">Single</span>(x => x.Id == id));
    }

    <span style="color:#0000ff">public</span> List<Movie> GetAll()
    {
        <span style="color:#0000ff">return</span> _movies;
    }

    <span style="color:#0000ff">public</span> Movie GetById(<span style="color:#0000ff">int</span> id)
    {
        <span style="color:#0000ff">return</span> _movies.<span style="color:#339999">Single</span>(x => x.Id == id);
    }

    <span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> Insert(Movie movie)
    {
        _movies.Add(movie);
    }
}</span></span>

现在我们可以配置接口和实现类。在program.cs中,您可以使用builder.Services. 要添加IMoviesand Movies,只需使用以下行:

C#
<span style="color:#000000"><span style="background-color:#fbedbb"><span style="color:#008000"><em>//</em></span><span style="color:#008000"><em> Configuration for dependency injection</em></span>
builder.Services.AddScoped<IMovies, Movies>();</span></span>

将此行放在 之后builder.Services.AddSwaggerGen()。我们已经配置好了,让我们使用吧!这MapGet真的很容易:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/"</span>, (IMovies movies) =>
{
    <span style="color:#0000ff">return</span> Results.Ok(movies.GetAll());
})</span></span>

只需将接口和变量名添加到映射的参数列表中。

好,我们来看看第二个get,那个在URL里有id的。

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/{id:int}"</span>, (<span style="color:#0000ff">int</span> id, IMovies movies) =>
{
    <span style="color:#0000ff">return</span> Results.Ok(movies.GetById(id));
});</span></span>

是的,就是这样!但是……如果我们切换 id 和 movies 会发生什么?没有什么!嗯,有事发生。它就像上面的例子一样工作。.NET 识别类型,命名约定也有帮助。但是,如果您需要我的建议:在参数列表中保持一致的顺序。我个人喜欢在注入之前添加查询参数。

让我们用 DI 修复最后两个映射:

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapPost(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/"</span>, (Movie movie, IMovies movies) =>
{
    movies.Insert(movie);

    <span style="color:#0000ff">return</span> Results.Ok(movies.GetAll());
});

app.MapDelete(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/{id:int}"</span>, (<span style="color:#0000ff">int</span> id, IMovies movies) =>
{
    movies.Delete(id);

    <span style="color:#0000ff">return</span> movies.GetAll();
});</span></span>

使其异步

我们的大多数操作都是异步的,以使 API 处理多个请求并使其工作得更快。我向您展示的示例不是异步的。使映射异步并不难。

对于这一部分,我将类中的方法Movies设为异步。这不是最好的例子,但它是关于 API,而不是一些基本示例类中的逻辑。

C#
收缩▲   
<span style="color:#000000"><span style="background-color:#fbedbb"><span style="color:#0000ff">public</span> <span style="color:#0000ff">interface</span> IMovies
{
    Task<List<Movie>> GetAll();
    Task<Movie> GetById(<span style="color:#0000ff">int</span> id);
    Task Delete(<span style="color:#0000ff">int</span> id);
    Task Insert(Movie movie);
}

<span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> Movies: IMovies
{
    <span style="color:#0000ff">private</span> List<Movie> _movies = <span style="color:#0000ff">new</span>()
    {
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">1</span>, Rating = <span style="color:#000080">5</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Shrek"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">2</span>, Rating = <span style="color:#000080">1</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Inception"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">3</span>, Rating = <span style="color:#000080">3</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">Jaws"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">4</span>, Rating = <span style="color:#000080">1</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">The Green Latern"</span> },
        <span style="color:#0000ff">new</span>() { Id = <span style="color:#000080">5</span>, Rating = <span style="color:#000080">5</span>, Title = <span style="color:#800080">"</span><span style="color:#800080">The Matrix"</span> },
    };

    <span style="color:#0000ff">public</span> <span style="color:#0000ff">async</span> Task Delete(<span style="color:#0000ff">int</span> id)
    {
        _movies.Remove(_movies.<span style="color:#339999">Single</span>(x => x.Id == id));
    }

    <span style="color:#0000ff">public</span> <span style="color:#0000ff">async</span> Task<List<Movie>> GetAll()
    {
        <span style="color:#0000ff">return</span> _movies;
    }

    <span style="color:#0000ff">public</span> <span style="color:#0000ff">async</span> Task<Movie> GetById(<span style="color:#0000ff">int</span> id)
    {
        <span style="color:#0000ff">return</span> _movies.<span style="color:#339999">Single</span>(x => x.Id == id);
    }

    <span style="color:#0000ff">public</span> <span style="color:#0000ff">async</span> Task Insert(Movie movie)
    {
        _movies.Add(movie);
    }
}</span></span>

我们现在要做的就是使映射异步。这并不难,就像前几章一样。让我们重新开始MapGet

C#
<span style="color:#000000"><span style="background-color:#fbedbb">app.MapGet(<span style="color:#800080">"</span><span style="color:#800080">/api/movies/"</span>, <span style="color:#0000ff">async</span> (IMovies movies) =>
{
    <span style="color:#0000ff">return</span> Results.Ok(<span style="color:#0000ff">await</span> movies.GetAll());
});</span></span>

看?没那么难。我将 lambda 表达式标记为 async 并在等待的前面添加了等待await movies.GetAll()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值