Özel kaynak sağlayıcıları için RESTful uç noktası yazma

Özel kaynak sağlayıcısı, Azure ile uç nokta arasındaki sözleşmedir. Özel kaynak sağlayıcılarıyla Azure'da iş akışlarını özelleştirebilirsiniz. Bu öğreticide özel bir kaynak sağlayıcısı RESTful uç noktasının nasıl yazacakları gösterilmektedir. Azure Özel Kaynak Sağlayıcıları hakkında bilginiz yoksa , özel kaynak sağlayıcılarına genel bakışa bakın.

Not

Bu öğretici, özel kaynak sağlayıcıları için Azure İşlevleri ayarlama öğreticisini oluşturur. Bu öğreticideki adımlardan bazıları yalnızca özel kaynak sağlayıcılarıyla çalışmak üzere Azure İşlevleri'de bir işlev uygulaması ayarlanmışsa çalışır.

Özel eylemler ve özel kaynaklarla çalışma

Bu öğreticide, işlev uygulamasını özel kaynak sağlayıcınız için RESTful uç noktası olarak çalışacak şekilde güncelleştirirsiniz. Azure'daki kaynaklar ve eylemler aşağıdaki temel RESTful belirtimlerinden sonra modellenir:

  • PUT: Yeni kaynak oluşturma
  • GET (örnek): Mevcut bir kaynağı alma
  • DELETE: Var olan bir kaynağı kaldırma
  • POST: Eylem tetikleme
  • GET (koleksiyon): Tüm mevcut kaynakları listeleme

Bu öğretici için Azure Tablo depolamayı kullanırsınız, ancak herhangi bir veritabanı veya depolama hizmeti çalışır.

Depolamada özel kaynakları bölümleme

RESTful hizmeti oluşturduğunuz için, oluşturulan kaynakları depolamanız gerekir. Azure Tablo depolama için verileriniz için bölüm ve satır anahtarları oluşturmanız gerekir. Özel kaynak sağlayıcıları için veriler özel kaynak sağlayıcısına bölümlenmelidir. Özel kaynak sağlayıcısına bir gelen istek gönderildiğinde, özel kaynak sağlayıcısı üst bilgiyi uç noktaya giden isteklere ekler x-ms-customproviders-requestpath .

Aşağıdaki örnekte özel bir x-ms-customproviders-requestpath kaynak için üst bilgi gösterilmektedir:

X-MS-CustomProviders-RequestPath: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CustomProviders/resourceProviders/{resourceProviderName}/{myResourceType}/{myResourceName}

Üst bilgi temelinde x-ms-customproviders-requestpath , aşağıdaki tabloda gösterildiği gibi depolama alanınız için partitionKey ve rowKey parametrelerini oluşturabilirsiniz:

Parametre Şablon Açıklama
partitionKey {subscriptionId}:{resourceGroupName}:{resourceProviderName} partitionKey parametresi verilerin nasıl bölümlendiğini belirtir. Veriler genellikle özel kaynak sağlayıcısı örneği tarafından bölümlenmiştir.
rowKey {myResourceType}:{myResourceName} rowKey parametresi, verilerin tek tek tanımlayıcısını belirtir. Tanımlayıcı genellikle kaynağın adıdır.

Ayrıca özel kaynağınızı modellemek için yeni bir sınıf oluşturmanız gerekir. Bu öğreticide, işlev uygulamanıza aşağıdaki CustomResource sınıfını eklersiniz:

// Custom Resource Table Entity
public class CustomResource : ITableEntity
{
    public string Data { get; set; }

    public string PartitionKey { get; set; }

    public string RowKey { get; set; }

    public DateTimeOffset? Timestamp { get; set; }

    public ETag ETag { get; set; }
}

CustomResource , giriş verilerini kabul eden basit ve genel bir sınıftır. Verileri depolamak için kullanılan ITableEntity'yi temel alır. CustomResource sınıfı ITableEntity arabiriminden tüm özellikleri uygular: zaman damgası, eTag, partitionKey ve rowKey.

Özel kaynak sağlayıcısı RESTful yöntemlerini destekleme

