65.9K
CodeProject 正在变化。 阅读更多。
Home

一个功能齐全的 ASP.NET Core 数据服务 Web API,支持 IIS、IIS Express 和 HTTP.sys 启动主机

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2024 年 9 月 9 日

CPOL

26分钟阅读

viewsIcon

4163

downloadIcon

6

在 Windows 平台上,使用 ASP.NET Core 8.0 和各种启动主机实现功能齐全的数据服务 Web API 的实现、设置、教程和问题解决方案。代码向后兼容 ASP.NET Core 6.0 和 7.0。

 

引言

数据服务 Web API 的主要结构和数据访问部分基于我之前的文章,并更新了 ASP.NET Core 版本、代码中的重大更改以及新的或扩展功能。本文不会重复大多数主题,例如通用配置、多层数据访问相关过程、发布网站项目等。请参阅我之前的文章以获取那里描述的主题。此处可下载的源代码文件也包括所有之前和最新的更改。

本文包含以下主题

初始项目设置与运行

数据服务 Web API 应用程序和源代码目前仅为 Microsoft Windows 系统设计。已发布源代码文件的设置处于基本模式,即禁用了 HTTPS 和基本认证,以方便应用程序在开始时在任何本地环境中设置和运行。功能和配置将在本文后面的逐步说明中添加或启用。

以下是在本地计算机上设置项目的先决条件

  • Microsoft Windows 10 或 11
  • Visual Studio 2022,带有 ASP.NET 和 Web 开发工作负载
  • 从“程序和功能”>“打开或关闭 Windows 功能”安装的 Internet Information Services (IIS)

另请阅读此 Microsoft 文档,了解 ASP.NET Core 中与多个环境相关的设置。

当使用 Visual Studio 打开应用程序解决方案 SM.Store.CoreApi.sln 并转到解决方案资源管理器时,将显示两个网站项目,如下面的屏幕截图突出显示:SW.Store.Api.Web 针对传统 IIS 或 IIS Express Web 服务器,SM.Store.Api.Http 针对 HTTP.sys Web 服务器。有关 HTTP.sys 的详细信息,请参阅使用 HTTP.sys 的网站项目部分。

通过选择 Visual Studio 工具栏上的“启动项目”和“调试目标”下拉菜单中的组合,可以轻松切换使用 IIS、IIS Express 或 HTTP.sys 运行应用程序。在重新生成解决方案后,使用 IIS Express 或 HTTP.sys 运行应用程序不需要额外的设置步骤。本文稍后将介绍使用带有自定义域名的本地 IIS 配置和启动应用程序。

1.  IIS Express

  • Visual Studio 工具栏上的选择如下图所示

 

  • 要运行项目,请单击“IIS Express”按钮或按 F5

2. HTTP.sys

  • Visual Studio 工具栏上的选择如下图所示

 

  • 要运行项目,请单击“http”按钮或按 F5

根据原始设置,内存中的 SQL Server 数据库用于提供演示数据。因此,在初始设置阶段,不需要设置任何物理数据库即可从 Web API 调用中获取数据结果。本文稍后将详细介绍使用 SQL Server LocalDB 作为数据源的主题。

应用程序中内置了 startup.html,它默认调用 API 方法以查找数据并加载页面。运行应用程序后,启动页面应在浏览器中显示如下。

如果在浏览器地址栏中将 startup.html 替换为 swagger 并按 Enter,将显示 API 文档 Swagger 页面。有关 Swagger 的更多信息将在后面的部分使用 Swagger 进行文档编写中介绍。从下载的源代码中,TestCasesForDataServices_8.0.txt 文件包含可用于测试 API 调用请求和响应的输入参数示例。Postman 也是进行 API 调用的好工具(请参阅本文后面几个部分中的屏幕截图示例)。

友情提示:在开始任何新的运行会话之前,请清除浏览器缓存,尤其是在进行代码更改后页面上显示任何差异或意外结果时。

最小托管模型

ASP.NET Core 6.0+ 中启动过程最显著的新功能可能是最小托管模型。无论是否将其视为一项重大更改,它可能只是工作流样式和序列的更改,如下图所示。

在传统托管模型中:

回调(创建配置服务集合 > 配置)> 构建主机 > 运行应用程序

在最小托管模型中:

创建配置服务集合 > 构建主机 > 配置 > 运行应用程序

尽管新版本仍然支持传统托管模型,但本应用程序采用最小托管模型以提高代码清晰度和可扩展性。将启动过程包装在单个 Program.cs 文件中或跨多个文件并不重要,应取决于工作流需求。我仍然在 Startup.cs 文件中使用 Startup 类,因为应用程序需要多个不同启动主机的共享配置例程。Startup.cs 文件位于 SM.Store.Api.HostShared 项目中,IIS/IIS Express 和 HTTP.sys 网站项目都可以引用它。

