カスタム コネクタでカスタム コードを使用する

カスタム コードは、既存のポリシー テンプレートの範囲を超えて要求と応答のペイロードを変換します。 コードを使用する場合は、コードレス定義よりも優先されます。

詳細については、カスタム コネクタを一から作成するを参照してください。

スクリプト クラス

コードでは、実行時に実行される ExecuteAsync というメソッドを実装する必要があります。 必要に応じて、このクラスに他のメソッドを作成し、 ExecuteAsync メソッドから呼び出すことができます。 クラス名は Script する必要があり、 ScriptBaseを実装する必要があります。

public class Script : ScriptBase
{
    public override Task<HttpResponseMessage> ExecuteAsync()
    {
        // Your code here
    }
}

サポートするクラスとインターフェイスの定義

Script クラスは、次のクラスとインターフェイスを参照します。 ローカル テストとコンパイルに使用します。

public abstract class ScriptBase
{
    // Context object
    public IScriptContext Context { get; }

    // CancellationToken for the execution
    public CancellationToken CancellationToken { get; }

    // Helper: Creates a StringContent object from the serialized JSON
    public static StringContent CreateJsonContent(string serializedJson);

    // Abstract method for your code
    public abstract Task<HttpResponseMessage> ExecuteAsync();
}

public interface IScriptContext
{
    // Correlation Id
    string CorrelationId { get; }

    // Connector Operation Id
    string OperationId { get; }

    // Incoming request
    HttpRequestMessage Request { get; }

    // Logger instance
    ILogger Logger { get; }

    // Used to send an HTTP request
    // Use this method to send requests instead of HttpClient.SendAsync
    Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken);
}

サンプル

Hello World スクリプト

このサンプル スクリプトは、すべての要求の応答として常にHello Worldを返します。

public override async Task<HttpResponseMessage> ExecuteAsync()
{
    // Create a new response
    var response = new HttpResponseMessage();

    // Set the content
    // Initialize a new JObject and call .ToString() to get the serialized JSON
    response.Content = CreateJsonContent(new JObject
    {
        ["greeting"] = "Hello World!",
    }.ToString());

    return response;
}

正規表現スクリプト

次の例では、一致するテキストと正規表現式を受け取り、応答で一致の結果を返します。

public override async Task<HttpResponseMessage> ExecuteAsync()
{
    // Check if the operation ID matches what is specified in the OpenAPI definition of the connector
    if (this.Context.OperationId == "RegexIsMatch")
    {
        return await this.HandleRegexIsMatchOperation().ConfigureAwait(false);
    }

    // Handle an invalid operation ID
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
    response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
    return response;
}

private async Task<HttpResponseMessage> HandleRegexIsMatchOperation()
{
    HttpResponseMessage response;

    // We assume the body of the incoming request looks like this:
    // {
    //   "textToCheck": "<some text>",
    //   "regex": "<some regex pattern>"
    // }
    var contentAsString = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false);

    // Parse as JSON object
    var contentAsJson = JObject.Parse(contentAsString);

    // Get the value of text to check
    var textToCheck = (string)contentAsJson["textToCheck"];

    // Create a regex based on the request content
    var regexInput = (string)contentAsJson["regex"];
    var rx = new Regex(regexInput);

    JObject output = new JObject
    {
        ["textToCheck"] = textToCheck,
        ["isMatch"] = rx.IsMatch(textToCheck),
    };

    response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = CreateJsonContent(output.ToString());
    return response;
}

スクリプトの転送

次のサンプルは、着信要求をバックエンドに転送します。

public override async Task<HttpResponseMessage> ExecuteAsync()
{
    // Check if the operation ID matches what is specified in the OpenAPI definition of the connector
    if (this.Context.OperationId == "ForwardAsPostRequest")
    {
        return await this.HandleForwardOperation().ConfigureAwait(false);
    }

    // Handle an invalid operation ID
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
    response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
    return response;
}