Not

Kodu doğrudan bu öğreticiden kopyalamıyorsanız, yanıt içeriğinin üst bilgiyi application/jsonolarak ayarlayan Content-Type geçerli bir JSON olması gerekir.

Artık veri bölümleme ayarladığınıza göre, özel kaynaklar ve özel eylemler için temel CRUD ve tetikleyici yöntemlerini oluşturun. Özel kaynak sağlayıcıları ara sunucu olarak çalıştığından, RESTful uç noktasının isteği ve yanıtı modellemesi ve işlemesi gerekir. Aşağıdaki kod parçacıklarında temel RESTful işlemlerinin nasıl işlenmeleri gösterilir.

Özel eylem tetikleme

Özel kaynak sağlayıcıları için POST istekleri aracılığıyla özel bir eylem tetikler. Özel bir eylem isteğe bağlı olarak bir dizi giriş parametresi içeren bir istek gövdesini kabul edebilir. Ardından eylem, eylemin sonucunu ve başarılı veya başarısız olup olmadığını belirten bir yanıt döndürür.

İşlev uygulamanıza aşağıdaki TriggerCustomAction yöntemini ekleyin:

/// <summary>
/// Triggers a custom action with some side effects.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <returns>The HTTP response result of the custom action.</returns>
public static async Task<HttpResponseMessage> TriggerCustomAction(HttpRequestMessage requestMessage)
{
    var myCustomActionRequest = await requestMessage.Content.ReadAsStringAsync();

    var actionResponse = requestMessage.CreateResponse(HttpStatusCode.OK);
    actionResponse.Content = myCustomActionRequest != string.Empty ? 
        new StringContent(JObject.Parse(myCustomActionRequest).ToString(), System.Text.Encoding.UTF8, "application/json") :
        null;
    return actionResponse;
}

TriggerCustomAction yöntemi gelen bir isteği kabul eder ve yanıtı bir durum koduyla geri döndürür.

Özel kaynak oluşturma

Özel kaynak sağlayıcıları için PUT istekleri aracılığıyla özel bir kaynak oluşturulur. Özel kaynak sağlayıcısı, özel kaynak için bir dizi özellik içeren bir JSON istek gövdesini kabul eder. Azure'daki kaynaklar restful modelini izler. Kaynak oluşturmak, almak veya silmek için aynı istek URL'sini kullanabilirsiniz.

Yeni kaynaklar oluşturmak için aşağıdaki CreateCustomResource yöntemini ekleyin:

/// <summary>
/// Creates a custom resource and saves it to table storage.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableClient">The client that allows you to interact with Azure Tables hosted in either Azure storage accounts or Azure Cosmos DB table API.</param>
/// <param name="azureResourceId">The parsed Azure resource ID.</param>
/// <param name="partitionKey">The partition key for storage. This is the custom resource provider ID.</param>
/// <param name="rowKey">The row key for storage. This is '{resourceType}:{customResourceName}'.</param>
/// <returns>The HTTP response containing the created custom resource.</returns>
public static async Task<HttpResponseMessage> CreateCustomResource(HttpRequestMessage requestMessage, TableClient tableClient, ResourceId azureResourceId, string partitionKey, string rowKey)
{
    // Adds the Azure top-level properties.
    var myCustomResource = JObject.Parse(await requestMessage.Content.ReadAsStringAsync());
    myCustomResource["name"] = azureResourceId.Name;
    myCustomResource["type"] = azureResourceId.FullResourceType;
    myCustomResource["id"] = azureResourceId.Id;

    // Save the resource into storage.
    var customEntity =  new CustomResource
    {
        PartitionKey = partitionKey,
        RowKey = rowKey,
        Data = myCustomResource.ToString(),
    });
    await tableClient.AddEntity(customEntity);

    var createResponse = requestMessage.CreateResponse(HttpStatusCode.OK);
    createResponse.Content = new StringContent(myCustomResource.ToString(), System.Text.Encoding.UTF8, "application/json");
    return createResponse;
}