与 ASP.NET Core 5.0 示例应用程序中的旧 Startup 类相比,旧应用程序中所有活动使用的现有项都复制到新的 Startup 类中,并进行了一些微小更改,例如将枚举 TargetType 值作为多个网站项目的标志传递给 Startup 类构造函数。

public Startup(IWebHostEnvironment env, TargetType type)
{
    WebHostEnvironment = env;
    TargetType = type;
    - - -
}

枚举中定义了两种托管目标类型

public enum TargetType
{        
    IIS_IISExpress = 0,
    HTTP_Sys = 1        
}

作为一个功能齐全的数据服务应用程序,已将这些新功能或扩展功能的配置添加到共享的 Startup 类中。有关这些功能和设置的详细信息,请参阅本文后面的相应部分。

使用 HTTP.sys 的网站项目

HTTP.sys 并非新事物,但 Visual Studio 2022 中的 ASP.NET Core Web API 项目模板应用程序将 HTTP.sys 设置为默认应用程序启动主机。似乎 Microsoft 正在尝试推荐将 HTTP.sys 用于任何新开发。根据 Microsoft 的说法,HTTP.sys 对部署很有用,在以下情况下:

  • 需要不使用 IIS 将服务器直接暴露到 Internet。
  • 内部部署需要 Kestrel 中不可用的功能。

SM.Store.Api.Http 项目的设置为启动主机指向 HTTP.sys,如下图所示。要了解 ASP.NET Core 的 HTTP.sys,请通过在 Internet 上搜索“HTTP.sys”来阅读文档和博客。

1.  在 SM.Store.Api.Http.csproj 文件中,这些行设置为 HTTP.sys 特定: 

<PropertyGroup>                          
    <TargetFramework>net8.0-windows</TargetFramework>
    <PlatformName>windows</PlatformName>
</PropertyGroup>

2. 在 Program.cs 文件的 Program 类中(没有认证的初始设置):    

builder.WebHost.UseHttpSys(options =>
{
    options.AllowSynchronousIO = false;
    options.Authentication.Schemes = AuthenticationSchemes.None;
    options.Authentication.AllowAnonymous = true;
    options.MaxConnections = null;
    options.MaxRequestBodySize = 30_000_000;
});  

3. 在 Properties/LaunchSettings.json 文件中(没有 HTTPS 的初始设置)

"profiles": {
  "http": {
    "commandName": "Project",
    "dotnetRunMessages": true,
    "launchBrowser": true,
    "launchUrl": "startup.html",
    "applicationUrl": "https://:5024",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "LOCAL"
    }
  }
}

这些是配置和运行 HTTP.sys 网站项目所需的所有设置。Startup 类中的其他设置与 IIS 或 IIS Express 网站项目的设置几乎相同。

带有自定义域名的本地 IIS

为了对网站项目进行更多选项和控制,并更接近生产场景,使用本地 IIS 托管应用程序是本地代码开发工作的最佳选择。从本文下载的数据服务 Web API 源代码包括使用带有自定义域名进程内本地 IIS。但是,它只有在本地计算机上完成这些步骤后才能工作。

1. 使用本地管理员或提升权限在记事本中打开 C:\Windows\System32\drivers\etc 文件夹中的 hosts 文件,然后将此行输入到文件中

127.0.0.1    StoreDataServiceApi

2. 在 IIS 管理器(inetmgr)中,右键单击“应用程序池”>“添加应用程序池…”,然后进行如下图所示的条目或选择。请注意,对于 ASP.NET Core Web 应用程序,必须从“.NET CLR 版本”下拉列表中选择“无托管代码”。

3. 继续右键单击“站点”>“添加网站…”,然后进行如下图所示的条目或选择。

4. 从 Visual Studio 解决方案资源管理器中打开 SM.Store.Api.Web/Properties/LaunchSettings.json 文件。这些行用于使用 IIS Web 服务器启动应用程序。目前无需更改此文件。

"profiles": {
  - - -
  "SM.Store.Api.Web": {
    "commandName": "IIS",
    "launchBrowser": true,
    "launchUrl": "startup.html",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "LOCAL"
    }
  }
},
"iisSettings": { 
  - - -
  "iis": {
    "applicationUrl": "http://StoreDataServiceApi",
    "sslPort": 0
  }
}

5. 通过下载并运行适用于 Windows 的托管捆绑包安装程序,在本地计算机上安装最新版本的 ASP.NET Core 托管捆绑包。

6. 要以调试模式运行针对本地 IIS 的应用程序,请使用 Visual Studio 执行以下步骤

  • Visual Studio 工具栏上的选择如下图所示

 

  • 单击 SW.Store.Api.Web 按钮或按 F5

本地网站上的 HTTPS

