사용자 지정 리소스 공급자 C# RESTful 엔드포인트 참조
이 문서는 사용자 지정 리소스 공급자 C# RESTful 엔드포인트에 대한 기본 참조입니다. Azure 사용자 지정 리소스 공급자에 익숙하지 않은 경우 사용자 지정 리소스 공급자에 대한 개요를 참조하세요.
Azure Functions RESTful 엔드포인트
다음 코드는 Azure의 함수 앱에서 작동합니다. Azure 사용자 지정 리소스 공급자와 작동하도록 함수 앱을 설정하는 방법을 알아보려면 Azure 사용자 지정 리소스 공급자용 Azure Functions 설정에 대한 자습서를 참조하세요.
#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 Microsoft.WindowsAzure.Storage.Table;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
// Custom Resource Table Entity
public class CustomResource : TableEntity
{
public string Data { get; set; }
}
/// <summary>
/// Entry point for the Azure Function 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="tableStorage">The Azure Table storage account.</param>
/// <returns>The HTTP response for the custom Azure API.</returns>
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ILogger log, CloudTable tableStorage)
{
// 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,
tableStorage: tableStorage,
partitionKey: partitionKey,
resourceType: rowKey);
// Retrieve request for a custom resource.
case HttpMethod m when m == HttpMethod.Get && isResourceRequest:
return await RetrieveCustomResource(
requestMessage: req,
tableStorage: tableStorage,
partitionKey: partitionKey,
rowKey: rowKey);
// Create request for a custom resource.
case HttpMethod m when m == HttpMethod.Put && isResourceRequest:
return await CreateCustomResource(
requestMessage: req,
tableStorage: tableStorage,
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,
tableStorage: tableStorage,
partitionKey: partitionKey,
rowKey: rowKey);
// Invalid request received.
default:
return req.CreateResponse(HttpStatusCode.BadRequest);
}
}
/// <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;
}
/// <summary>
/// Enumerates all the stored custom resources for a given type.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableStorage">The Azure Table storage account.</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, CloudTable tableStorage, string partitionKey, string resourceType)
{
// Generate upper bound of the query.
var rowKeyUpperBound = new StringBuilder(resourceType);
rowKeyUpperBound[rowKeyUpperBound.Length - 1]++;
// Create the enumeration query.
var enumerationQuery = new TableQuery<CustomResource>().Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey),
TableOperators.And,
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThan, resourceType),
TableOperators.And,
TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, rowKeyUpperBound.ToString()))));
var customResources = (await tableStorage.ExecuteQuerySegmentedAsync(enumerationQuery, null))
.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;
}
/// <summary>
/// Retrieves a custom resource.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableStorage">The Azure Table storage account.</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, CloudTable tableStorage, string partitionKey, string rowKey)
{
// Attempt to retrieve the Existing Stored Value
var tableQuery = TableOperation.Retrieve<CustomResource>(partitionKey, rowKey);
var existingCustomResource = (CustomResource)(await tableStorage.ExecuteAsync(tableQuery)).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;
}
/// <summary>
/// Creates a custom resource and saves it to Table storage.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableStorage">The Azure Table storage account.</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, CloudTable tableStorage, ResourceId azureResourceId, string partitionKey, string rowKey)
{
// Constructs the new resource from the request body and adds the Azure Resource Manager fields.
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 insertOperation = TableOperation.InsertOrReplace(
new CustomResource
{
PartitionKey = partitionKey,
RowKey = rowKey,
Data = myCustomResource.ToString(),
});
await tableStorage.ExecuteAsync(insertOperation);
var createResponse = requestMessage.CreateResponse(HttpStatusCode.OK);
createResponse.Content = new StringContent(myCustomResource.ToString(), System.Text.Encoding.UTF8, "application/json");
return createResponse;
}
/// <summary>
/// Removes an existing custom resource.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <param name="tableStorage">The Azure Table storage account.</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, CloudTable tableStorage, string partitionKey, string rowKey)
{
// Attempt to retrieve the existing stored value
var tableQuery = TableOperation.Retrieve<CustomResource>(partitionKey, rowKey);
var existingCustomResource = (CustomResource)(await tableStorage.ExecuteAsync(tableQuery)).Result;
if (existingCustomResource != null) {
var deleteOperation = TableOperation.Delete(existingCustomResource);
await tableStorage.ExecuteAsync(deleteOperation);
}
return requestMessage.CreateResponse(
existingCustomResource != null ? HttpStatusCode.OK : HttpStatusCode.NoContent);
}