CreateCustomResource yöntemi gelen isteği Azure'a özgü alan kimliğini, adını ve türünü içerecek şekilde güncelleştirir. Bu alanlar, Azure genelindeki hizmetler tarafından kullanılan üst düzey özelliklerdir. Özel kaynak sağlayıcısının Azure İlkesi, Azure Resource Manager şablonları ve Azure Etkinlik Günlüğü gibi diğer hizmetlerle birlikte çalışmalarına olanak sağlar.

Özellik Örnek Açıklama
Adı {myCustomResourceName} Özel kaynağın adı
Türü Microsoft.CustomProviders/resourceProviders/{resourceTypeName} Kaynak türü ad alanı
id /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/
providers/Microsoft.CustomProviders/resourceProviders/{resourceProviderName}/
{resourceTypeName}/{myCustomResourceName}
Kaynak kimliği

Özellikleri eklemenin yanı sıra JSON belgesini De Azure Tablo depolama alanına kaydettiniz.

Özel kaynak alma

Özel kaynak sağlayıcıları için, GET istekleri aracılığıyla özel bir kaynak alınır. Özel kaynak sağlayıcısı JSON isteği gövdesini kabul etmez . GET isteklerinde uç nokta, zaten oluşturulmuş kaynağı döndürmek için üst bilgisini kullanır x-ms-customproviders-requestpath .

Mevcut kaynakları almak için aşağıdaki RetrieveCustomResource yöntemini ekleyin:

/// <summary>
/// Retrieves a custom resource.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableClient">The client that allows you to interact with Azure Tables hosted in either Azure storage accounts or Azure Cosmos DB table API.</param>
/// <param name="partitionKey">The partition key for storage. This is the custom resource provider ID.</param>
/// <param name="rowKey">The row key for storage. This is '{resourceType}:{customResourceName}'.</param>
/// <returns>The HTTP response containing the existing custom resource.</returns>
public static async Task<HttpResponseMessage> RetrieveCustomResource(HttpRequestMessage requestMessage, TableClient tableClient, string partitionKey, string rowKey)
{
    // Attempt to retrieve the Existing Stored Value
    var queryResult = tableClient.GetEntityAsync<CustomResource>(partitionKey, rowKey);
    var existingCustomResource = (CustomResource)queryResult.Result;

    var retrieveResponse = requestMessage.CreateResponse(
        existingCustomResource != null ? HttpStatusCode.OK : HttpStatusCode.NotFound);

    retrieveResponse.Content = existingCustomResource != null ?
            new StringContent(existingCustomResource.Data, System.Text.Encoding.UTF8, "application/json"):
            null;
    return retrieveResponse;
}

Azure'da kaynaklar bir RESTful modelini izler. Kaynak oluşturan istek URL'si, get isteği gerçekleştirilirse kaynağı da döndürür.

Özel kaynağı kaldırma

Özel kaynak sağlayıcıları için, DELETE istekleri aracılığıyla özel bir kaynak kaldırılır. Özel kaynak sağlayıcısı JSON isteği gövdesini kabul etmez . DELETE isteğinde uç nokta, zaten oluşturulmuş olan kaynağı silmek için üst bilgisini kullanır x-ms-customproviders-requestpath .

Mevcut kaynakları kaldırmak için aşağıdaki RemoveCustomResource yöntemini ekleyin:

/// <summary>
/// Removes an existing custom resource.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableClient">The client that allows you to interact with Azure Tables hosted in either Azure storage accounts or Azure Cosmos DB table API.</param>
/// <param name="partitionKey">The partition key for storage. This is the custom resource provider ID.</param>
/// <param name="rowKey">The row key for storage. This is '{resourceType}:{customResourceName}'.</param>
/// <returns>The HTTP response containing the result of the deletion.</returns>
public static async Task<HttpResponseMessage> RemoveCustomResource(HttpRequestMessage requestMessage, TableClient tableClient, string partitionKey, string rowKey)
{
    // Attempt to retrieve the Existing Stored Value
    var queryResult = tableClient.GetEntityAsync<CustomResource>(partitionKey, rowKey);
    var existingCustomResource = (CustomResource)queryResult.Result;

    if (existingCustomResource != null) {
        await tableClient.DeleteEntity(deleteEntity.PartitionKey, deleteEntity.RowKey);
    }

    return requestMessage.CreateResponse(
        existingCustomResource != null ? HttpStatusCode.OK : HttpStatusCode.NoContent);
}