如果尚未创建基于用户的 Windows 证书 MMC 管理单元,请执行以下步骤

  • 在命令提示符或“开始”菜单 >“运行”面板中,输入 mmc 以打开通用 MMC 控制台。

  • Ctrl + M 打开“添加或删除管理单元”屏幕。

  • 从“可用管理单元”列表中选择“证书”,并将其添加到“选定的管理单元”列表中。

  • 在“证书管理单元”弹出窗口中选择“计算机帐户”,然后单击“完成”。

  • 再次从“可用管理单元”列表中选择“证书”,并将其添加到“选定的管理单元”列表中。

  • 在“证书管理单元”弹出窗口中选择“我的用户帐户”,然后单击“完成”。

  • 单击“文件”>“另存为”以保存基于用户的证书 MMC 管理单元。<文件名>.msc 文件将位于路径 C:\Users\<当前用户>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Administrative Tools 中。可以创建桌面快捷方式以便轻松访问。

本地 IIS 网站 

完成此任务最简单的方法是运行 PowerShell 脚本以获取自签名证书,然后使用 IIS 管理器进行站点绑定。

1. 打开 PowerShell 控制台并运行以下脚本,该脚本一次性创建并信任自签名证书。此操作可能需要本地管理员或提升权限。

$cert = New-SelfSignedCertificate -CertStoreLocation "Cert:\LocalMachine\My" -DnsName   "StoreDataServiceApi" -FriendlyName "StoreDataService" -NotAfter "01/01/2034" -Verbose
$DestStore = new-object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreName]::Root,"localmachine")
$DestStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$DestStore.Add($cert)
$DestStore.Close()

脚本注意事项

  • -DnsName 参数支持多个以逗号分隔的域名,例如 "name-A", "name-B"。

  • 如果将本地 IIS 配置为使用此处所示以外的域名,请将 -DnsName-FriendlyName 参数的现有值替换为更改后的名称。

  • 如果网站配置了默认的 localhost:<端口号>,而不是自定义域名,则为 -DnsName 输入 "localhost"

2. 打开证书 MMC 管理单元控制台,在“证书(本地计算机)”>“个人”和“受信任的根证书颁发机构”>“证书”中检查新创建的证书。

3. 双击 MMC 管理单元中“颁发给”列表中的 StoreDataServiceApi,然后选择“证书路径”选项卡以确认新证书名称,该名称是 PowerShell 脚本中 -FriendlyName 参数的值。

4. 打开“IIS 管理器”>“站点”>“StoreDataServiceApi”>“操作”>“绑定…”,选择或输入应如下图所示。

5. 更改 SW.Store.Api.Web/Properties/launchSettings.json 中 IIS 的行,以使用 HTTPS URL 和端口号。

"iisSettings": {
  - - -
  "iis": {
    "applicationUrl": "https://StoreDataServiceApi",
    "sslPort": 443
  }
}

6. 选择 SM.Store.Api.Web 作为启动项目,选择 SM.Store.Api.Web 作为启动主机来运行应用程序。启动页面将显示 HTTPS。

7. 将 HTTP URL 切换到 HTTPS URL,以从服务客户端(例如 Postman 或 Swagger)进行调用。这将进一步确认 HTTPS 的自签名证书和设置正确。

IIS Express 网站

根据先决条件,需要使用 Visual Studio 2022 和 ASP.NET 以及 Web 开发工作负载运行应用程序。因此,Visual Studio 附带的 IIS 10.0 Express 应该已经安装在本地计算机上。“IIS Express 开发证书”也应该自动安装。如果使用证书 MMC 管理单元检查,它会显示在“证书(本地计算机)”>“个人”>“证书”中,并“颁发给"localhost"。但是,此证书默认情况下不受信任,因为这次“受信任的根证书颁发机构”>“证书”部分中没有 "localhost"

按照以下步骤进行必要的设置

1. 更改 SW.Store.Api.Web/Properties/launchSettings.json 中 IIS Express 的行,以使用 HTTPS URL 和端口号。

"iisSettings": {
  - - -
  "iisExpress": {
    "applicationUrl": "https://: 44330",
    "sslPort": 44330
  }
}

2. 在 Visual Studio 中,选择 SM.Store.Api.Web 作为启动项目,选择 IIS Express 作为启动主机。单击 IIS Express 按钮或按 F5

3. 如果这是第一个使用 HTTPS 和 IIS Express 运行的网站项目,通常会提示信任证书。单击弹出窗口上的“”会将 IIS Express 开发证书添加到本地计算机受信任的证书存储中。如果本地计算机上已成功运行任何其他使用 HTTPS 和 IIS Express 的网站项目,则 SM.Store.Api.Web 项目应该已经正常工作,无需此步骤。

4. 如果这是第一次使用 HTTPS 和 IIS Express 设置本地开发网站,并且 SM.Store.Api.Web 项目在没有自动信任证书提示的情况下运行,则最好使用 PowerShell 执行以下脚本行,将证书添加到本地受信任存储中

$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -eq "CN=localhost"}
$DestStore = new-object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreName]::Root,"localmachine")
$DestStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$DestStore.Add($cert)
$DestStore.Close()  

5. 选择 SM.Store.Api.Web 作为启动项目,选择 IIS Express 作为启动主机来运行应用程序。启动页面将显示 HTTPS。

