八、API 的版本控制和 ASP.NET Core 的登录

欢迎来到另一章。 本章将教你如何记录 API 请求,这是任何应用的基本部分,因为记录会给开发人员和业务人员带来好处。 您还将了解 API 版本控制,这有时是创建可维护 API 所必需的,但如果不正确地执行,可能会出现问题。

我们将涵盖以下议题:

  • API versioning
  • 日志在 ASP.NET Core

技术要求

以下是你完成本章所需要的东西:

  • Visual Studio 2019, Visual Studio for Mac,或 Rider
  • SQLite 浏览器或 SQLiteStudio

已完成存储库的链接可以在这里找到:https://github.com/PacktPublishing/ASP.NET-Core-and-Vue.js/tree/master/Chapter08

API 版本控制

所以我们在这一节将要学习的是关于 ASP 版本控制的一些事情.NET Core Web API。 我们将首先讨论什么是 API 版本控制以及 API 中的一些版本控制策略,然后我们将深入一些代码,并将 API 版本控制与 OpenAPI 集成,以便您可以在 Swagger UI 中看到版本控制的 API。

现在让我们从定义 API 版本控制开始。

什么是 API 版本控制?

简而言之,API 版本就是您希望处理 API 随时间变化的方式。 我们大多数后端开发人员专注于构建和部署 web 服务,因为这是我们的工作。 它们通常是我们必须在一个典型项目中实现的书面任务。 但有时我们会忘记,我们必须在未来 5 到 10 年里支持 web 服务,或者取代我们的开发人员会这样做。

一旦部署了 web 服务,并且开发人员已经根据 API 进行了编码,就会有用户依赖于我们构建的 API。 例如,具有特定版本的移动应用将期望 web 服务的端点永远不变。 有一个处理不变 api 的计划和策略是至关重要的,因为您的需求和应用的特性将继续发展。 您需要一种方法在不破坏现有客户端应用的情况下更改这些 api。

这就是 API 版本控制的核心思想。

那么我们如何对 api 进行版本化呢? 让我们看下一部分。

API 版本策略

有很多方法可以做到这一点,但我们只讨论最常用的三个 API 版本控制策略或方案。

你应该选择哪一个? 这取决于谁使用您的 API,您的 API 将如何发展,以及您需要什么样的技术需求。 找到适合您用例的东西。

那么我们如何弃用一些 api 呢?

URI 路径版本控制

URI 路径策略很流行,因为实现起来很简单。 在 URI 路径的某处,您在根附近插入一个版本指示符,例如v1v2,然后路径的其余部分跟随其后。 这里有一个例子:

https://traveltour.xyz/api/v1/customers

前面的例子是 API 的版本 1,版本 2 如下所示:

https://traveltour.xyz/api/v2/customers

请注意

当切换 API 版本时,缓存使用 URI 作为键来使其失效,以便缓存中的内容本身与返回的 API 的特定版本匹配。

URI 路径版本控制在其他框架(例如 PHP 中的 Laravel、Java 中的 Spring Boot 和 Ruby on Rails)中也比基于查询的版本控制或基于 header 的版本控制更常见。

稍后我们将在应用中应用 URI 版本控制。 现在让我们继续下一个流行的 API 方案或策略。

消息头版本控制

使用头进行版本控制时,您可以使用一个动词,并且使用头值来指示开发人员正在寻找的版本。

这里是你可以指定版本的头的样本内容:

GET /api/customers HTTP/1.1
Host: localhost:5001
Content-Type: application/json
x-api-version: 2

前面的头内容显示没有向 URI 添加过滤器。

这种策略很有用,因为它不会破坏 uri。 然而,在客户端使用这些类型的 api 将需要一些复杂的步骤来将正确的请求和头发送到 web 服务。

查询字符串版本控制

查询字符串版本化就是使用查询字符串指定 API 的版本。 它允许 api 的使用者在需要时更改版本。 他们可以选择旧版本或新版本的 api。

