使用 Azure Functions 和 API 管理集成在 Visual Studio 中创建无服务器 API

REST API 通常是使用 OpenAPI 定义(以前称为 Swagger)文件描述的。 此文件包含有关 API 中的操作以及 API 的请求和响应数据应采用哪种结构的信息。

本教程介绍如何执行下列操作:

  • 在 Visual Studio 中创建代码项目
  • 安装 OpenAPI 扩展
  • 添加 HTTP 触发器终结点,其中包括 OpenAPI 定义
  • 使用内置的 OpenAPI 功能在本地测试函数 API
  • 将项目发布到 Azure 中的函数应用
  • 启用 API 管理集成
  • 下载 OpenAPI 定义文件

创建的无服务器函数将提供一个 API,可让你确定风力涡轮机的紧急维修是否具有成本效益。 由于你在消耗层创建函数应用和 API 管理实例,因此按最低成本完成本教程。

先决条件

创建代码项目

Visual Studio 中的 Azure Functions 项目模板创建了一个项目,该项目可发布到 Azure 中的函数应用。 你还将根据支持 OpenAI 定义文件(以前称为 Swagger 文件)生成的模板创建 HTTP 触发的函数。

  1. 在 Visual Studio 菜单中,选择“文件”>“新建”>“项目”

  2. 在“创建新项目”中,在搜索框中输入“functions”,选择“Azure Functions”模板,然后选择“下一步”。

  3. 在“配置新项目”中输入项目的项目名称(例如 TurbineRepair),然后选择“创建” 。

  4. 对于“创建新的 Azure Functions 应用程序”设置,请选择 Functions 辅助角色的以下选项之一,其中选择的选项取决于所选进程模型:

    .NET 8.0 独立(长期支持):你的 C# 函数将在独立的辅助角色模型中运行,这是推荐的做法。 有关详细信息,请参阅独立辅助角色模型指南

  5. 对于其余选项,请使用下表中的值:

    设置 说明
    函数模板 Empty 此操作会创建一个不包含触发器的项目,通过此方法,可以在稍后添加它时更好地控制 HTTP 触发函数的名称。
    将 Azurite 用于运行时存储账户 (AzureWebJobsStorage) 已选中 可以使用仿真器在本地开发 HTTP 触发器函数。 由于 Azure 中的函数应用需要存储帐户,因此在将项目发布到 Azure 时会分配或创建一个存储帐户。
    授权级别 函数 Azure 中运行的客户端在访问终结点时必须提供密钥。 有关详细信息,请参阅授权级别
  6. 选择“创建”以创建函数项目。

接下来,可通过安装适用于 Azure Functions 的 OpenAPI 扩展来更新项目,从而在应用中实现 API 终结点的可发现性。

安装 OpenAPI 扩展

要安装 OpenAPI 扩展,请:

  1. 从“工具”菜单中,选择“NuGet 程序包管理器”>“包管理器控制台”

  2. 在控制台中,运行以下 Install-Package 命令以安装 OpenAPI 扩展:

    NuGet\Install-Package Microsoft.Azure.Functions.Worker.Extensions.OpenApi -Version 1.5.1
    

    可能需要根据 .NET 版本更新特定版本

现在,可以添加 HTTP 终结点函数。

添加 HTTP 终结点函数

在 C# 类库中,函数使用的绑定是通过在代码中应用属性来定义的。 要使用 HTTP 触发器创建函数,请:

  1. 在“解决方案资源管理器”中,右键单击你的项目节点,然后选择“添加”>“新建 Azure 函数”。

  2. 为该类输入 Turbine.cs,然后选择“添加”。

  3. 选择“Http 触发器”模板,将“授权级别”设置为“函数”,然后选择“添加”。 Turbine.cs 代码文件将会添加到项目中,它会使用 HTTP 触发器定义新的函数终结点。

现在,可以将 HTTP 触发器模板代码替换为实现 Turbine 函数终结点的代码,以及使用 OpenAPI 定义终结点的属性。

更新函数代码

该函数使用的 HTTP 触发器采用以下两个参数:

参数名称 说明
小时数 预计维修涡轮机所需的时间。
容量 涡轮机的容量,以千瓦为单位。

然后,该函数将计算维修成本,以及涡轮机在 24 小时内可产生的收入。 参数在查询字符串或者在 POST 请求的有效负载中提供。

在 Turbine.cs 项目文件中,将根据 HTTP 触发器模板生成的类的内容替换为以下代码,具体取决于进程模型:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using System.Net;

namespace TurbineRepair
{
    public class Turbine
    {
        const double revenuePerkW = 0.12;
        const double technicianCost = 250;
        const double turbineCost = 100;