6. 如果遇到 IIS Express 开发证书的一些严重问题或过期,可以使用以下步骤重置证书

  • 从“本地计算机”>“个人”和“本地计算机”>“受信任的根证书颁发机构”存储中删除“localhost”证书。

  • 在“控制面板”>“程序和功能”屏幕上运行 IIS 10.0 Express 的修复。这将自动重新安装 IIS Express 开发证书

  • 重复上述第 #1 到 #4 条说明步骤。

HTTP.sys 网站

执行以下步骤来设置和运行带有 HTTPS 的 SM.Store.Api.Http 网站项目

1.  打开命令提示符,然后运行以下行。

dotnet dev-certs https --trust 

这将把颁发给“localhost”的证书添加到当前用户的“个人”>“证书”和“受信任的根证书颁发机构”存储中。从证书 MMC 管理单元检查并确认证书已在“证书 - 当前用户”部分中创建。认证路径中的名称是“ASP.NET Core HTTPS 开发证书”。

2. 打开 SM.Store.Api.Http/Properties 文件夹下的 launchSettings.json 文件,并将 applicationUrl 的值更改为使用 HTTPS,

from

"applicationUrl": "https://:5024",

to

"applicationUrl": "https://:44360",

3. 选择 SM.Store.Api.Http 作为启动项目,选择 http 作为启动主机来启动应用程序。启动页面将显示 HTTPS。

自定义错误处理和报告

为 ASP.NET Core 数据服务 Web API 实现了高级自定义错误日志和报告系统。错误处理过程有两种模式:带中断当前代码执行的全局错误处理器和带继续当前代码执行的本地错误处理器。

全局错误处理器

全局错误处理器流程从 Startup 类中的 configureExceptionHandler 自定义中间件方法启动

public void Configure(IApplicationBuilder app)
{
    //Custom global error handling, logging and reporting.
    app.ConfigureExceptionHandler();
   -  -  -
}

此中间件方法调用 SM.Store.Api.Common/ErrorLogging/ExceptionHandlerFactory.cs 中内置的 UseExceptionHandler 中间件

public static void ConfigureExceptionHandler(this IApplicationBuilder app)
{
    //Enable request buffering.
    app.Use((context, next) =>
    {
        context.Request.EnableBuffering();
        return next();
    });

    //Handle global error.
    app.UseExceptionHandler(appError =>
    {
        appError.Run(async context =>
        {
            context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.Response.ContentType = "application/json";

            var errorFeature = context.Features.Get<IExceptionHandlerFeature>();
            if (errorFeature != null)
            {
                await ProcessError.LogAndReport(context, errorFeature.Error);
            }
        });
    });
}

这个中间件例程进一步调用 ProcessError 类中的自定义 LogAndReport 方法。该方法反过来执行以下操作

  • 以文本格式构建错误消息,包括错误类别、描述、状态码和堆栈跟踪。

  • 将生成的文本行写入当前和历史日志文件。文件位置可以在 appsettings.json 中针对不同的环境进行配置。当新错误发生时,当前日志文件会刷新。历史日志文件可以根据可配置的最大文件大小设置自动存档。

  • 通过 SMTP 服务发送电子邮件。所有服务和电子邮件参数都可以在 appsettings.json 中配置。

要测试全局错误处理器和报告,请按照以下步骤操作

1. 下载smtp4dev 工具,选择首选版本。由于我们只需要确认发送和接收错误报告电子邮件,而对花哨的电子邮件 UI 不感兴趣,我建议直接下载独立的、仅限 Windows 的 GUI 版本,无需安装。直接运行解压后的可执行文件 smtp4dev.exe,并让它在后台运行。

2. 使用 Visual Studio 解决方案资源管理器或记事本打开 SM.Store.Api.Web/wwwroot 文件夹下的 startup.html。找到此行

request.open('Get', 'api/lookupcategories', true);

然后将调用的方法名改为 lookupcategorieserror。此 API 方法被有意设计为抛出带有测试错误消息的假错误。

request.open('Get', 'api/lookupcategorieserror', true);

3. 保存文件并按 F5 运行应用程序。

4. 可能需要清除浏览器缓存并刷新浏览器会话。启动页面将显示通用消息“An error occurred during your request: 500”,因为 startup.html 没有获取详细的直通错误消息。如果使用 Postman 调用相同的方法,它将显示确切的测试错误消息

5. 从物理位置 …\SM.Store.Api.Web\bin\Debug\net8.0\Logs 打开 StoreDataService.Log 文件。文件中应显示构造的错误详细信息文本行。在我这边,文件内容看起来像这样

6. 从后台打开 smtp4dev 工具。报告错误的电子邮件项应该在那里。突出显示电子邮件并单击“检查”按钮,或者如果您更喜欢将电子邮件加载到外部电子邮件提供商应用程序,则单击“查看”按钮。电子邮件的内容应该与当前错误日志文件中显示的内容几乎相同。

本地错误处理器并继续

