在自訂連接器中使用自訂程式碼

自訂程式碼可轉換超出現有原則範本所涵蓋範圍的要求和回應承載資料。 當你使用程式碼時,它優先於無程式碼的定義。

欲了解更多資訊,請參閱「 從零開始建立自訂連接器」。

指令碼類別

你的程式碼需要實作一個名為 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;
}

Regex 指令碼

以下範例接受一些要比對的文字和正則表達式,並在回應中傳回比對結果。

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# 命名空間都有支援。 目前,您只能從以下命名空間使用函數。

這很重要

目前支援的 .NET 版本為 .NET 標準 2.0。

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 連接器。

自訂程式碼常見問題集

想了解更多自訂程式碼,請參見 步驟 4:(可選)使用自訂程式碼支援。

問:我可以在每個自訂連接器中使用多個腳本嗎?答: 不行,你只能用一個自訂連接器的腳本檔案。

問:我在更新自訂連接器時會遇到內部伺服器錯誤。 這可能是什麼問題? 答: 很可能是編譯程式碼時出了問題。 未來系統會顯示完整的編譯錯誤清單,以改善此體驗。 目前, 先用支援類別 在本地測試編譯錯誤,作為一個變通方法。

問:我可以為程式碼加寫日誌並取得除錯的追蹤嗎?答: 目前還沒有,但未來會新增此功能支援。

問:在此期間我該如何測試我的程式碼?答: 在本地測試,並確保你能只使用 支援命名空間中提供的命名空間來編譯程式碼。 有關本地測試的資訊,請參閱 「在自訂連接器中撰寫程式碼」。

問:有什麼限制嗎?答: 是的。 您的指令碼必須在 2 分鐘內完成執行,且指令檔大小不能超過 1 MB。 這個新的 2 分鐘逾時設定適用於任何新建立的自訂連接器。 對於現有的自訂連接器,你需要更新連接器來套用新的逾時。

問:我可以用腳本程式碼建立自己的 HTTP 客戶端嗎?答: 目前是,但系統未來會封鎖這個功能。 建議的做法是使用 this.Context.SendAsync 方法。

問:我可以在本地資料閘道使用自訂程式碼嗎?答: 目前沒有。

虛擬網路支援

當您在 連結虛擬網路的 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
}

下一步

從頭開始建立自訂連接器

提供意見反應

非常感謝您提供有關連接器平台問題,或新功能構想的意見反應。 若要提供意見反應,請移至提交問題或取得連接器說明,然後選取您的意見反應類型。