無伺服器模型會從基礎計算基礎結構中抽象化程序代碼,讓開發人員不需進行廣泛的設定,即可專注於商業規則。 無伺服器程式代碼可降低成本,因為您只需支付程式代碼執行資源和持續時間的費用。
無伺服器事件驅動模型符合特定事件觸發已定義動作的情況。 例如,接收傳入裝置訊息會觸發記憶體以供日後使用,或資料庫更新會觸發一些進一步的處理。
為了協助您探索 Azure 中的 Azure 無伺服器技術,Microsoft 開發和測試使用 Azure Functions 的無伺服器應用程式。 本文會逐步解說無伺服器函式解決方案的程式代碼,並說明您可能會遇到的設計決策、實作詳細數據,以及一些您可能遇到的「gotchas」。
探索解決方案
兩部分解決方案描述假設的無人機遞送系統。 無人機會將飛行中狀態傳送至雲端,以儲存這些訊息供日後使用。 Web 應用程式可讓使用者擷取訊息,以取得裝置的最新狀態。
您可以從 GitHub 下載此解決方案的程式碼。
本逐步解說假設對下列技術有基本的熟悉度:
您不需要是 Functions 或事件中樞的專家,但您應該瞭解其在高層級的功能。 以下是開始使用的一些良好資源:
了解案例
Fabrikam 會管理無人機遞送服務的無人機車隊。 應用程式包含兩個主要功能區域:
事件擷取。 在飛行期間,無人機會將狀態消息傳送至雲端端點。 應用程式會擷取並處理這些訊息,並將結果寫入後端資料庫 (Azure Cosmos DB)。 裝置會以 通訊協定緩衝區 (protobuf) 格式傳送訊息。 Protobuf 是一種有效率、自我描述的串行化格式。
這些訊息包含部分更新。 在每個固定間隔內,每個無人機都會傳送包含所有狀態字段的「主要畫面格」訊息。 在主要畫面格之間,狀態消息只會包含自上次訊息後變更的欄位。 此行為通常是許多需要節省頻寬和電源的IoT裝置。
Web 應用程式。 Web 應用程式可讓使用者查閱裝置,並查詢裝置的最後已知狀態。 用戶必須登入應用程式,並使用 Microsoft Entra 識別碼進行驗證。 應用程式只允許已獲授權存取應用程式的使用者要求。
以下是 Web 應用程式的螢幕快照,其中顯示查詢的結果:
設計應用程式
Fabrikam 已決定使用 Azure Functions 來實作應用程式商業規則。 Azure Functions 是「函式即服務」(FaaS)的範例。 在此計算模型中,函式是部署至雲端並在裝載環境中執行的一段程序代碼。 此裝載環境會抽象化執行程式碼的伺服器。
為何選擇無伺服器方法?
具有 Functions 的無伺服器架構是事件驅動架構的範例。 函式程式代碼是由函式外部的某些事件所觸發,在此案例中為來自無人機的訊息,或用戶端應用程式的 HTTP 要求。 使用函式應用程式時,您不需要為觸發程式撰寫任何程序代碼。 您只會撰寫執行以回應觸發程式的程式代碼。 這表示您可以專注於商業規則,而不是撰寫許多程式代碼來處理基礎結構問題,例如傳訊。
使用無伺服器架構也有一些操作優點:
- 不需要管理伺服器。
- 計算資源會視需要動態配置。
- 您只需支付用來執行程式碼的計算資源費用。
- 計算資源會根據流量依需求進行調整。
架構
下圖顯示應用程式的高層級架構:
在一個數據流中,箭號會顯示從裝置到事件中樞並觸發函式應用程式的訊息。 從應用程式,一個箭號會顯示傳送至記憶體佇列的寄不出的信件訊息,另一個箭號則會顯示寫入 Azure Cosmos DB。 在另一個數據流中,箭號會顯示用戶端 Web 應用程式透過 CDN 從 Blob 記憶體靜態 Web 裝載取得靜態檔案。 另一個箭號顯示透過 API 管理 的用戶端 HTTP 要求。 從 API 管理,一個箭號會顯示從 Azure Cosmos DB 觸發和讀取數據的函式應用程式。 另一個箭號會顯示透過 Microsoft Entra ID 進行驗證。 使用者也會登入 Microsoft Entra ID。
事件擷取:
- 無人機訊息會由 Azure 事件中樞 內嵌。
- 事件中樞會產生包含訊息數據的事件數據流。
- 這些事件會觸發 Azure Functions 應用程式來處理它們。
- 結果會儲存在 Azure Cosmos DB 中。
Web 應用程式:
- 靜態檔案是由來自 Blob 記憶體的 CDN 提供。
- 使用者使用 Microsoft Entra ID 登入 Web 應用程式。
- Azure API 管理 可作為公開 REST API 端點的閘道。
- 來自客戶端的 HTTP 要求會觸發從 Azure Cosmos DB 讀取並傳回結果的 Azure Functions 應用程式。
此應用程式是以下列參考架構為基礎。
您可以閱讀上述文章,以深入瞭解高階架構、解決方案中使用的 Azure 服務,以及延展性、安全性和可靠性的考慮。
無人機遙測功能
讓我們從查看處理來自事件中樞無人機訊息的函式開始。 函式定義於名為 RawTelemetryFunction
的類別中:
namespace DroneTelemetryFunctionApp
{
public class RawTelemetryFunction
{
private readonly ITelemetryProcessor telemetryProcessor;
private readonly IStateChangeProcessor stateChangeProcessor;
private readonly TelemetryClient telemetryClient;
public RawTelemetryFunction(ITelemetryProcessor telemetryProcessor, IStateChangeProcessor stateChangeProcessor, TelemetryClient telemetryClient)
{
this.telemetryProcessor = telemetryProcessor;
this.stateChangeProcessor = stateChangeProcessor;
this.telemetryClient = telemetryClient;
}
}
...
}
這個類別有數個相依性,這些相依性會使用相依性插入插入建構函式:
ITelemetryProcessor
和IStateChangeProcessor
介面會定義兩個協助程序物件。 如我們所見,這些物件會執行大部分的工作。TelemetryClient 是 Application Insights SDK (傳統 API) 的一部分。 它用來將自定義應用程式計量傳送至 Application Insights。
稍後,我們將探討如何設定相依性插入。 現在,只要假設這些相依性存在。
設定事件中樞觸發程式
函式中的邏輯會實作為名為的 RunAsync
異步方法。 這是方法簽名:
[FunctionName("RawTelemetryFunction")]
[StorageAccount("DeadLetterStorage")]
public async Task RunAsync(
[EventHubTrigger("%EventHubName%", Connection = "EventHubConnection", ConsumerGroup ="%EventHubConsumerGroup%")]EventData[] messages,
[Queue("deadletterqueue")] IAsyncCollector<DeadLetterMessage> deadLetterMessages,
ILogger logger)
{
// implementation goes here
}
方法會採用下列參數:
-
messages
是事件中樞訊息的陣列。 -
deadLetterMessages
是 Azure 儲存體 佇列,用於儲存寄不出的信件訊息。 -
logging
提供記錄介面,用於寫入應用程式記錄。 這些記錄會傳送至 Azure 監視器。
參數 EventHubTrigger
上的 messages
屬性會設定觸發程式。 屬性的屬性會指定事件中樞名稱、連接字串 和取用者群組。 (取 用者群組 是事件中樞事件數據流的隔離檢視。這個抽象概念允許相同事件中樞的多個取用者。
請注意某些屬性屬性中的百分比符號 \ 。 這些表示 屬性會指定應用程式設定的名稱,而實際值則是在運行時間從該應用程式設定中取得。 否則,如果沒有百分比符號,屬性就會提供常值。
屬性 Connection
是例外狀況。 這個屬性一律會指定應用程式設定名稱,絕不是常值,因此不需要百分比符號。 此區別的原因是 連接字串 是秘密,而且不應該簽入原始程式碼。
雖然其他兩個屬性(事件中樞名稱和取用者群組)不是敏感數據,例如 連接字串,但最好將它們放入應用程式設定中,而不是硬式編碼。 如此一來,即可更新它們,而不需要重新編譯應用程式。
如需設定此觸發程式的詳細資訊,請參閱 azure Functions 的 Azure 事件中樞 系結。
訊息處理邏輯
以下是處理一批訊息的方法實作 RawTelemetryFunction.RunAsync
:
[FunctionName("RawTelemetryFunction")]
[StorageAccount("DeadLetterStorage")]
public async Task RunAsync(
[EventHubTrigger("%EventHubName%", Connection = "EventHubConnection", ConsumerGroup ="%EventHubConsumerGroup%")]EventData[] messages,
[Queue("deadletterqueue")] IAsyncCollector<DeadLetterMessage> deadLetterMessages,
ILogger logger)
{
telemetryClient.GetMetric("EventHubMessageBatchSize").TrackValue(messages.Length);
foreach (var message in messages)
{
DeviceState deviceState = null;
try
{
deviceState = telemetryProcessor.Deserialize(message.Body.Array, logger);
try
{
await stateChangeProcessor.UpdateState(deviceState, logger);
}
catch (Exception ex)
{
logger.LogError(ex, "Error updating status document", deviceState);
await deadLetterMessages.AddAsync(new DeadLetterMessage { Exception = ex, EventData = message, DeviceState = deviceState });
}
}
catch (Exception ex)
{
logger.LogError(ex, "Error deserializing message", message.SystemProperties.PartitionKey, message.SystemProperties.SequenceNumber);
await deadLetterMessages.AddAsync(new DeadLetterMessage { Exception = ex, EventData = message });
}
}
}
叫用函式時, messages
參數會包含來自事件中樞的訊息數位。 在批次中處理訊息通常會比一次讀取一則訊息產生更好的效能。 不過,您必須確定函式具有復原性,並正常處理失敗和例外狀況。 否則,如果函式在批次中間擲回未處理的例外狀況,您可能會遺失其餘的訊息。 在錯誤處理一節中會更詳細地討論此考慮。
但是,如果您忽略例外狀況處理,則每個訊息的處理邏輯都很簡單:
- 呼叫
ITelemetryProcessor.Deserialize
以還原串行化包含裝置狀態變更的訊息。 - 呼叫
IStateChangeProcessor.UpdateState
以處理狀態變更。
讓我們更詳細地看看這兩種方法,從 Deserialize
方法開始。
還原串行化方法
方法 TelemetryProcess.Deserialize
會採用包含訊息承載的位元組數位。 它會還原串行化這個承載,並傳 DeviceState
回 物件,此物件代表無人機的狀態。 狀態可能代表部分更新,只包含來自上次已知狀態的差異。 因此,方法必須處理 null
還原串行化承載中的欄位。
public class TelemetryProcessor : ITelemetryProcessor
{
private readonly ITelemetrySerializer<DroneState> serializer;
public TelemetryProcessor(ITelemetrySerializer<DroneState> serializer)
{
this.serializer = serializer;
}
public DeviceState Deserialize(byte[] payload, ILogger log)
{
DroneState restored = serializer.Deserialize(payload);
log.LogInformation("Deserialize message for device ID {DeviceId}", restored.DeviceId);
var deviceState = new DeviceState();
deviceState.DeviceId = restored.DeviceId;
if (restored.Battery != null)
{
deviceState.Battery = restored.Battery;
}
if (restored.FlightMode != null)
{
deviceState.FlightMode = (int)restored.FlightMode;
}
if (restored.Position != null)
{
deviceState.Latitude = restored.Position.Value.Latitude;
deviceState.Longitude = restored.Position.Value.Longitude;
deviceState.Altitude = restored.Position.Value.Altitude;
}
if (restored.Health != null)
{
deviceState.AccelerometerOK = restored.Health.Value.AccelerometerOK;
deviceState.GyrometerOK = restored.Health.Value.GyrometerOK;
deviceState.MagnetometerOK = restored.Health.Value.MagnetometerOK;
}
return deviceState;
}
}
這個方法會使用另一個協助程式介面 ITelemetrySerializer<T>
,將原始訊息還原串行化。 然後,結果會轉換成 更容易使用的POCO 模型。 此設計有助於隔離處理邏輯與串行化實作詳細數據。 介面 ITelemetrySerializer<T>
定義於共用連結庫中,裝置模擬器也會使用此連結庫來產生模擬裝置事件,並將其傳送至事件中樞。
using System;
namespace Serverless.Serialization
{
public interface ITelemetrySerializer<T>
{
T Deserialize(byte[] message);
ArraySegment<byte> Serialize(T message);
}
}
UpdateState 方法
方法 StateChangeProcessor.UpdateState
會套用狀態變更。 每個無人機的最後已知狀態會儲存為 Azure Cosmos DB 中的 JSON 檔。 由於無人機會傳送部分更新,因此當應用程式取得更新時,無法直接覆寫檔。 相反地,它必須擷取先前的狀態、合併欄位,然後執行 upsert 作業。
public class StateChangeProcessor : IStateChangeProcessor
{
private IDocumentClient client;
private readonly string cosmosDBDatabase;
private readonly string cosmosDBCollection;
public StateChangeProcessor(IDocumentClient client, IOptions<StateChangeProcessorOptions> options)
{
this.client = client;
this.cosmosDBDatabase = options.Value.COSMOSDB_DATABASE_NAME;
this.cosmosDBCollection = options.Value.COSMOSDB_DATABASE_COL;
}
public async Task<ResourceResponse<Document>> UpdateState(DeviceState source, ILogger log)
{
log.LogInformation("Processing change message for device ID {DeviceId}", source.DeviceId);
DeviceState target = null;
try
{
var response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(cosmosDBDatabase, cosmosDBCollection, source.DeviceId),
new RequestOptions { PartitionKey = new PartitionKey(source.DeviceId) });
target = (DeviceState)(dynamic)response.Resource;
// Merge properties
target.Battery = source.Battery ?? target.Battery;
target.FlightMode = source.FlightMode ?? target.FlightMode;
target.Latitude = source.Latitude ?? target.Latitude;
target.Longitude = source.Longitude ?? target.Longitude;
target.Altitude = source.Altitude ?? target.Altitude;
target.AccelerometerOK = source.AccelerometerOK ?? target.AccelerometerOK;
target.GyrometerOK = source.GyrometerOK ?? target.GyrometerOK;
target.MagnetometerOK = source.MagnetometerOK ?? target.MagnetometerOK;
}
catch (DocumentClientException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
target = source;
}
}
var collectionLink = UriFactory.CreateDocumentCollectionUri(cosmosDBDatabase, cosmosDBCollection);
return await client.UpsertDocumentAsync(collectionLink, target);
}
}
此程式代碼會 IDocumentClient
使用 介面從 Azure Cosmos DB 擷取檔。 如果檔存在,新的狀態值會合併至現有的檔。 否則會建立新的文件。 這兩個案例都是由 UpsertDocumentAsync
方法處理。
此程式代碼已針對檔已經存在且可合併的情況進行優化。 在指定無人機的第一個遙測訊息上 ReadDocumentAsync
,方法會擲回例外狀況,因為該無人機沒有檔。 第一則訊息之後,檔將可供使用。
請注意,這個類別會使用相依性插入來插入 IDocumentClient
Azure Cosmos DB 的 ,以及 IOptions<T>
具有組態設定的 。 我們稍後將瞭解如何設定相依性插入。
注意
Azure Functions 支援 Azure Cosmos DB 的輸出系結。 此系結可讓函式應用程式在 Azure Cosmos DB 中撰寫檔,而不需要任何程式代碼。 不過,輸出系結不適用於此特定案例,因為需要自定義 upsert 邏輯。
錯誤處理
如先前所述,函 RawTelemetryFunction
式應用程式會在迴圈中處理一批訊息。 這表示函式必須正常處理任何例外狀況,並繼續處理批次的其餘部分。 否則,可能會卸除訊息。
如果在處理訊息時遇到例外狀況,函式會將訊息放入寄不出的信件佇列:
catch (Exception ex)
{
logger.LogError(ex, "Error deserializing message", message.SystemProperties.PartitionKey, message.SystemProperties.SequenceNumber);
await deadLetterMessages.AddAsync(new DeadLetterMessage { Exception = ex, EventData = message });
}
寄不出的信件佇列是使用 記憶體佇列的輸出系結 來定義:
[FunctionName("RawTelemetryFunction")]
[StorageAccount("DeadLetterStorage")] // App setting that holds the connection string
public async Task RunAsync(
[EventHubTrigger("%EventHubName%", Connection = "EventHubConnection", ConsumerGroup ="%EventHubConsumerGroup%")]EventData[] messages,
[Queue("deadletterqueue")] IAsyncCollector<DeadLetterMessage> deadLetterMessages, // output binding
ILogger logger)
在這裡,Queue
屬性會指定輸出系結,而 StorageAccount
屬性會指定保留記憶體帳戶 連接字串 的應用程式設定名稱。
設定相依性插入
下列程式代碼會設定函式的 RawTelemetryFunction
相依性插入:
[assembly: FunctionsStartup(typeof(DroneTelemetryFunctionApp.Startup))]
namespace DroneTelemetryFunctionApp
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddOptions<StateChangeProcessorOptions>()
.Configure<IConfiguration>((configSection, configuration) =>
{
configuration.Bind(configSection);
});
builder.Services.AddTransient<ITelemetrySerializer<DroneState>, TelemetrySerializer<DroneState>>();
builder.Services.AddTransient<ITelemetryProcessor, TelemetryProcessor>();
builder.Services.AddTransient<IStateChangeProcessor, StateChangeProcessor>();
builder.Services.AddSingleton<CosmosClient>(ctx => {
var config = ctx.GetService<IConfiguration>();
var cosmosDBEndpoint = config.GetValue<string>("CosmosDBEndpoint");
return new CosmosClient(
accountEndpoint: cosmosDBEndpoint,
new DefaultAzureCredential());
});
}
}
}
針對 .NET 撰寫的 Azure Functions 可以使用 ASP.NET Core 相依性插入架構。 基本概念是宣告元件的啟動方法。 方法會採用 IFunctionsHostBuilder
介面,用來宣告 DI 的相依性。 您可以在物件上Add*
呼叫 Services
方法來執行此動作。 當您新增相依性時,您會指定其存留期:
- 每次要求暫時性 物件時都會建立。
- 每個函式執行會建立範圍物件一次。
- 單一物件會在函式主機的存留期內跨函式執行重複使用。
在此範例中 TelemetryProcessor
,和 StateChangeProcessor
物件會宣告為暫時性。 這適用於輕量型無狀態服務。 另一方面,類別 DocumentClient
應該是單一類別,以獲得最佳效能。 如需詳細資訊,請參閱 Azure Cosmos DB 和 .NET 的效能秘訣。
如果您回到 RawTelemetryFunction 的程式代碼,您會看到另一個相依性不會出現在 DI 設定程式碼中,也就是TelemetryClient
用來記錄應用程式計量的類別。 Functions 運行時間會自動將此類別註冊到 DI 容器,因此您不需要明確地註冊它。
如需 Azure Functions 中 DI 的詳細資訊,請參閱下列文章:
在 DI 中傳遞組態設定
有時候必須使用某些組態值初始化 物件。 一般而言,這些設定應該來自應用程式設定,或 Azure 金鑰保存庫 的秘密。
此應用程式中有兩個範例。 首先,類別 DocumentClient
會採用 Azure Cosmos DB 服務端點和密鑰。 針對這個物件,應用程式會註冊將由 DI 容器叫用的 Lambda。 此 Lambda 會 IConfiguration
使用 介面來讀取組態值:
builder.Services.AddSingleton<CosmosClient>(ctx => {
var config = ctx.GetService<IConfiguration>();
var cosmosDBEndpoint = config.GetValue<string>("CosmosDBEndpoint");
return new CosmosClient(
accountEndpoint: cosmosDBEndpoint,
new DefaultAzureCredential());
});
第二個範例是 類別 StateChangeProcessor
。 針對這個對象,我們使用稱為 選項模式的方法。 以下說明其運作方式:
定義包含組態設定的類別
T
。 在此情況下,Azure Cosmos DB 資料庫名稱和集合名稱。public class StateChangeProcessorOptions { public string COSMOSDB_DATABASE_NAME { get; set; } public string COSMOSDB_DATABASE_COL { get; set; } }
將類別
T
新增為 DI 的選項類別。builder.Services.AddOptions<StateChangeProcessorOptions>() .Configure<IConfiguration>((configSection, configuration) => { configuration.Bind(configSection); });
在所設定類別的建構函式中,包含
IOptions<T>
參數。public StateChangeProcessor(IDocumentClient client, IOptions<StateChangeProcessorOptions> options)
DI 系統會使用組態值自動填入 options 類別,並將此值傳遞至建構函式。
此方法有數個優點:
- 將類別與組態值的來源分離。
- 輕鬆設定不同的組態來源,例如環境變數或 JSON 組態檔。
- 簡化單元測試。
- 使用強型別選項類別,其容易出錯,而不只是傳入純量值。
GetStatus 函式
此解決方案中的其他 Functions 應用程式會實作簡單的 REST API,以取得無人機的最後已知狀態。 此函式定義於名為 GetStatusFunction
的類別中。 以下是函式的完整程式代碼:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
using System.Threading.Tasks;
namespace DroneStatusFunctionApp
{
public static class GetStatusFunction
{
public const string GetDeviceStatusRoleName = "GetStatus";
[FunctionName("GetStatusFunction")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequest req,
[CosmosDB(
databaseName: "%COSMOSDB_DATABASE_NAME%",
collectionName: "%COSMOSDB_DATABASE_COL%",
ConnectionStringSetting = "COSMOSDB_CONNECTION_STRING",
Id = "{Query.deviceId}",
PartitionKey = "{Query.deviceId}")] dynamic deviceStatus,
ClaimsPrincipal principal,
ILogger log)
{
log.LogInformation("Processing GetStatus request.");
if (!principal.IsAuthorizedByRoles(new[] { GetDeviceStatusRoleName }, log))
{
return new UnauthorizedResult();
}
string deviceId = req.Query["deviceId"];
if (deviceId == null)
{
return new BadRequestObjectResult("Missing DeviceId");
}
if (deviceStatus == null)
{
return new NotFoundResult();
}
else
{
return new OkObjectResult(deviceStatus);
}
}
}
}
此函式會使用 HTTP 觸發程式來處理 HTTP GET 要求。 函式會使用 Azure Cosmos DB 輸入系結來擷取要求的檔。 其中一個考慮是,此系結會在函式內執行授權邏輯之前執行。 如果未經授權的使用者要求檔,函式系結仍會擷取檔。 然後授權碼會傳回 401,因此使用者不會看到檔。 此行為是否可接受,可能取決於您的需求。 例如,這種方法可能會讓稽核敏感數據的數據存取更加困難。
驗證與授權
Web 應用程式會使用 Microsoft Entra 識別碼來驗證使用者。 因為應用程式是在瀏覽器中執行的單頁應用程式 (SPA),因此 授權碼流程 是適當的:
- Web 應用程式會將使用者重新導向至識別提供者(在此案例中,Microsoft Entra ID)。
- 使用者輸入其認證。
- 身分識別提供者會以授權碼重新導向回 Web 應用程式,稍後可以交換存取令牌。
- Web 應用程式會將要求傳送至 Web API,並在授權標頭中包含資源的存取令牌。
函式應用程式可以設定為使用零個程式代碼來驗證使用者。 如需詳細資訊,請參閱 Azure App Service 中的驗證與授權。
另一方面,授權通常需要一些商業規則。 Microsoft Entra ID 支援 宣告型驗證。 在此模型中,使用者的身分識別會以一組來自識別提供者的宣告表示。 宣告可以是使用者的任何資訊片段,例如其名稱或電子郵件位址。
存取令牌包含使用者宣告的子集。 其中包括使用者指派給的任何應用程式角色。
函 principal
式的參數是 ClaimsPrincipal 物件,其中包含來自存取令牌的宣告。 每個宣告都是宣告類型和宣告值的索引鍵/值組。 應用程式會使用這些來授權要求。
下列擴充方法會測試物件是否 ClaimsPrincipal
包含一組角色。 如果遺漏任何指定的角色,則會傳 false
回 。 如果此方法傳回 false,則函式會傳回 HTTP 401 (未經授權)。
namespace DroneStatusFunctionApp
{
public static class ClaimsPrincipalAuthorizationExtensions
{
public static bool IsAuthorizedByRoles(
this ClaimsPrincipal principal,
string[] roles,
ILogger log)
{
var principalRoles = new HashSet<string>(principal.Claims.Where(kvp => kvp.Type == "roles").Select(kvp => kvp.Value));
var missingRoles = roles.Where(r => !principalRoles.Contains(r)).ToArray();
if (missingRoles.Length > 0)
{
log.LogWarning("The principal does not have the required {roles}", string.Join(", ", missingRoles));
return false;
}
return true;
}
}
}
如需此應用程式中驗證和授權的詳細資訊,請參閱 參考架構的安全性考慮 一節。
下一步
一旦您瞭解此參考解決方案的運作方式,請瞭解類似解決方案的最佳做法和建議。 如需無伺服器 Web 應用程式,請參閱 Azure 上的無伺服器 Web 應用程式。
Azure Functions 只是一個 Azure 計算選項。 如需選擇計算技術的協助,請參閱 為您的應用程式選擇 Azure 計算服務。
相關資源
- 如需在內部部署和雲端開發無伺服器解決方案的深入討論,請參閱 無伺服器應用程式:架構、模式和 Azure 實作。
- 深入瞭解 事件驅動架構樣式。