在某些情况下,当任何代码结构(例如类构造函数、方法或属性)中发生错误时,我们需要记录或/并报告错误,但不想停止当前代码执行。本地错误处理器和报告类型旨在实现此目的。

本地错误处理程序从任何需要报告错误但继续执行剩余进程的例程的错误 catch 块启动。演示用例放置在 ProductController.Post_GetPagedProductListSp 方法中。内存数据库不支持存储过程,因此调用存储过程会因消息“Query root of type 'FromSqlQueryRootExpression' wasn't handled by provider code”而导致错误,这是调用 FromSqlFromSqlRaw 方法时任何错误的通用消息。当此错误发生时,如果我们不想为此特定情况打扰全局错误处理程序,并且需要更清晰的错误消息报告,并在响应正文中包含空数据项,我们可以实现以下代码逻辑

try
{
    - - -     
}
catch (Exception ex)
{
    //Demo for using type of local error handler and continue. 
    if (StaticConfigs.GetConfig("UseInMemoryDatabase").ToLower() == "true")
    {
        var errMsg = "Stored procedure is not supported by using InMemory database.";
        var dataInfo = "Stored Procedure Name: dbo.GetPagedProductList.";
        var asynTask = new Task(async () =>
        {
            await ProcessError.LocalErrorAndContinue(HttpContext, ex, errMsg, dataInfo);
        });
        asynTask.RunSynchronously();                    
    }
    else
    {
        //Go to normal global error handler workflow.
        throw;
    }                    
}            
return resp; 

对于本地错误处理器,构建错误消息、将详细信息保存到错误日志文件以及发送错误报告电子邮件的步骤都与 ProcessError 类中的全局错误处理器共享相同的代码库。

要测试本地错误处理程序和报告,我们可以使用 Postman 调用 getpagedproductlistbysp API 方法,其中包含 TestCasesForDataServices_8.0.txt 文件中的 POST 参数。确保 appSettings.json 中“UseInMemoryDatabase”设置为“true”。将获得空数据,响应中没有任何错误,这对于继续处理模式是正确的。错误详细信息应记录到日志文件并通过电子邮件报告。使用与全局错误处理程序和报告相同的方法检查日志文件和电子邮件。

客户端调用的基本认证

该应用程序使用基于角色的基本认证方法。这种方法的优点是我们可以为数据服务中允许的不同操作设置多个策略和角色。来自请求头的不同用户或服务帐户可能属于不同的组,因此,如果需要,可以拥有不同的权限。

在本节中,我只描述了与此应用程序使用的单个“Basic Auth Role”组相关的实现主题和测试用例。

主要实施步骤

1. 创建 Windows 本地组“Basic Auth Role”和本地用户“BasicAuthAccount”,密码在 Startup.html 中指明。将该用户添加到该组中。请注意,这仅适用于本地开发环境。对于部署在任何具有活动目录 (AD) 系统的公司测试或生产服务器上的服务应用程序,将使用 AD 组和服务帐户。

2. 在 Web 服务器中设置方案(不适用于 HTTP.sys)。

  • 对于 IIS,除了已启用的“匿名身份验证”之外,还要从“IIS 管理器”>“站点”>“StoreDataServiceApi”>“身份验证”中启用“基本身份验证”。

    请注意,如果为仅 HTTP 的站点设置基本认证,可能会显示警告消息,因为这会以纯文本形式传递凭据,从而导致安全漏洞。在生产环境中,基本认证始终使用 HTTPS。

  • 对于 IIS Express,使用记事本打开 <解决方案根文件夹>\.vs\SM.Store.CoreApi\config 下的 applicationhost.config 文件。搜索 location path="SM.Store.Api.Web"。将 basicAuthentication 行添加到 Authentication 节点中,但位于 anonymousAuthentication 行下方

    <anonymousAuthentication enabled="true" />
    <basicAuthentication enabled="true" />

3. 在启动代码中设置方案。

  • 对于 IIS 和 IIS Express,将链接的身份验证方案添加到 Startup.cs 中的构建器服务集合中

    if (TargetType == TargetType.IIS_IISExpress)
    {
        //Register authentication scheme that takes the setting from IIS/IIS Express.
        services.AddAuthentication(IISServerDefaults.AuthenticationScheme);
    }
  • 对于 Http.sys,更改 SM.Store.Api.Http/Program.cs 中的选项设置

    builder.WebHost.UseHttpSys(options =>
    {
        options.AllowSynchronousIO = false;
        options.Authentication.Schemes = AuthenticationSchemes.Basic; //Value before change: None
        options.Authentication.AllowAnonymous = true;
        options.MaxConnections = null;
        options.MaxRequestBodySize = 30_000_000;
    });
    

4. 从启动代码中向服务集合添加一个名为“RoleBasedBasicAuth”的策略。此策略将可配置的“AuthRoleGroup”设置为必需角色,这意味着请求头中的用户身份必须属于 Windows 安全或 AD 系统的该组才能通过授权检查。此代码片段适用于任何 IIS、IIS Express 或 Http.sys 网站项目。