记住,如果请求中没有查询字符串,那么您应该拥有 api 的隐式默认版本。 下面是一个查询字符串版本控制的例子:

https://traveltour.xyz/api/customers?api-version=2

如您所见,?api-version=2意味着此请求是针对客户路由和控制器的版本 2 的。

好的,那么有没有办法让开发人员知道某个 API 是否不再推荐使用呢? 是的,它是一个已弃用的 API,您将在下一节中看到。

弃用 API

已弃用的 API 是指不再推荐开发人员使用的 API。 一旦某个版本的 API 在几个月内没有用户,就可以将该版本从应用中删除。

因此,将一个 API 标记为 deprecated 是非常简单的。 方法如下:

[ApiVersion("1.0", Deprecated = true)]

这是一个来自Microsoft.AspNetCore.Mvc命名空间的注释,告诉开发人员 API 版本 1 现在已弃用。

在下一节中,让我们看看如何实现版本控制、API 的弃用,以及如何将它们与 OpenAPI 集成,以便在 Swagger UI 中实现可视化表示。

API 版本集成与 OpenAPI

现在我们到了部分,我们将实现 API 版本控制,弃用 API,并将其与 OpenAPI 集成。 所有的变化都将发生在Travel.WebAPI项目内部:

  1. Go to Travel.WebAPI and install two packages. The following NuGet library adds an API versioning service for ASP.NET Core:

    Microsoft.AspNetCore.Mvc.Versioning

    下面的 NuGet 库增加了发现 api 版本的控制器和动作的元数据的功能,以及 url 和允许的 HTTP 方法:

    Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

  2. Next, let's create two new folders inside the Controller directory and name them v1 and v2. Then move all the following controllers inside v1:

    ApiControllers.cs

    TourListsControllers.cs

    TourPackagesControllers.cs

    WeatherForecastController.cs

    将每个控制器的命名空间更新为namespace Travel.WebApi.Controllers.v1,并从ApiController派生WeatherForecastController

  3. Next, we update the annotations on top of the ApiContoller abstract class in ApiController.cs. The code should be as follows:

    namespace Travel.WebApi.Controllers.v1 {     [ApiVersion("1.0")]     [ApiController]     [Route("api/v{version:apiVersion}/[controller]")]     public abstract class ApiController :       ControllerBase     {         private IMediator _mediator;         protected IMediator Mediator => _mediator ??=           HttpContext.RequestServices.GetService             <IMediator>();     } }

    我们添加了的apiVersion注释和指定的版本。 我们还用apicontroller之间的api动态版本更新了Route注释。 这两个注释还将自动应用于从ApiController类派生的任何控制器。

  4. Now let's deprecate the WeatherForecast endpoint by updating WeatherForecastController inside the Controller directory:

    namespace Travel.WebApi.Controllers.v1 {     [ApiVersion("1.0", Deprecated = true)]     public class WeatherForecastController :       ApiController     { …     } }

    该注释意味着我们显式地将WeatherForecastController标记为 deprecated。

  5. After deprecating, let's create a new version of the WeatherForecast endpoint by creating a new C# file inside the v2 directory of the controller and name it WeatherForecast.cs.

    v2文件夹中创建一个新的WeatherForecast controller文件后,用下面的代码更新代码:

    namespace Travel.WebApi.Controllers.v2 {     [ApiVersion("2.0")]     [ApiController]     [Route("api/v{version:apiVersion}/[controller]")]     public class WeatherForecastController :       ControllerBase     {       …         [HttpPost]         public IEnumerable<WeatherForecast>           Post(string city)         {             var rng = new Random();             return Enumerable               .Range(1, 5)                 .Select(index => new WeatherForecast             {                 …                 City = city             })             .ToArray();         }     } }

    如果你注意到,v1 WeatherForecastv2 WeatherForecast之间的区别是HTTP方法。 在版本 1 中,您必须发送一个GET请求来获取日期和温度数据,而在版本 2 中,您必须发送一个带有city查询参数的POST请求。 这是 API 中破坏性更改的一个极好的例子。 因此,API 必须具有的版本控制,以避免破坏正在使用WeatherForecastAPI 的第一个版本的应用。

    POST 请求query并不是一个最佳实践,因为它的非幂等性,更适合使用在一个请求,而【显示】,,【病人】和删除 idempodent 请求。 但是,带有query参数的 POST 请求是打破WeatherForecast端点的最简单方法。 所以现在请耐心听我说。****

    现在,对于 OpenAPI,让我们在Travel.WebApi的根目录下创建一个新文件夹,并将其命名为Helpers。 创建文件夹后,在文件夹中创建两个 c#文件ConfigureSwaggerOptions.csSwaggerDefaultValues.cs:

    // ConfigureSwaggerOptions.cs

    *using System; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; namespace Travel.WebApi.OpenApi {     public class ConfigureSwaggerOptions :       IConfigureOptions<SwaggerGenOptions>     { …         public void Configure(SwaggerGenOptions           options)         {       …         }         private static OpenApiInfo           CreateInfoForApiVersion             (ApiVersionDescription description)         {       …         }     } }*

    这里我们需要写两个方法。 让我们将第一个方法命名为Configure(),将命名为第二个方法OpenApiInfo()。 以下是Configure()方法的代码块:

    *public void Configure(SwaggerGenOptions options)         {             foreach (var description in               _provider.ApiVersionDescriptions)                    options.SwaggerDoc                      (description.GroupName,                        CreateInfoForApiVersion                          (description));         }*

    Configure()方法所做的就是为每一个发现的 API 版本添加一个 Swagger 文档。 下面是OpenApiInfo()方法的代码块:

    *     private static OpenApiInfo        CreateInfoForApiVersion         (ApiVersionDescription description)         {             var info = new OpenApiInfo             {                 Title = "Travel Tour",                 Version =                   description.ApiVersion.ToString(),                 Description = "Web Service for Travel                   Tour.",                 Contact = new OpenApiContact                 {                     Name = "IT Department",                     Email =                       "developer@traveltour.xyz",                     Url = new Uri                     ("https://traveltour.xyz/support")                 }             };             if (description.IsDeprecated)                 info.Description += " <strong>This API                   version of Travel Tour has                     been deprecated.</strong>";             return info;         }*

    基本上,这个代码是针对应用的titleversiondescriptioncontact namecontact emailURL等 Swagger 信息:

    // SwaggerDefaultValues.cs

    *using System.Linq; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; namespace Travel.WebApi.OpenApi {     public class SwaggerDefaultValues :       IOperationFilter     {         public void Apply(OpenApiOperation operation,           OperationFilterContext context)         {             …         }     } }*

    我们将使用此代码来替换我们在Startup.cs中的services.AddSwaggerGen()方法中编写的配置。 下面是Apply()方法的代码块:

    *public void Apply(OpenApiOperation operation, OperationFilterContext context)         {             var apiDescription =               context.ApiDescription;             operation.Deprecated |=               apiDescription.IsDeprecated();             if (operation.Parameters == null)                 return;             foreach (var parameter in               operation.Parameters)             {                 var description = apiDescription.                   ParameterDescriptions.First(                     pd => pd.Name == parameter.Name);                 parameter.Description ??=                 description.ModelMetadata.Description;                 if (parameter.Schema.Default == null                    &&                      description.DefaultValue != null)                     parameter.Schema.Default = new                       OpenApiString                 (description.DefaultValue.ToString());                 parameter.Required |=                   description.IsRequired;             }         }*

    Apply()方法允许 Swagger 生成器添加 API 浏览器的所有相关元数据。

