Sdílet prostřednictvím


Vytvoření koncového bodu RESTful pro vlastní poskytovatele prostředků

Vlastní poskytovatel prostředků je kontrakt mezi Azure a koncovým bodem. S vlastními poskytovateli prostředků můžete přizpůsobit pracovní postupy v Azure. V tomto kurzu se dozvíte, jak vytvořit koncový bod RESTful vlastního poskytovatele prostředků. Pokud vlastní poskytovatele prostředků Azure neznáte, projděte si přehled vlastních poskytovatelů prostředků.

Poznámka

Tento kurz vychází z kurzu Nastavení Azure Functions pro vlastní poskytovatele prostředků. Některé kroky v tomto kurzu fungují jenom v případě, že je aplikace funkcí nastavená v Azure Functions pro práci s vlastními poskytovateli prostředků.

Práce s vlastními akcemi a vlastními prostředky

V tomto kurzu aktualizujete aplikaci funkcí tak, aby fungovala jako koncový bod RESTful pro vašeho vlastního poskytovatele prostředků. Prostředky a akce v Azure se modelují podle následující základní specifikace RESTful:

  • PUT: Vytvoření nového prostředku
  • GET (instance): Načtení existujícího prostředku
  • DELETE: Odebrání existujícího prostředku
  • POST: Aktivace akce
  • GET (kolekce): Výpis všech existujících prostředků

Pro účely tohoto kurzu použijete Azure Table Storage, ale funguje jakákoli databáze nebo služba úložiště.

Dělení vlastních prostředků v úložišti

Protože vytváříte službu RESTful, musíte vytvořené prostředky uložit. Pro Azure Table Storage musíte pro data vygenerovat klíče oddílů a řádků. U vlastních poskytovatelů prostředků by data měla být rozdělená na vlastního poskytovatele prostředků. Když se do vlastního poskytovatele prostředků odešle příchozí požadavek, vlastní poskytovatel prostředků přidá hlavičku x-ms-customproviders-requestpath odchozích požadavků do koncového bodu.

Následující příklad ukazuje hlavičku x-ms-customproviders-requestpath pro vlastní prostředek:

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

Na základě hlavičky x-ms-customproviders-requestpath můžete pro své úložiště vytvořit parametry partitionKey a rowKey , jak je znázorněno v následující tabulce:

Parametr Template (Šablona) Popis
partitionKey {subscriptionId}:{resourceGroupName}:{resourceProviderName} Parametr partitionKey určuje způsob dělení dat. Data obvykle rozděluje instance vlastního poskytovatele prostředků.
klíč řádku {myResourceType}:{myResourceName} Parametr rowKey určuje individuální identifikátor dat. Identifikátorem je obvykle název prostředku.

Musíte také vytvořit novou třídu pro modelování vlastního prostředku. V tomto kurzu přidáte do aplikace funkcí následující třídu CustomResource :

// 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 je jednoduchá obecná třída, která přijímá všechna vstupní data. Je založená na ITableEntity, která se používá k ukládání dat. CustomResource Třída implementuje všechny vlastnosti z rozhraní ITableEntity: timestamp, eTag, partitionKey a rowKey.

Podpora vlastních metod RESTful poskytovatele prostředků

Poznámka

Pokud nekopírujete kód přímo z tohoto kurzu, obsah odpovědi musí být platný json, který hlavičku Content-Type nastaví na application/json.

Teď, když jste nastavili dělení dat, vytvořte základní metody CRUD a aktivační metody pro vlastní prostředky a vlastní akce. Vzhledem k tomu, že vlastní poskytovatelé prostředků fungují jako proxy, musí koncový bod RESTful modelovat a zpracovávat požadavky a odpovědi. Následující fragmenty kódu ukazují, jak zpracovat základní operace RESTful.

Aktivace vlastní akce

U vlastních poskytovatelů prostředků se vlastní akce aktivuje prostřednictvím požadavků POST. Vlastní akce může volitelně přijmout text požadavku, který obsahuje sadu vstupních parametrů. Akce pak vrátí odpověď, která signalizuje výsledek akce a zda byla úspěšná nebo neúspěšná.

Do aplikace funkcí přidejte následující metodu TriggerCustomAction :

/// <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 Metoda přijme příchozí požadavek a vrátí odpověď se stavovým kódem.

Vytvoření vlastního prostředku

Pro vlastní poskytovatele prostředků se vlastní prostředek vytvoří prostřednictvím požadavků PUT. Vlastní poskytovatel prostředků přijímá text požadavku JSON, který obsahuje sadu vlastností vlastního prostředku. Prostředky v Azure se řídí modelem RESTful. Stejnou adresu URL požadavku můžete použít k vytvoření, načtení nebo odstranění prostředku.

Pokud chcete vytvořit nové prostředky, přidejte následující metodu CreateCustomResource :

/// <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;
}