var authRoleGroup = StaticConfigs.GetConfig("AuthRoleGroup") ?? "Basic Auth Role";
services.AddAuthorization(options =>
{
    options.AddPolicy("RoleBasedBasicAuth", policy =>
      policy.RequireRole(authRoleGroup));
});

5. 将 CustomerAuthorizeAttribute.cs 添加到应用程序中。在下载的源代码中,此文件位于 SM.Store.Api.Common/Classes/BasicAuth 文件夹中。我们只需要关注构造函数。此属性类接受一个可选参数用于策略名称,该参数与多策略场景兼容。

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public CustomAuthorizeAttribute(string policy = "RoleBasedBasicAuth")
    {           
        this.Policy = policy;
    }      
}

6. 在需要授权访问的 API 控制器或方法中,添加 [CustomAuthorize] 属性,带或不带(使用默认值)策略名称参数。

namespace SM.Store.Api.Web
{  
    [CustomAuthorize]
    [ApiController]
    [Route("api")]
    public class LookupController : ControllerBase
    {
        - - -
    }
}

7. 使用 IClaimsTrasformation 将自定义声明身份与生成的令牌添加到声明主体中。在本应用程序中,在 Startup.cs 中创建了附加类 ClaimsTransformer 来完成此任务。

//Basic Auth claim transformation.
public class ClaimsTransformer : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("now", DateTime.Now.ToString()));
        return Task.FromResult(principal);
    }
}

8. 声明转换服务需要添加到 Startup 类中的服务集合中

//Add Basic Authentication claim transformation.
services.AddTransient<IClaimsTransformation, ClaimsTransformer>();

本地运行不需要认证?

在本地代码工作和测试周期中,基本认证总是被强制执行,这很烦人。为了绕过本地环境中的授权检查器,一个临时的解决方案是注释掉 API 控制器或方法顶部的 [CustomAuthorize] 属性。然而,这是一种不明智或最后的行为。我们需要在实现步骤 #3 中更新代码逻辑,有条件地调用 policy.RequireAssertion 方法,为本地代码工作环境设置直通策略。

//API basic auth.
var authRoleGroup = StaticConfigs.GetConfig("AuthRoleGroup") ?? "Basic Auth Role";
if (!WebHostEnvironment.IsEnvironment("LOCAL"))
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("RoleBasedBasicAuth", policy =>
            policy.RequireRole(authRoleGroup));
    });
}
else
{
    //Bypass auth for LOCAL environment;
    services.AddAuthorization(options =>
    {       
        options.AddPolicy("RoleBasedBasicAuth", policy =>
           policy.RequireAssertion(ahc => true));           
    });
} 

测试用例

1. 无论使用任何启动主机,并且 launchSettings.json"ASPNETCORE_ENVIRONMENT" 的值设置为 "LOCAL",调用 API 方法(无论是加载启动页面还是使用 Postman)都应该顺利进行,因为本地环境绕过了基本认证过程。

2. 将 launchSettings.json 中任何启动主机的 "ASPNETCORE_ENVIRONMENT" 设置为 "DEV""PROD",然后运行应用程序,无论是否输入帐户信息。

  • 启动页面无认证:原始 Startup.html 文件中带有用户名和密码的请求行被注释掉了。页面将加载并提示凭据。

    如果单击“取消”按钮,将呈现 401 错误(未授权)。

  • 启动页面带认证:在 startup.html 中,注释掉现有请求行,并启用带用户名和密码的行

    request.open('Get', 'api/lookupcategories', true, "BasicAuthAccount", "Hb87j#G34asYm&f%Op");
    
    //request.open('Get', 'api/lookupcategories', true);

    保存文件,清除浏览器缓存,然后刷新浏览器屏幕。页面应正常渲染。

  • 不带认证的 Postman:从请求“Auth”选项卡中选择“No Auth”。单击“发送”按钮将收到“401 unauthorized”错误消息。

  • 带认证的 Postman:从请求“Auth”选项卡中选择“Basic Auth”。输入与 startup.html 中相同的用户名和密码。单击“发送”按钮。调用将成功,数据项将正常加载。

使用 Swagger 进行文档编写

Swagger UI 不仅可以用作公开 API 规范的文档工具,还可以用作在代码开发工作中调用 API 方法的测试客户端。Visual Studio 2022 中的 ASP.NET Core Web API 项目模板包含了 Swagger UI,因此开发人员可以轻松地将其用于任何新项目和应用程序。

使用 Swagger UI 的代码简单明了。在 Visual Studio 模板应用程序中,Program.cs 中的这四行代码充当 Swagger UI 的基本模型

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
- - -
app.UseSwagger();
app.UseSwaggerUI(); 

在下载的源代码中,向 Startup 类添加了两组额外的代码片段,以扩展 Swagger UI 的功能。

1. 自定义 Swagger 页面标题和描述文本显示

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
    {
        Title = "Store Data Service API",
        Version = "v1",
        Description = "ASP.NET Core data service application",
    });
    - - -
});