** Now let's update the Startup.cs file. Find the AddSwaggerGen() method inside ConfigureServices and update it with the following code:

```
services.AddSwaggerGen(c =>
{
   c.OperationFilter<SwaggerDefaultValues>();
});
```

前面的代码允许 Swagger 生成器在最初使用过滤器(我们在前面创建的`SwaggerDefaultValues`)生成操作之后修改操作。

接下来,为我们之前创建的`ConfigureSwaggerOptions()`方法添加一个服务生命周期:

```
services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
```

`AddTransient()`方法放在`AddSwaggerGen()`方法块下。

现在我们可以添加我们安装的`Microsoft.AspNetCore.Mvc.Versioning`中的`ApiVersioning`配置。 把它放在`AddTransient()`方法下面:

```
services.AddApiVersioning(config =>
            {
                config.DefaultApiVersion = new
                  ApiVersion(1, 0);
                config.
                 AssumeDefaultVersionWhenUnspecified 
                   = true;
                config.ReportApiVersions = true;
            });
```

上述代码在服务集合中添加了`ApiVersioning` `ApiVersioning`声明 API 的默认版本,*声明 API 支持的版本* API 响应的头文件中。

接下来,我们从安装的`Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer`NuGet 包中添加`API Explorer` 把它放在`AddApiVersioning()`方法下面:

```
services.AddVersionedApiExplorer(options =>
{
   options.GroupNameFormat = "'v'VVV";
   options.SubstituteApiVersionInUrl = true;
});
```

这段代码添加了一个 API 浏览器,它可以理解应用中的 API 版本。 它添加了这样的格式:`"'v'major[.minor][-status]"`

现在在`Configure()`方法中添加一个参数。 命名为`provider`,类型为`IApiVersionDescriptionProvider`,如下所示:

```
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
{

}
```

前面的代码发现并描述了应用中 API 版本的信息。

让我们用下面的代码来更新`Configure()`方法中的`UseSwaggerUI()`方法:

```
app.UseSwaggerUI(c =>
{
foreach (var description in provider.ApiVersionDescriptions)
          {
                c.SwaggerEndpoint($"/swagger/
                {description.GroupName}/swagger.json",
                description.GroupName.ToUpperInvariant
                ());
         }
});
```

这段代码将通过循环提供者为每个发现的 API 版本构建一个 Swagger 端点。

*   Now let's run the application and see the results of the code we've added to our application. Let's look at Swagger UI and test the version 1 `WeatherForecast` API and the version 2 `WeatherForecast` API to see if they are working correctly if we send a request.

从下面的截图*图 8.1*中可以看到,你可以选择你想要检查的 api 的版本:*

*Figure 8.1 – Version picker

图 8.1 -版本选择器

图 8.1 显示了一个下拉菜单,让您选择要查看的 api 版本。

现在让我们看看版本 1 中的WeatherForecast控制器。 你会注意到在下一张图片图 8.2中,版本 1WeatherForecast在 Swagger UI 如何呈现端点细节方面与版本 2 不同:

Figure 8.2 – Deprecated API

图 8.2 -弃用 API

图 8.2 中中的默认样式,带有褪色的颜色和一个插入的端点名称,告诉开发人员该端点已经被标记为 deprecated。

现在,让我们尝试WeatherForecast端点的版本 2,这是一个 POST 请求,需要添加一个query参数的关键和城市的价值正如它的名字你想用你的请求的数据:

**Figure 8.3 – Version 2 of WeatherForecast

图 8.3 -天气预报第二版

在 Swagger UI 的帮助下,我们可以方便地在输入框中插入查询参数。 让我们在文本框中键入Oslo并执行它。

让我们看看点击Execute按钮后端点的响应:

Figure 8.4 – WeatherForecast version 2 response

图 8.4 -天气预报版本二的回应

您将注意到,响应现在将作为数组中WeatherForecast对象的city属性包含您的输入。 现在可以看到,新端点正在正常工作。

以上就是 API 版本控制的内容,现在我们可以开始登录应用,这也是一个令人兴奋的主题。