Metoda CreateCustomResource aktualizuje příchozí požadavek tak, aby obsahoval ID, název a typ polí specifických pro Azure. Tato pole jsou vlastnosti nejvyšší úrovně, které používají služby v azure. Umožňují poskytovateli vlastních prostředků spolupracovat s dalšími službami, jako jsou Azure Policy, šablony Azure Resource Manager a protokol aktivit Azure.

Vlastnost Příklad Popis
Jméno {myCustomResourceName} Název vlastního prostředku
Typ Microsoft.CustomProviders/resourceProviders/{resourceTypeName} Obor názvů typu prostředku
id /subscriptions/{id_předplatného}/resourceGroups/{název_skupiny_prostředků}/
providers/Microsoft.CustomProviders/resourceProviders/{resourceProviderName}/
{resourceTypeName}/{myCustomResourceName}
ID prostředku

Kromě přidání vlastností jste také uložili dokument JSON do služby Azure Table Storage.

Načtení vlastního prostředku

U poskytovatelů vlastních prostředků se vlastní prostředek načítá prostřednictvím požadavků GET. Vlastní poskytovatel prostředků nepřijímá text požadavku JSON. V případě požadavků GET koncový bod použije hlavičku x-ms-customproviders-requestpath k vrácení již vytvořeného prostředku.

Přidejte následující metodu RetrieveCustomResource pro načtení existujících prostředků:

/// <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;
}

V Azure se prostředky řídí modelem RESTful. Adresa URL požadavku, která vytváří prostředek, také vrátí prostředek, pokud se provede požadavek GET.

Odebrání vlastního prostředku

U poskytovatelů vlastních prostředků se vlastní prostředek odebere prostřednictvím požadavků DELETE. Vlastní poskytovatel prostředků nepřijímá text požadavku JSON. V případě požadavku DELETE použije koncový bod hlavičku x-ms-customproviders-requestpath k odstranění již vytvořeného prostředku.

Pokud chcete odebrat existující prostředky, přidejte následující metodu RemoveCustomResource :

/// <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);
}

V Azure se prostředky řídí modelem RESTful. Adresa URL požadavku, která vytváří prostředek, také prostředek odstraní, pokud se provede požadavek DELETE.

Vypsat všechny vlastní prostředky

Pro vlastní poskytovatele prostředků můžete vytvořit výčet seznamu existujících vlastních prostředků pomocí kolekcí požadavků GET. Vlastní poskytovatel prostředků nepřijímá text požadavku JSON. Pro kolekci požadavků GET použije koncový bod hlavičku x-ms-customproviders-requestpath k vytvoření výčtu již vytvořených prostředků.

Přidejte následující metodu EnumerateAllCustomResources pro výčet existujících prostředků:

/// <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;
}

Poznámka

RowKey QueryComparisons.GreaterThan a QueryComparisons.LessThan je syntaxe služby Azure Table Storage, která provádí dotaz startswith pro řetězce.

Pokud chcete zobrazit seznam všech existujících prostředků, vygenerujte dotaz Azure Table Storage, který zajistí, že prostředky existují v rámci vlastního oddílu poskytovatele prostředků. Dotaz pak zkontroluje, jestli klíč řádku začíná stejnou {myResourceType} hodnotou.

Integrace operací RESTful

Po přidání všech metod RESTful do aplikace funkcí aktualizujte hlavní metodu Run , která volá funkce pro zpracování různých požadavků REST:

/// <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);
    }
}

Aktualizovaná metoda Run teď obsahuje vstupní vazbu tableClient , kterou jste přidali pro Azure Table Storage. První část metody přečte hlavičku x-ms-customproviders-requestpath a použije knihovnu Microsoft.Azure.Management.ResourceManager.Fluent k analýze hodnoty jako ID prostředku. Hlavičku x-ms-customproviders-requestpath odešle vlastní poskytovatel prostředků a určuje cestu příchozího požadavku.

Pomocí analyzovaného ID prostředku můžete vygenerovat hodnoty partitionKey a rowKey pro data pro vyhledání nebo uložení vlastních prostředků.

Po přidání metod a tříd je potřeba aktualizovat metody using pro aplikaci funkcí. Na začátek souboru jazyka C# přidejte následující kód:

#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;

Pokud se v libovolném okamžiku tohoto kurzu ztratíte, najdete kompletní ukázku kódu v referenčních informacích o koncovém bodu RESTful pro vlastního poskytovatele prostředků C#. Po dokončení aplikace funkcí uložte adresu URL aplikace funkcí. Můžete ho použít k aktivaci aplikace funkcí v pozdějších kurzech.

Další kroky

V tomto článku jste vytvořili koncový bod RESTful pro práci s koncovým bodem vlastního poskytovatele prostředků Azure. Informace o vytvoření vlastního poskytovatele prostředků najdete v článku Vytvoření a použití vlastního poskytovatele prostředků.