有关自定义标题和描述文本,请参阅下一节的屏幕截图。

2. 基本认证设置和授权按钮

使用以下代码,Swagger UI 可以处理带有基本认证安全检查器的请求。它还启用 Swagger 页面中的“授权”按钮和相关工作流。

services.AddSwaggerGen(options =>
{
    - - -
    options.AddSecurityDefinition("basic", new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        Scheme = "basic",
        In = ParameterLocation.Header,
        Description = "Basic Authorization header using the Bearer scheme."
    });

    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "basic"                            
                }
            },
            new string[] {}
        }
    });                
}); 

“授权”按钮显示在 Swagger 页面的右侧

在选择要调用的任何 API 方法之前,我们可以通过单击“授权”按钮预设用户或服务帐户,并保存帐户信息以在整个会话中调用多个 API 方法。

使用 SQL Server LocalDB

SQL Server LocalDB(官方名称为 SQL Server Express LocalDB,本文中也简称为 LocalDB)专为本地开发环境设计。它由 Visual Studio 作为数据存储和处理工作负载的一部分安装,或由从 SQL Server Express 版安装包中提取的独立 SqlLocalDB.msi 文件安装。LocalDB 与 SQL Server 系列中的其他版本有主要区别:

  • 它作为类似 API 的桌面程序运行,而不是像其他 SQL Server 版本那样的 Windows 服务。

  • 实例是在当前用户上下文/帐户中创建和运行的,除了在本地计算机上为多个用户共享和取消共享实例的命令外,不涉及管理员或提升权限。

  • 按需管理会在需要时自动启动实例,并在超时期间不使用时关闭实例。

  • 数据库工具是私有的、仅限本地的,与任何网络资源或操作无关,并且不可能进行任何来自网络的远程连接。

LocalDB 的这些功能使其非常适合替代 SQL Server Developer 或 Express 版本,用于需要本地测试数据库但限制可能在任何本地计算机上运行面向网络的工具的开发工作。

将 Web API 连接到 LocalDB

1. localDB 的安装可能不费力。如果尚未安装,请安装 LocalDB,或使用 Visual Studio 安装的版本。有关说明,请参阅此文档

2. 打开命令提示符,通过执行以下行检查 LocalDB 的自动实例。实例名称应显示为 MSSQLLocalDB

sqllocaldb info

3. 运行此命令启动 LocalDB 实例。

sqllocaldb start MSSQLLocalDB

此步骤是可选的,因为当任何指向 LocalDB 实例的应用程序由当前用户启动时,如果实例处于停止状态,它将自动启动。

4. 如果尚未安装,请安装SQL Server Management Studio (SSMS)。

5. 使用 LocalDB 实例 (localdb)\MSSQLLocalDB 打开 SSMS。如果这是一个新实例,它应该没有客户数据库。

6. 使用 Visual Studio 打开数据服务 Web API 解决方案,转到 appSettings.json

  • 将“UseInMemoryDatabase”的值更改为“false”。

  • 如果需要将数据库 mdfldf 文件创建在用户定义的 位置,请将此 name=value 对添加到 ConnectionStrings.StoreDbConnection JSON 项中(创建数据库后可以删除此行)。否则,数据库文件将默认创建在 C:\Users\<用户>\Documents 文件夹中。

    AttachDBFilename=<drive>:\\<parent-directory-path>\\MSSQLLocalDB\\StoreCF8.mdf;

7. 选择 IIS Express 或 HTTP.sys 作为启动主机并运行应用程序。如果没有出现错误,StoreCF8 数据库应该会自动由内置数据库初始化器(通常只用于非生产环境)创建。应用程序现在使用 LocalDB 数据库作为数据提供程序。

8. 使用本地 IIS 和 LocalDB 运行网站项目时,可能会由于某些环境或用户帐户设置问题而发生错误。有关详细信息和解决方案,请参阅本地 IIS 相关错误主题。

实例启动失败

我们最常看到的错误消息可能是“在建立与 SQL Server 的连接时发生网络相关或实例特定的错误。找不到服务器或无法访问。。”。其他描述也可能存在,例如“无法创建自动实例”或“SQL Server 进程启动失败”。

可能是其他人(例如域管理员而非实际机器所有者)安装了 LocalDB。自动实例将在该安装程序的上下文中创建。除非在计算机中共享该实例,否则拥有机器的用户无法访问不由该用户创建的实例。在这种情况下,请尝试通过运行以下命令创建当前用户自己的实例

sqllocaldb create MSSQLLocalDB

如果收到“实例已存在”消息,请在再次运行创建实例命令之前运行以下两行命令。

sqllocaldb stop MSSQLLocalDB
sqllocaldb delete MSSQLLocalDB

不建议在这种情况下共享实例,因为在当前用户模式下,用户很难管理从其他物理用户帐户共享的实例。

