تأليف نقطة نهاية RESTful لموفري الموارد المخصصة
موفر الموارد المخصص هو عقد بين Azure ونقطة نهاية. باستخدام موفري الموارد المخصصين، يمكنك تخصيص مهام سير العمل على Azure. يوضح هذا البرنامج التعليمي كيفية تأليف نقطة نهاية RESTful لموفر موارد مخصص. إذا لم تكن على دراية بموفري الموارد المخصصة من Azure، فراجع نظرة عامة حول موفري الموارد المخصصة.
ملاحظة
يعتمد هذا البرنامج التعليمي على البرنامج التعليمي إعداد وظائف Azure لموفري الموارد المخصصة. تعمل بعض الخطوات الواردة في هذا البرنامج التعليمي فقط إذا تم إعداد تطبيق وظائف في Azure Functions للعمل مع موفري الموارد المخصصين.
العمل مع الإجراءات المخصصة والموارد المخصصة
في هذا البرنامج التعليمي، يمكنك تحديث تطبيق الوظائف للعمل كنقطة نهاية RESTful لموفر الموارد المخصص. تم تصميم الموارد والإجراءات في Azure وفقاً لمواصفات RESTful الأساسية التالية:
- PUT: أنشئ مورداً جديداً
- GET (مثيل) : استرداد مورد موجود
- DELETE: إزالة مورد موجود
- POST: لبدء إجراء
- GET (مجموعة) : ضع قائمة بجميع الموارد الموجودة
بالنسبة لهذا البرنامج التعليمي، يمكنك استخدام مخزن Azure Table، لكن أي قاعدة بيانات أو خدمة تخزين تعمل.
قسم الموارد المخصصة في التخزين
نظراً لأنك تنشئ خدمة RESTful، فأنت بحاجة إلى تخزين الموارد التي تم إنشاؤها. بالنسبة لتخزين Azure Table، تحتاج إلى إنشاء أقسام ومفاتيح صف لبياناتك. بالنسبة لموفري الموارد المخصصة، يجب تقسيم البيانات إلى موفر الموارد المخصص. عند إرسال طلب وارد إلى موفر الموارد المخصص، يضيف x-ms-customproviders-requestpath
موفر الموارد المخصص العنوان إلى الطلبات الصادرة إلى نقطة النهاية.
يوضح المثال التالي رأس x-ms-customproviders-requestpath
لمورد مخصص:
X-MS-CustomProviders-RequestPath: /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.CustomProviders/resourceProviders/{resourceProviderName}/{myResourceType}/{myResourceName}
استناداً إلى العنوان x-ms-customproviders-requestpath
، يمكنك إنشاء معلمات partitionKey وrowKey لمخزنك كما هو موضح في الجدول التالي:
المعلمة | القالب | الوصف |
---|---|---|
partitionKey | {subscriptionId}:{resourceGroupName}:{resourceProviderName} |
تحدد المعلمة partitionKey كيفية تقسيم البيانات. عادة ما يتم تقسيم البيانات بواسطة مثيل موفر الموارد المخصص. |
rowKey | {myResourceType}:{myResourceName} |
تحدد المعلمة rowKey المعرف الفردي للبيانات. عادة ما يكون المعرف هو اسم المورد. |
تحتاج أيضاً إلى إنشاء فصل دراسي جديد لتصميم موردك المخصص. في هذا البرنامج التعليمي، يمكنك إضافة فئة 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 هو فئة عامة بسيطة تقبل أي بيانات إدخال. يعتمد على ITableEntity، والذي يستخدم لتخزين البيانات. تنفذ فئة CustomResource جميع الخصائص من الواجهة ITableEntity : timestamp وeTag وpartitionKey ومفتاح rowKey.
دعم أساليب RESTful لموفر الموارد المخصص
ملاحظة
إذا لم تكن تنسخ الرمز مباشرةً من هذا البرنامج التعليمي، فيجب أن يكون محتوى الاستجابة JSON صالحاً يعيّن رأس Content-Type
على application/json
.
الآن بعد أن قمت بإعداد تقسيم البيانات، قم بإنشاء CRUD الأساسية وطرق التشغيل للموارد المخصصة والإجراءات المخصصة. نظرا لأن موفري الموارد المخصصين يعملون كوكلاء، يجب أن تقوم نقطة نهاية RESTful بنمذجة الطلب والاستجابة والتعامل معها. توضح مقتطفات التعليمة البرمجية التالية كيفية التعامل مع عمليات RESTful الأساسية.
قم بتشغيل إجراء مخصص
بالنسبة لموفري الموارد المخصصة، يتم تشغيل إجراء مخصص من خلال طلبات POST. يمكن أن يقبل الإجراء المخصص اختيارياً نص طلب يحتوي على مجموعة من معلمات الإدخال. يقوم الإجراء بعد ذلك بإرجاع استجابة تشير إلى نتيجة الإجراء وما إذا كان قد نجح أو فشل.
أضف طريقة 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 طلباً وارداً وتردد استجابة لتعليمة برمجية للحالة.
إنشاء مورد مخصص
بالنسبة لموفري الموارد المخصصة، يتم إنشاء مورد مخصص من خلال طلبات PUT. يقبل موفر الموارد المخصص نص طلب JSON، والذي يحتوي على مجموعة من الخصائص للمورد المخصص. تتبع الموارد في Azure نموذج RESTful. يمكنك استخدام نفس عنوان URL للطلب لإنشاء مورد أو استرداده أو حذفه.
أضف طريقة 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;
}
تعمل طريقة CreateCustomResource على تحديث الطلب الوارد لتضمين الحقول الخاصة بـ Azure id وname وtype. هذه الحقول هي خصائص من المستوى الأعلى تستخدمها الخدمات عبر Azure. يسمحون لموفر الموارد المخصص بالتداخل مع خدمات أخرى مثل Azure Policy وقوالب Azure Resource Manager وسجل نشاط Azure.
الخاصية | مثال | الوصف |
---|---|---|
الاسم | {myCustomResourceName} | اسم المورد المخصص |
النوع | Microsoft.CustomProviders/resourceProviders/{resourceTypeName} | مساحة اسم نوع المورد |
المُعرف | /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/ providers/Microsoft.CustomProviders/resourceProviders/{resourceProviderName}/ {resourceTypeName}/{myCustomResourceName} |
معرف المورد |
بالإضافة إلى إضافة الخصائص، قمت أيضاً بحفظ مستند JSON في تخزين Azure Table.
استرداد مورد مخصص
بالنسبة لموفري الموارد المخصصة، يتم استرداد مورد مخصص من خلال طلبات GET.
لا يقبل موفر الموارد المخصص نص طلب JSON. بالنسبة لطلبات GET، تستخدم نقطة النهاية رأس x-ms-customproviders-requestpath
لإرجاع المورد الذي تم إنشاؤه بالفعل.
أضف طريقة RetrieveCustomResource التالية لاسترداد الموارد الحالية:
/// <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، تتبع الموارد نموذج RESTful. يقوم عنوان URL للطلب الذي ينشئ مورداً أيضاً بإرجاع المورد إذا تم تنفيذ طلب GET.
إزالة مورد مخصص
بالنسبة لموفري الموارد المخصصة، تتم إزالة مورد مخصص من خلال طلبات DELETE.
لا يقبل موفر الموارد المخصص نص طلب JSON. بالنسبة لطلب الحذف، تستخدم نقطة النهاية رأس x-ms-customproviders-requestpath
لحذف المورد الذي تم إنشاؤه بالفعل.
أضف طريقة 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);
}
في Azure، تتبع الموارد نموذج RESTful. يقوم عنوان URL للطلب الذي ينشئ موراً بحذف المورد أيضاً إذا تم تنفيذ طلب DELETE.
قائمة بجميع الموارد المخصصة
بالنسبة لموفري الموارد المخصصة، يمكنك تعداد قائمة بالموارد المخصصة الموجودة باستخدام طلبات GET للمجموعة.
لا يقبل موفر الموارد المخصص نص طلب JSON. لمجموعة من طلبات GET، تستخدم نقطة النهاية رأس x-ms-customproviders-requestpath
لتعداد الموارد التي تم إنشاؤها بالفعل.
أضف طريقة EnumerateAllCustomResources التالية لتعداد الموارد الموجودة:
/// <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;
}
ملاحظة
RowKey QueryComparisons.GicturesThan وQueryComparisons.LessThan هو بناء جملة تخزين Azure Table لتنفيذ استعلام "startswith" للسلاسل.
لسرد جميع الموارد الموجودة، قم بإنشاء استعلام تخزين Azure Table يضمن وجود الموارد ضمن قسم موفر الموارد المخصص. يتحقق الاستعلام بعد ذلك من أن مفتاح الصف يبدأ بنفس قيمة {myResourceType}
.
دمج عمليات RESTful
بعد إضافة جميع طرق RESTful إلى تطبيق الوظيفة، قم بتحديث طريقة Run الرئيسية التي تستدعي الوظائف للتعامل مع طلبات 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);
}
}
تتضمن طريقة التشغيل المحدثة الآن ربط الإدخال tableClient الذي أضفته لتخزين جدول Azure. يقرأ الجزء الأول من الطريقة رأس x-ms-customproviders-requestpath
ويستخدم مكتبة Microsoft.Azure.Management.ResourceManager.Fluent
لتحليل القيمة كمعرف مورد.
x-ms-customproviders-requestpath
يتم إرسال الرأس بواسطة موفر الموارد المخصص ويحدد مسار الطلب الوارد.
باستخدام معرف المورد الذي تم تحليله، يمكنك إنشاء قيم partitionKey وrowKey للبيانات للبحث عن الموارد المخصصة أو لتخزينها.
بعد إضافة الأساليب والفئات، تحتاج إلى تحديث طرق باستخدام لتطبيق الوظيفة. أضف الكود التالي إلى أعلى ملف C #:
#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;
إذا فقدت في أي نقطة من هذا البرنامج التعليمي، يمكنك العثور على نموذج التعليمات البرمجية الكامل في مرجع نقطة نهاية C# RESTful لموفر الموارد المخصص. بعد الانتهاء من تطبيق الوظيفة، احفظ عنوان URL لتطبيق الوظيفة. يمكن استخدامه لتشغيل تطبيق الوظيفة في البرامج التعليمية اللاحقة.
الخطوات التالية
في هذه المقالة، قمت بتأليف نقطة نهاية RESTful للعمل مع نقطة نهاية Azure Custom Resource Provider. لمعرفة كيفية إنشاء موفر موارد مخصص، انتقل إلى المقالة إنشاء موفر موارد مخصص واستخدامه.