private async Task<HttpResponseMessage> HandleForwardOperation()
{
    // Example case: If your OpenAPI definition defines the operation as 'GET', but the backend API expects a 'POST',
    // use this script to change the HTTP method.
    this.Context.Request.Method = HttpMethod.Post;

    // Use the context to forward/send an HTTP request
    HttpResponseMessage response = await this.Context.SendAsync(this.Context.Request, this.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
    return response;
}

スクリプトの転送および変換

次のサンプルは、着信要求を転送し、バックエンドから返された応答を変換します。

public override async Task<HttpResponseMessage> ExecuteAsync()
{
    // Check if the operation ID matches what is specified in the OpenAPI definition of the connector
    if (this.Context.OperationId == "ForwardAndTransformRequest")
    {
        return await this.HandleForwardAndTransformOperation().ConfigureAwait(false);
    }

    // Handle an invalid operation ID
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
    response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
    return response;
}

private async Task<HttpResponseMessage> HandleForwardAndTransformOperation()
{
    // Use the context to forward/send an HTTP request
    HttpResponseMessage response = await this.Context.SendAsync(this.Context.Request, this.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);

    // Do the transformation if the response was successful, otherwise return error responses as-is
    if (response.IsSuccessStatusCode)
    {
        var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false);
        
        // Example case: response string is some JSON object
        var result = JObject.Parse(responseString);
        
        // Wrap the original JSON object into a new JSON object with just one key ('wrapped')
        var newResult = new JObject
        {
            ["wrapped"] = result,
        };
        
        response.Content = CreateJsonContent(newResult.ToString());
    }

    return response;
}

サポートされている名前空間

すべての C# 名前空間がサポートされているわけではありません。 現在、次の名前空間からの関数のみを使用できます。

Important

現在サポートされている.NETバージョンは、Standard 2.0 .NETです。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

GitHub のサンプル

DocuSign コネクタの例については、GitHub の Power Platform コネクタをご覧ください。

カスタム コードに関する FAQ

カスタム コードの詳細については、「 手順 4: (省略可能) カスタム コード サポートを使用する」を参照してください。

Q: カスタム コネクタごとに複数のスクリプトを使用できますか。A: いいえ。カスタム コネクタごとに使用できるスクリプト ファイルは 1 つだけです。

Q: カスタム コネクタを更新すると、内部サーバー エラーが発生します。 何が問題でしょうか? A: ほとんどの場合、コードのコンパイルに問題があります。 今後、このエクスペリエンスを向上させるために、コンパイル エラーの完全な一覧が表示されます。 ここでは、 サポート クラス を使用して、回避策としてコンパイル エラーをローカルでテストします。

Q: コードにログ記録を追加し、デバッグ用のトレースを取得することはできますか?A: 現時点ではサポートされていませんが、この機能のサポートは今後追加される予定です。

Q: それまでの間にコードをテストするにはどうすればよいですか?A: ローカルでテストし、 サポートされている名前空間で提供されている名前空間のみを使用してコードをコンパイルできることを確認します。 ローカル テストの詳細については、「 カスタム コネクタでコードを記述する」を参照してください。

Q: 制限はありますか?A: はい。 スクリプトは 2 分以内に実行を終了する必要があり、スクリプト ファイルのサイズが 1 MB を超えることはできません。 この新しい 2 分間のタイムアウトは、新しく作成されたカスタム コネクタに適用されます。 既存のカスタム コネクタの場合は、新しいタイムアウトを適用するようにコネクタを更新する必要があります。

Q: スクリプト コードで独自の HTTP クライアントを作成できますか。A: 現時点では、はい、ただし、システムは将来的にこの機能をブロックします。 推奨される方法は、this.Context.SendAsync メソッドを使用することです。

Q: オンプレミス データ ゲートウェイでカスタム コードを使用できますか。A: 現時点では、いいえ。

Virtual Network のサポート

仮想ネットワークにリンクされている Power Platform 環境でコネクタを使用する場合は、次の制限事項が適用されます。

  • Context.SendAsync はパブリック エンドポイントを使用するため、仮想ネットワーク上のプライベート エンドポイントからデータにアクセスすることはできません。

一般的な既知の問題と制限

一部のリージョンでは、 OperationId ヘッダーは base64 でエンコードされた形式で返されます。 実装で OperationId 値が必要な場合は、base64 でデコードして使用します。

public override async Task<HttpResponseMessage> ExecuteAsync()
{
    string realOperationId = this.Context.OperationId;
    // Resolve potential issue with base64 encoding of the OperationId
    // Test and decode if it's base64 encoded
    try {
        byte[] data = Convert.FromBase64String(this.Context.OperationId);
        realOperationId = System.Text.Encoding.UTF8.GetString(data);
    }
    catch (FormatException ex) {}
    // Check if the operation ID matches what is specified in the OpenAPI definition of the connector
    if (realOperationId == "RegexIsMatch")
    // Refer to the original examples above for remaining details
}

次のステップ

カスタム コネクタを最初から作成する

フィードバックを提供する

コネクタ プラットフォームの問題点や新機能のアイデアなど、フィードバックをお待ちしています。 フィードバックを提供するには、「問題を送信するか、コネクタに関するヘルプを入手する」にアクセスし、フィードバックの種類を選択します。