使用C#的.NET 6的Minimal API

目录

介绍

Minimal API说明

创建Minimal API

映射端点

映射GET

映射POST

映射DELETE

Minimal API和依赖注入

使其异步

兴趣点


介绍

在本文中,我将展示并解释创建和运行.NET 6 Minimal API的步骤。但在深入了解.NET 6Minimal API之前,让我们看一下用于创建API的方式。尤其是控制器、动作和一些配置。

使用.NET 5创建和Web API时,它将搭建名为Controllers的文件夹。此文件夹中是包含操作的类。控制器和操作的组合构成了API的终结点。该操作是可以执行逻辑工作或调用处理逻辑的另一个服务类的方法。它看起来像这样:

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

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

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

Minimal API说明

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

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

app.MapGet("/movies", () => new List<string>() { "Shrek", "The Matrix", "Inception"});

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

此外,还有MapGetMapPostMapDeleteMapPut。这些表示属性HTTPGetHTTPPostHTTPDeleteHTTPPut,我们在.NET 5 API中的操作上使用。

这是基本思想。让我们跳到一个例子中!

创建Minimal API

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

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

附加信息屏幕需要注意一点。

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

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

对于本文,我不关心那些顶级陈述。但我确实关心第一个。默认情况下会选中此功能,它将为控制器和操作搭建基架,就像我们习惯的方式一样。如果您取消选中,Visual Studio将不会创建这些控制器,这正是我们想要的。让我们取消选中它,然后按创建按钮。

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

映射端点

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

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateTime.Now.AddDays(index),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast");

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

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

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

List<movie> movies = new()
{
    new() { Id = 1, Rating = 5, Title = "Shrek" },
    new() { Id = 2, Rating = 1, Title = "Inception" },
    new() { Id = 3, Rating = 3, Title = "Jaws" },
    new() { Id = 4, Rating = 1, Title = "The Green Latern" },
    new() { Id = 5, Rating = 5, Title = "The Matrix" },
};

public class Movie
{
    public int Id { get; set; } 
    public string Title { get; set; }
    public int Rating { get; set; }
}

映射GET

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

app.MapGet("/api/movies/", () =>
{
    return Results.Ok(movies);
});

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

MapGet指示要创建GET端点。使用POST端结点将不起作用。

如果要按id或其他参数获取特定电影,只需简单的使用MapGet添加其到端点中。

app.MapGet("/api/movies/", () =>
{
    return Results.Ok(movies);
});

app.MapGet("/api/movies/{id:int}", (int id) =>
{
    return Results.Ok(movies.Single(x => x.Id == id));
});

通过在端点中添加{id:int},您可以告诉API可以期望一个数字。然后,接下来添加变量声明,就可以在正文中使用它了。

你也可以声明不带类型的id变量,如下所示:

app.MapGet("/api/movies/{id:int}", (id) =>
{
    return Results.Ok(movies.Single(x => x.Id == id));
});

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

映射POST

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

.NET 5中,我们习惯于只在操作的参数中声明一个类型。它仍然有点相同。看看下面的代码:

app.MapPost("/api/movies/", (Movie movie) =>
{
    movies.Add(movie);

    return Results.Ok(movies);
});

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

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

映射DELETE

我想向您展示的最后一个映射是DELETE请求或删除。其他请求类型GETPOSTDEL基本相同。

删除需要密钥或唯一的东西。否则,您最终可能会删除比实际想要的太多内容。所以我们需要一个查询参数,就像MapGet-方法中显示的id一样。

app.MapDelete("/api/movies/{id:int}", (int id) =>
{
    movies.Remove(movies.Single(x => x.Id == id));

    return movies;
});

它看起来与MapGet结构几乎相同,只是使用了MapDelete。请注意,我没有使用Results.Ok(…)。不需要。像这样返回电影将导致HTTP 200代码,这很好。

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

Minimal API和依赖注入

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

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

public interface IMovies
{
    List<Movie> GetAll();
    Movie GetById(int id);
    void Delete(int id);
    void Insert(Movie movie);
}

public class Movies: IMovies
{
    private List<Movie> _movies = new()
    {
        new() { Id = 1, Rating = 5, Title = "Shrek" },
        new() { Id = 2, Rating = 1, Title = "Inception" },
        new() { Id = 3, Rating = 3, Title = "Jaws" },
        new() { Id = 4, Rating = 1, Title = "The Green Latern" },
        new() { Id = 5, Rating = 5, Title = "The Matrix" },
    };

    public void Delete(int id)
    {
        _movies.Remove(_movies.Single(x => x.Id == id));
    }

    public List<Movie> GetAll()
    {
        return _movies;
    }

    public Movie GetById(int id)
    {
        return _movies.Single(x => x.Id == id);
    }

    public void Insert(Movie movie)
    {
        _movies.Add(movie);
    }
}

现在我们可以配置接口和实现类了。在program.cs中,你可以使用builder.Services来添加依赖注入。要添加IMoviesMovies,只需使用以下行:

// Configuration for dependency injection
builder.Services.AddScoped<IMovies, Movies>();

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

app.MapGet("/api/movies/", (IMovies movies) =>
{
    return Results.Ok(movies.GetAll());
})

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

好的,让我们看一下第二个getURL中带有id的那个。

app.MapGet("/api/movies/{id:int}", (int id, IMovies movies) =>
{
    return Results.Ok(movies.GetById(id));
});

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

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

app.MapPost("/api/movies/", (Movie movie, IMovies movies) =>
{
    movies.Insert(movie);

    return Results.Ok(movies.GetAll());
});

app.MapDelete("/api/movies/{id:int}", (int id, IMovies movies) =>
{
    movies.Delete(id);

    return movies.GetAll();
});

使其异步

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

对于这部分,我使Movies类中的方法异步。这不是最好的示例,但它与API有关,而不是某些基本示例类中的逻辑。

public interface IMovies
{
    Task<List<Movie>> GetAll();
    Task<Movie> GetById(int id);
    Task Delete(int id);
    Task Insert(Movie movie);
}

public class Movies: IMovies
{
    private List<Movie> _movies = new()
    {
        new() { Id = 1, Rating = 5, Title = "Shrek" },
        new() { Id = 2, Rating = 1, Title = "Inception" },
        new() { Id = 3, Rating = 3, Title = "Jaws" },
        new() { Id = 4, Rating = 1, Title = "The Green Latern" },
        new() { Id = 5, Rating = 5, Title = "The Matrix" },
    };

    public async Task Delete(int id)
    {
        _movies.Remove(_movies.Single(x => x.Id == id));
    }

    public async Task<List<Movie>> GetAll()
    {
        return _movies;
    }

    public async Task<Movie> GetById(int id)
    {
        return _movies.Single(x => x.Id == id);
    }

    public async Task Insert(Movie movie)
    {
        _movies.Add(movie);
    }
}

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

app.MapGet("/api/movies/", async (IMovies movies) =>
{
    return Results.Ok(await movies.GetAll());
});

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

我也可以向您展示其他映射,但它们都是一样的,我想你明白了。

兴趣点

如果您的API较小且终端节点不多,则使用Minimal API是一种很好的方法。在我看来,当你有很多端点时,它开始变得丑陋。您可能会迷失在所有映射中。当您有一个小型项目时,使用.NETMinimal API会更好。如果你有更大的项目,你可以切换到好的旧控制器。

https://www.codeproject.com/Articles/5344967/A-Minimal-API-with-NET-6-using-Csharp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值