登录 ASP.NET Core

有时我们假设一个理想的世界,在那里我们的后端应用能够成功运行。 然而,在现实世界中,总有一些事件或错误是可以预期的。 例如,我们到 SQL Server 的连接可能会因为任何原因而中断。 为了做好我们的工作,作为最佳实践,我们应该预见到错误并适当地处理它们。

日志是在应用运行时打印输出行的地方,这些输出行给您一些关于应用的使用、性能、错误和诊断的信息。 简而言之,输出告诉我们应用内部发生了什么。

我们如何在 ASP 中做到这一点.NET Core?

登录 ASP.NET Core

如果你有曾经用 ASP 做过任何工作.NET Core,你可能见过ILogger接口。 也许,您甚至知道 ASP.NET Core 默认内置了登录功能。 但你知道我们可以配置或替换它吗? 是的,我们可以使用第三方库,如SerilogNLog,稍后您将看到。 但是让我们讨论一下日志记录的基础知识,也就是日志记录的级别。

以下是定义的ILogger接口的日志级别:

  • Trace:记录中最详细的消息,如敏感的应用数据。 但是,在缺省情况下,消息是禁用的,并且不能在生产环境中启用。 输出目标是一个跟踪侦听器。
  • Debug:开发过程中用于交互调查的日志。 在几乎所有情况下,这都应该是您的日志级别。
  • Information:监控应用一般流程的日志,在生产环境中需要这些日志来查看应用的运行状态。
  • Warning:应用中出现异常、意外或异常事件的日志。 这些事件不会停止应用。
  • Error:当执行流由于失败而停止时,突出显示的日志。 哪些错误发生得最多?
  • Critical:显示系统崩溃或不可恢复的应用或灾难的日志。

现在我们知道了日志的级别。 是它吗? 不,还没有。 下面是一些日志输出的例子:

Microsoft.Hosting.Lifetime: Information: Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]

前面的日志的例子告诉我们,端口上运行的计算机,但是成堆的这些文本日志将给我们很难弄清楚发生了什么在我们的应用。更不用说,这是费力而耗费时间来得到你想要的信息,极大的日志数据。

那么,如果有一种简单的方法可以从日志中查询特定的数据呢? 这是结构化的记录。

什么是结构化日志?

结构化日志为全结构化 JSON 对象的日志。 例如,我们可以使用 Kibana、Elmah、Seq、Stackify 等工具来过滤和分析日志,而不需要编写正则表达式。 这种类型的日志是很好的,因为突然之间,找到关于趋势和人们如何使用我们销售的产品的各种信息变得微不足道了。

所以,现在的问题是我们如何在 ASP 中进行结构化日志记录.NET Core?

Serilog versus NLog

SerilogNLog是。net 中两个可以使用的流行的日志框架,它们都有很多文档。 因此,您可以很容易地在互联网上找到使用 Serilog 或 NLog 在应用中需要做什么。

这两个日志记录器有相似之处,比如基于 c#的配置和结构化日志记录。 然而,根据我的经验,我发现在 Serilog 中设置结构化日志比在 NLog 中更容易。 类似地,Serilog 使用 Fluent API 进行基于 c#的配置。

Serilog 和 NLog 在日志级别上有一些相似之处。 日志级别在Serilog致命的,错误,警告、【显示】信息、调试,【病人】详细。 虽然NLog致命的,【t16.1】错误,警告,信息调试,和跟踪。

这些是您在其他编程语言和框架中也经常看到的日志级别。

因此,我们将在应用中使用 Serilog。 在我看来,Serilog 具有结构更好的日志支持,易于配置。 Serilog 中的接口对开发人员友好,使用 Serilog 的开发人员比使用 NLog 的开发人员多,这增加了开发人员体验 Serilog 的机会。

给你 Serilog 名字的起源,它来自术语序列化日志。 好的,让我们开始将 Serilog 添加和配置到应用中。