        private readonly ILogger<Turbine> _logger;

        public Turbine(ILogger<Turbine> logger)
        {
            _logger = logger;
        }

        [Function("TurbineRepair")]
        [OpenApiOperation(operationId: "Run")]
        [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
        [OpenApiRequestBody("application/json", typeof(RequestBodyModel),
            Description = "JSON request body containing { hours, capacity}")]
        [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(string),
            Description = "The OK response message containing a JSON result.")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            // Get request body data.
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic? data = JsonConvert.DeserializeObject(requestBody);
            int? capacity = data?.capacity;
            int? hours = data?.hours;

            // Return bad request if capacity or hours are not passed in
            if (capacity == null || hours == null)
            {
                return new BadRequestObjectResult("Please pass capacity and hours in the request body");
            }
            // Formulas to calculate revenue and cost
            double? revenueOpportunity = capacity * revenuePerkW * 24;
            double? costToFix = hours * technicianCost + turbineCost;
            string repairTurbine;

            if (revenueOpportunity > costToFix)
            {
                repairTurbine = "Yes";
            }
            else
            {
                repairTurbine = "No";
            };

            return new OkObjectResult(new
            {
                message = repairTurbine,
                revenueOpportunity = "$" + revenueOpportunity,
                costToFix = "$" + costToFix
            });
        }
        public class RequestBodyModel
        {
            public int Hours { get; set; }
            public int Capacity { get; set; }
        }
    }
}

此函数代码会返回 YesNo 消息,指示紧急修复是否具有成本效益。 它还返回涡轮机所代表的收入机会和修复涡轮机的成本。

在本地运行并验证 API

当你运行该函数时,OpenAPI 终结点会让使用生成的页面在本地试用该函数非常容易。 在本地运行时,无需提供函数访问密钥。

  1. 按 F5 启动项目。 当 Functions 运行时在本地启动时,输出中会显示一组 OpenAPI 和 Swagger 终结点,以及函数终结点。

  2. 在浏览器中打开 RenderSwaggerUI 终结点,类似于 http://localhost:7071/api/swagger/ui。 此时将根据 OpenAPI 定义呈现一个页面。

  3. 选择“POST”>“试用”,以查询参数的形式或者在 JSON 请求正文中输入 hourscapacity 的值,然后选择“执行” 。

    用于测试 TurbineRepair API 的 Swagger UI

  4. 如果输入整数值(例如为 hours 输入 6,为 capacity 输入 2500),将获得如以下示例所示的 JSON 响应:

    TurbineRepair 函数的响应 JSON 数据。

现在已经有了确定紧急修复是否经济高效的函数。 接下来,将项目和 API 定义发布到 Azure。

将项目发布到 Azure

必须在 Azure 订阅中有一个函数应用,然后才能发布项目。 Visual Studio 发布将在你首次发布项目时创建一个函数应用。 它还可以创建一个与函数应用集成的 API 管理实例,以公开 TurbineRepair API。

  1. 在解决方案资源管理器中,右键单击项目,选择“发布”,然后在“目标”中选择“Azure”,接着选择“下一步” 。

  2. 对于“特定目标”,请选择“Azure 函数应用(Windows)”以创建一个在 Windows 上运行的函数应用,然后选择“下一步” 。

  3. 在“函数实例”中,选择“+ 创建新的 Azure 函数…” 。

    创建新的函数应用实例

  4. 使用下表中指定的值创建新的实例:

    设置 描述
    名称 全局唯一名称 用于唯一标识新 Function App 的名称。 接受此名称或输入新名称。 有效的字符是 a-z0-9-
    订阅 你的订阅 要使用的 Azure 订阅。 接受此订阅,或从下拉列表中选择一个新订阅。
    资源组 资源组的名称 要在其中创建函数应用的资源组。 从下拉列表中选择现有资源组,或者选择“新建”来创建新的资源组。
    计划类型 消耗 将项目发布到在消耗计划中运行的函数应用时,只需为函数应用的执行付费。 其他托管计划会产生更高的成本。
    位置 服务的位置 在靠近你或者靠近函数访问的其他服务的区域中选择一个位置
    Azure 存储 常规用途存储帐户 Functions 运行时需要 Azure 存储帐户。 选择“新建”即可配置常规用途存储帐户。 也可选择一个符合存储帐户要求的现有帐户。

    在 Azure 中创建具有存储的新函数应用

  5. 选择“创建”,在 Azure 中创建一个函数应用及其相关资源。 资源创建的状态如窗口左下角所示。

  6. 回到“函数实例”,确保已选中“从包文件运行” 。 启用从包运行模式,函数应用将使用 Zip 部署进行部署。 建议对你的函数项目使用此部署方法,因为它可提高性能。

  7. 选择“下一步”,然后在“API 管理”页中选择“+ 创建 API 管理 API” 。

  8. 使用下表中的值在 API 管理中创建一个 API:

    设置 说明
    API 名称 TurbineRepair API 的名称。
    订阅名称 订阅 要使用的 Azure 订阅。 接受此订阅,或从下拉列表中选择一个新订阅。
    资源组 资源组的名称 从下拉列表中选择你的函数应用所在的资源组。
    API 管理服务 新实例 选择“新建”,以在无服务器层中的同一位置创建新的 API 管理实例。 选择“确定”以创建实例。

    使用 API 创建 API 管理实例

  9. 选择“创建”,以使用函数集成中的 TurbineRepair API 创建 API 管理实例。

  10. 选择“完成”,然后在发布配置文件创建过程完成后,选择“关闭”。

  11. 现在,“验证发布”页面上会显示“准备发布”,然后选择“发布”,以将包含项目文件的包部署到 Azure 中的新函数应用

    部署完成后,Azure 中函数应用的根 URL 将显示在“发布”选项卡中。