如果修复尝试后问题仍然存在,删除和重新创建自动实例也是任何损坏实例的最后手段。通常,开发人员会经常备份数据库或存档数据库 mdfldf 文件,以防重新创建新实例时恢复数据库。

本地 IIS 相关错误

如果在使用 IIS 和 LocalDB 时出现“发生网络相关或实例特定错误。。”,请尝试理解并按照以下说明修复错误。

1. 当应用程序池使用默认的 ApplicationPoolIdentity 时,需要从 IIS 设置中启用完整的用户配置文件。

  • 使用具有本地管理员或提升权限的记事本,打开路径 C:\Windows\System32\inetsrv\config 中的 applicationHost.config 文件。

  • 搜索并找到具有属性 "setProfileEnvironment" 的节点。

  • "setProfileEnvironment" 的值从 "false" 更改为 "true"

  • 文件中更新的 XML 节点应如下所示

    <applicationPoolDefaults managedRuntimeVersion="v4.0">
        <processModel identityType="ApplicationPoolIdentity" loadUserProfile="true" setProfileEnvironment="true" />
    </applicationPoolDefaults>                                                          

2. 使用默认的 ApplicationPoolIdentity 会导致从不同的用户帐户访问 LocalDB,因此可能会导致实例错误,因为单个 LocalDB 实例只能由当前用户访问。此问题可能不会出现在较新版本的 ASP.NET Core 和 Entity Framework Core 中进行数据访问。但如果发生,请尝试使用此命令创建一个共享实例

sqllocaldb share MSSqlLocalDB SharedLocalDb

注意:对于 sqllocaldb 命令行实用程序,只有 shareunshare 命令需要本地管理员或提升权限。

然后,共享实例名称可用于 LocalDB 连接

(localdb)\.\ SharedLocalDB

3. 如果在错误消息文本中出现“用户 登录失败”,尤其是在旧版本的 ASP.NET Core 或 Entity Framework Core 中,则需要将应用程序池帐户添加到 LocalDB 中,以进行登录认证和数据库访问授权。

要将 IIS APPPOOL\<app-pool-name> 添加到 LocalDB 中,请按照我之前的文章中“使用 IIS Express 和本地 IIS”>“映射应用程序池帐户”部分中的步骤操作。

或者,在 SSMS 中执行以下 SQL 脚本可以编程方式将用户帐户映射到此 Web API 应用程序的 LocalDB 实例和数据库

USE [master]
GO
IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = 'IIS APPPOOL\StoreDataServiceApiPool')
BEGIN
    CREATE LOGIN [IIS APPPOOL\StoreDataServiceApiPool] FROM WINDOWS WITH DEFAULT_DATABASE=[StoreCF8],
    DEFAULT_LANGUAGE=[us_english] 
END
GO
USE StoreCF8
GO
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'IIS APPPOOL\StoreDataServiceApiPool')
BEGIN
    CREATE USER [IIS APPPOOL\StoreDataServiceApiPool] FOR LOGIN [IIS APPPOOL\StoreDataServiceApiPool]
        WITH DEFAULT_SCHEMA=[dbo]
    EXEC sp_addrolemember N'db_datareader', N'IIS APPPOOL\StoreDataServiceApiPool'
        EXEC sp_addrolemember N'db_datawriter', N'IIS APPPOOL\StoreDataServiceApiPool'END;
GO
GRANT EXECUTE TO [IIS APPPOOL\StoreDataServiceApiPool];
GO

超时问题

如前所述,LocalDB 实例可以根据需要进行开启和关闭。在某些情况下,当客户端应用程序发出调用时,实例可能在超时期限后无法自动开启,从而导致相同的“实例启动失败”错误。任何 LocalDB 实例的默认超时期限仅为 5 分钟。

在使用较新版本的 Entity Framework Core 处理客户端调用的应用程序中,超时情况可能很少见。但是,如果遇到此类问题,使用 SSMS 运行以下脚本可以延长 LocalDB 实例的超时期限。最大值为 65535 分钟,大约为 45 天,因此,相对于本地计算机而言,几乎没有超时。   

USE [master]
GO
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
GO
--maximum value: 65535 min
EXEC sp_configure 'user instance timeout', 65535;
RECONFIGURE;
GO

摘要

本文及附带的源代码介绍了 ASP.NET Core 数据服务 Web API 应用程序的许多主要主题,这些主题在我之前使用旧版 ASP.NET Core 的文章中未曾描述。该应用程序的开发模型还涵盖了同一 Visual Studio 解决方案中的多个启动主机,但每种类型的网站项目都可以单独构建和发布。许多部分提供了详细的分步教程、测试用例和问题解决方案,这些将有助于开发人员在相关代码工作中受益。读者可能还会对此处展示的许多功能的实现感兴趣。祝您编程愉快!

历史

  • 2024 年 9 月 9 日
    • 初次发布

 

一个功能齐全的 ASP.NET Core 数据服务 Web API,支持 IIS、IIS Express 和 HTTP.sys 启动主机 - CodeProject - 代码之家
© . All rights reserved.