配置 Serilog

现在我们需要添加一些 NuGet 包,以便正确地使用和配置 Serilog。 我们将把这些包添加到Travel.WebApi并更新Program.cs文件:

  1. Here are the NuGet packages that we are adding. The following package allows you to use Serilog in ASP.NET Core:

    Serilog.AspNetCore

    下面的包允许您使用appsettings.json配置 Serilog:

    Serilog.Settings.Configuration

    下面的包允许您记录异常详细信息:

    Serilog.Exceptions

    下面的包允许你以一种简单而紧凑的 json 格式记录事件:

    Serilog.Formatting.Compact

    让我介绍一下 Serilog 中的丰富内容。 富集剂可以修改、添加、删除 Serilog 中的属性。 充实器通过实现ILogEventEnricher实现此功能,该功能在日志记录期间应用,为日志事件提供额外的信息。

  2. Here are some of the enrichers that we need. Let's also install them in the Travel.WebApi project.

    下面的包用System.Environment发出的属性丰富了 Serilog 日志事件:

    Serilog.Enrichers.Environment

    下面的包为 Serilog 添加了一个进程丰富器:

    Serilog.Enrichers.Process

    下面的包用当前线程发出的属性丰富了 Serilog 日志事件:

    Serilog.Enrichers.Thread

  3. Now let me also introduce you to sinks in Serilog. Sinks allow you to direct your logs to storage in various formats. You can target different storages to log events. There are too many sink choices to fit on a single page so please go here to see all of them: https://github.com/serilog/serilog/wiki/Provided-Sinks.

    下面的包允许您在文本文件中编写 Serilog 事件。 它可以是纯文本或 JSON 格式:

    Serilog.Sinks.File

    下面的包允许你在一个 SQLite 数据库中编写你的 Serilog 事件:

    Serilog.Sinks.SQLite

    现在我们可以进入下一节,在这里我们将编写一些代码。

  4. After adding the packages, let's update the Program.cs file of the Travel.WebApi project.

    用以下代码更新Main()方法:

    public static int Main(string[] args)         {             var name = Assembly.GetExecutingAssembly().GetName();             Log.Logger = new LoggerConfiguration()                 .MinimumLevel.Debug()                 .CreateLogger();             // Wrap creating and running the host in a               try-catch block             /* CreateHostBuilder inside a try catch */         }

    更新后的代码块以一种简单的方式实现了 Serilog。 您可以在这里看到来自Serilog名称空间的LoggerConfiguration构造。 您还将在这里看到将被传递到接收器的日志事件的MinimumLevel。 然后编写CreateLogger()方法,该方法使用最小级别、充实器和下沉器创建记录器。

  5. Now let's add our enrichers and sinks by updating LoggerConfiguration. Let's insert the enrichers and the sinks between the MinimumLevel() and CreateLogger() methods like so:

    Log.Logger = new LoggerConfiguration()                 .MinimumLevel.Debug()                 .Enrich.FromLogContext()                 .Enrich.WithExceptionDetails()                 .Enrich.WithMachineName()                 .Enrich.WithProperty("Assembly",                    $"{name.Name}")                 .Enrich.WithProperty("Assembly",                   $"{name.Version}")                 .WriteTo.SQLite(                         Environment.CurrentDirectory +                           @"/Logs/log.db",                            // For Mac and Linux users                                                          // Environment.CurrentDirectory + @"\Logs\log.db", // For Windows users                         restrictedToMinimumLevel:                           LogEventLevel.Information,                         storeTimestampInUtc: true)                 .WriteTo.File(                         new CompactJsonFormatter(),                         Environment.CurrentDirectory +                           @"/Logs/log.json",                            // For Mac and Linux users                                                          // Environment.CurrentDirectory + @"\Logs\log.json", // For Windows users                         rollingInterval:                           RollingInterval.Day,                         restrictedToMinimumLevel:                           LogEventLevel.Information)                 .WriteTo.Console()                 .CreateLogger();

    现在我们正在使用从 NuGet 包管理器中安装的所有充实器和接收器。 用您各自的操作系统替换 SQLite 的路径,因为获取目录或文件的路径取决于您所使用的操作系统。

  6. Let's now write CreateHostBuilder wrapped with a try catch, like so:

    try             {                 Log.Information("Starting host");                 CreateHostBuilder(args).Build().Run();                 return 0;             }             catch (Exception ex)             {                 Log.Fatal(ex, "Host terminated                   unexpectedly");                 return 1;             }             finally             {                 Log.CloseAndFlush();             }

    很多 Serilog 同步会异步地将这些事件发送到不同的目的地,当你的应用出于任何原因退出时,你真的想要那些闪光。 返回0表示程序成功; 否则,程序将带着错误或异常退出。

  7. The last thing to do before we test our Serilog is to include the CreateHostBuilder definition, like so:

    public static IHostBuilder CreateHostBuilder(string[] args) =>             Host.CreateDefaultBuilder(args)                 .UseSerilog()                 .ConfigureWebHostDefaults(webBuilder                   =>                 {                     webBuilder.UseStartup<Startup>();                 });

    我们在这里将 Serilog 设置为默认日志提供程序。 这是可能的,因为ILogger接口允许覆盖它。

  8. Now let's run the application again to see the log events in the JSON file and SQLite database file; both files will be generated automatically.

    应用再次运行后,使用 Swagger UI 向WeatherForecast v2端点或TourList v1端点发送请求。 在图 8.5中,您将看到自动生成的 SQLite 文件和出现在新Logs目录中的 JSON 文件。 这些文件包含 Serilog 产生的日志事件:

Figure 8.5 – SQLite file and JSON file in the Logs directory

图 8.5 -日志目录中的 SQLite 文件和 JSON 文件

让我们使用 SQLite 浏览器或 SQLiteStudio 来查看 SQLite 文件。 在图 8.6中,你会注意到RenderedMessageProperties是 JSON 格式的:

Figure 8.6 – Log events in the SQLite file

图 8.6 -在 SQLite 文件中记录事件

格式将能够帮助任何数据可视化工具在 UI 图中呈现数据。 它还有助于任何数据可视化工具查询您想从日志事件中提取的特定细节。

现在让我们检查 JSON 文件。 你还会在图 8.7中看到日志事件以 JSON 格式存储:

Figure 8.7 – Log events in JSON format

图 8.7 -以 JSON 格式记录事件

您将注意到日志事件中有@ signs。 符号前缀@是一个操作符,它告诉 Serilog 序列化传入的对象,而不是使用ToString()

请注意

数据库 URL 或 URL 路径必须根据应用环境(如测试、验收和生产环境)而变化。 我在教程中使用了 c#,这样我们就可以悬停鼠标,阅读 NuGet 包接口的细节。 它还为我们在编写 c#代码时提供了智能感知。 但是,您应该在appsettings.json中配置 Serilog。

好吧。 让我们快速回顾一下你在这里学到的东西。

总结

至此,您已经了解到 API 版本控制在 API 开发之初至关重要,它可以使 API 灵活应对未来的任何更改。

您已经学习了如何使用 API 版本控制策略,即 URI 路径版本控制、头版本控制和查询字符串版本控制。 URI 路径是所有方案中最常见的。 API 版本控制的一部分是弃用 API 并将其与 OpenAPI 集成,这有助于开发人员在 Swagger UI 中查看所有 API 的详细信息。

您还学习了在应用中记录日志的重要性,以及在 ASP 中使用哪个库。 净的核心。 您现在还知道了什么是结构化日志记录,以及如何将其保存到数据库中,以便可以轻松地查询希望在应用中研究的内容。

在下一章中,我们将为我们的 ASP 增加安全性.NET Core 应用使用 JWT,或 JSON web 令牌。***