获取函数访问密钥

  1. 在“发布”选项卡中,选择“托管”旁边的省略号图标 (...),然后选择“在 Azure 门户中打开” 。 创建的函数应用将在默认浏览器中的 Azure 门户上打开。

  2. 在“概述”页上的“函数”下,选择>“Turbine”,然后选择“函数密钥”。

    获取 TurbineRepair 函数的访问密钥

  3. 在“函数密钥”下,选择“默认”键旁边的“复制到剪贴板”图标。 现在,可以在 API 管理中设置复制的键,以便它可以访问函数终结点。

配置 API 管理

  1. 在函数应用页中,展开“API”,然后选择“API 管理”。

  2. 如果函数应用尚未连接到新的 API 管理实例,请在“API 管理”下选择它,然后依次选择“API”>“Azure Functions 上的 OpenAPI 文档”,确保选中“导入函数”,然后选择“链接 API”。 确保仅选择“TurbineRepair”进行导入,然后选择“选择”。

  3. 选择页面顶部的“转到 API 管理”,然后在 API 管理实例中展开“API”

  4. 在“API”>“所有 API”下,选择“Azure Functions 上的 OpenAI 文档”>“POST 运行”,然后在“入站处理”下选择“添加策略”>“设置查询参数”

  5. 在“入站处理”下方的“设置查询参数”中,为“名称”键入 code,选择“+ 值”,粘贴复制的函数密钥,然后选择“保存”。 API 管理在将调用传递到函数终结点时会包含该函数密钥。

    向 API 入站处理规则提供函数凭据

现在,你已设置函数密钥,可以调用 turbine API 终结点来验证当它托管在 Azure 中时是否正常工作。

在 Azure 中验证 API

  1. 在 API 中,依次选择“测试”选项卡、“POST 运行”,在“请求正文”>“原始”中输入以下代码,然后选择“发送” :

    {
        "hours": "6",
        "capacity": "2500"
    }
    

    API 管理 API 中的 OpenAPI 测试页

    与前面一样,也可以提供相同的值作为查询参数。

  2. 选择“发送”,然后查看“HTTP 响应”以验证是否从 API 返回了相同的结果 。

下载 OpenAPI 定义

如果 API 按预期工作,则可以从 API 管理下载新托管 API 的 OpenAPI 定义。

    1. 在“API”下,依次选择“Azure Functions 上的 OpenAPI 文档”、省略号图标 (...) 和“导出”。

    下载 OpenAPI 定义

  1. 选择 API 导出方式,包括采用各种格式的 OpenAPI 文件。 也可以将 API 从 Azure API 管理导出到 Power Platform

清理资源

在前面的步骤中,你在资源组中创建了 Azure 资源。 如果将来不再需要这些资源,可以通过删除资源组来删除它们。

从 Azure 门户菜单或“主页”页上,选择“资源组” 。 然后,在“资源组”页上选择创建的组。

在“myResourceGroup”页中,确保列出的资源是要删除的资源。

选择“删除资源组”,在文本框中键入该组的名称以确认,然后选择“删除” 。

后续步骤

你已使用 Visual Studio 2022 创建了一个自编文档(由于使用了 OpenAPI 扩展)并且与 API 管理集成的函数。 现在,可以在门户上的“API 管理”中优化定义。 还可以详细了解 API 管理