Azure'da kaynaklar bir RESTful modelini izler. Bir kaynak oluşturan istek URL'si, delete isteği gerçekleştirilirse kaynağı da siler.

Tüm özel kaynakları listeleme

Özel kaynak sağlayıcıları için, koleksiyon GET isteklerini kullanarak mevcut özel kaynakların listesini listeleyebilirsiniz. Özel kaynak sağlayıcısı JSON isteği gövdesini kabul etmez . GET istekleri koleksiyonu için uç nokta, zaten oluşturulmuş kaynakları listelemek için üst bilgisini kullanır x-ms-customproviders-requestpath .

Mevcut kaynakları listelemek için aşağıdaki EnumerateAllCustomResources yöntemini ekleyin:

/// <summary>
/// Enumerates all the stored custom resources for a given type.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableClient">The client that allows you to interact with Azure Tables hosted in either Azure storage accounts or Azure Cosmos DB table API.</param>
/// <param name="partitionKey">The partition key for storage. This is the custom resource provider ID.</param>
/// <param name="resourceType">The resource type of the enumeration.</param>
/// <returns>The HTTP response containing a list of resources stored under 'value'.</returns>
public static async Task<HttpResponseMessage> EnumerateAllCustomResources(HttpRequestMessage requestMessage, TableClient tableClient, string partitionKey, string resourceType)
{
    // Generate upper bound of the query.
    var rowKeyUpperBound = new StringBuilder(resourceType);
    rowKeyUpperBound[rowKeyUpperBound.Length - 1]++;

    // Create the enumeration query.
    var queryResultsFilter = tableClient.Query<CustomResource>(filter: $"PartitionKey eq '{partitionKey}' and RowKey lt '{rowKeyUpperBound.ToString()}' and RowKey ge '{resourceType}'")
    
    var customResources = await queryResultsFilter.ToList().Select(customResource => JToken.Parse(customResource.Data));

    var enumerationResponse = requestMessage.CreateResponse(HttpStatusCode.OK);
    enumerationResponse.Content = new StringContent(new JObject(new JProperty("value", customResources)).ToString(), System.Text.Encoding.UTF8, "application/json");
    return enumerationResponse;
}

Not

RowKey QueryComparisons.GreaterThan ve QueryComparisons.LessThan, dizeler için "startswith" sorgusu gerçekleştiren Azure Tablo depolama söz dizimidir.

Mevcut tüm kaynakları listelemek için, kaynakların özel kaynak sağlayıcısı bölümünüzün altında olmasını sağlayan bir Azure Tablo depolama sorgusu oluşturun. Sorgu daha sonra satır anahtarının aynı {myResourceType} değerle başlayıp başlamadığını denetler.

RESTful işlemlerini tümleştirme

İşlev uygulamasına tüm RESTful yöntemleri eklendikten sonra, farklı REST isteklerini işlemek için işlevleri çağıran ana Run yöntemini güncelleştirin:

/// <summary>
/// Entry point for the function app webhook that acts as the service behind a custom resource provider.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="log">The logger.</param>
/// <param name="tableClient">The client that allows you to interact with Azure Tables hosted in either Azure storage accounts or Azure Cosmos DB table API.</param>
/// <returns>The HTTP response for the custom Azure API.</returns>
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ILogger log, TableClient tableClient)
{
    // Get the unique Azure request path from request headers.
    var requestPath = req.Headers.GetValues("x-ms-customproviders-requestpath").FirstOrDefault();

    if (requestPath == null)
    {
        var missingHeaderResponse = req.CreateResponse(HttpStatusCode.BadRequest);
        missingHeaderResponse.Content = new StringContent(
            new JObject(new JProperty("error", "missing 'x-ms-customproviders-requestpath' header")).ToString(),
            System.Text.Encoding.UTF8, 
            "application/json");
    }

    log.LogInformation($"The Custom Resource Provider Function received a request '{req.Method}' for resource '{requestPath}'.");

    // Determines if it is a collection level call or action.
    var isResourceRequest = requestPath.Split('/').Length % 2 == 1;
    var azureResourceId = isResourceRequest ? 
        ResourceId.FromString(requestPath) :
        ResourceId.FromString($"{requestPath}/");

    // Create the Partition Key and Row Key
    var partitionKey = $"{azureResourceId.SubscriptionId}:{azureResourceId.ResourceGroupName}:{azureResourceId.Parent.Name}";
    var rowKey = $"{azureResourceId.FullResourceType.Replace('/', ':')}:{azureResourceId.Name}";

    switch (req.Method)
    {
        // Action request for a custom action.
        case HttpMethod m when m == HttpMethod.Post && !isResourceRequest:
            return await TriggerCustomAction(
                requestMessage: req);

        // Enumerate request for all custom resources.
        case HttpMethod m when m == HttpMethod.Get && !isResourceRequest:
            return await EnumerateAllCustomResources(
                requestMessage: req,
                tableClient: tableClient,
                partitionKey: partitionKey,
                resourceType: rowKey);

        // Retrieve request for a custom resource.
        case HttpMethod m when m == HttpMethod.Get && isResourceRequest:
            return await RetrieveCustomResource(
                requestMessage: req,
                tableClient: tableClient,
                partitionKey: partitionKey,
                rowKey: rowKey);

        // Create request for a custom resource.
        case HttpMethod m when m == HttpMethod.Put && isResourceRequest:
            return await CreateCustomResource(
                requestMessage: req,
                tableClient: tableClient,
                azureResourceId: azureResourceId,
                partitionKey: partitionKey,
                rowKey: rowKey);

        // Remove request for a custom resource.
        case HttpMethod m when m == HttpMethod.Delete && isResourceRequest:
            return await RemoveCustomResource(
                requestMessage: req,
                tableClient: tableClient,
                partitionKey: partitionKey,
                rowKey: rowKey);

        // Invalid request received.
        default:
            return req.CreateResponse(HttpStatusCode.BadRequest);
    }
}

Güncelleştirilmiş Run yöntemi artık Azure Tablo depolama için eklediğiniz tableClient giriş bağlamasını içerir. Yöntemin ilk bölümü üst bilgiyi okur x-ms-customproviders-requestpath ve değeri kaynak kimliği olarak ayrıştırmak için kitaplığını kullanır Microsoft.Azure.Management.ResourceManager.Fluent . Üst x-ms-customproviders-requestpath bilgi özel kaynak sağlayıcısı tarafından gönderilir ve gelen isteğin yolunu belirtir.

Ayrıştırılmış kaynak kimliğini kullanarak, verilerin araması veya özel kaynakları depolaması için partitionKey ve rowKey değerlerini oluşturabilirsiniz.

Yöntemleri ve sınıfları ekledikten sonra işlev uygulaması için kullanma yöntemlerini güncelleştirmeniz gerekir. C# dosyasının en üstüne aşağıdaki kodu ekleyin:

#r "Newtonsoft.Json"
#r "Microsoft.WindowsAzure.Storage"
#r "../bin/Microsoft.Azure.Management.ResourceManager.Fluent"

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Configuration;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Collections.Generic;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Azure.Data.Table;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Bu öğreticinin herhangi bir noktasında kaybolursanız, kod örneğinin tamamını özel kaynak sağlayıcısı C# RESTful uç nokta başvurusunda bulabilirsiniz. İşlev uygulamasını tamamladıktan sonra işlev uygulaması URL'sini kaydedin. Sonraki öğreticilerde işlev uygulamasını tetikleme amacıyla kullanılabilir.

Sonraki adımlar

Bu makalede, Azure Özel Kaynak Sağlayıcısı uç noktasıyla çalışmak için bir RESTful uç noktası yazdınız. Özel kaynak sağlayıcısı oluşturmayı öğrenmek için Özel kaynak sağlayıcısı oluşturma ve kullanma makalesine gidin.