사용자 지정 커넥터에 코드 작성

사용자 지정 코드는 기존 정책 템플릿의 범위를 넘어 요청 및 응답 페이로드를 변환합니다. 코드가 사용되면 코드가 없는 정의보다 우선합니다.

자세한 내용은 처음부터 사용자 지정 커넥터 만들기를 참고하세요.

스크립트 클래스

코드는 런타임 중에 호출되는 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# 네임스페이스가 지원되는 것은 아닙니다. 현재 다음 네임스페이스의 함수만 사용할 수 있습니다.

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단계: (선택 사항) 사용자 지정 코드 지원 사용으로 이동하세요.

질문: 사용자 지정 커넥터별로 여러 스크립트를 사용할 수 있습니까?
답변: 아니요, 사용자 지정 커넥터당 하나의 스크립트 파일만 지원됩니다.

질문: 사용자 지정 커넥터를 업데이트할 때 내부 서버 오류가 발생합니다. 무엇이 문제가 될 수 있습니까?
답변: 코드를 컴파일하는 문제일 것입니다. 앞으로 이 경험을 개선하기 위해 컴파일 오류의 전체 목록을 표시할 것입니다. 지금은 임시로 지원 클래스를 사용하여 로컬에서 컴파일 오류를 테스트하는 것이 좋습니다.

질문: 내 코드에 로깅을 추가하고 디버깅을 위한 추적을 얻을 수 있습니까?
답변: 현재는 아니지만 이에 대한 지원은 향후 추가될 예정입니다.

질문: 그동안 내 코드를 어떻게 테스트할 수 있습니까?
답변: 로컬에서 테스트하고 지원되는 네임스페이스에 제공된 네임스페이스만 사용하여 코드를 컴파일할 수 있는지 확인하세요. 로컬 테스트에 대한 정보를 보려면 사용자 지정 커넥터에 코드 작성으로 이동하세요.

질문: 제한이 있나요?
A:: 예. 스크립트는 5초 이내에 실행을 완료해야 하며 스크립트 파일의 크기는 1MB를 초과할 수 없습니다.

질문: 스크립트 코드에서 고유한 http 클라이언트를 만들 수 있습니까?
답변: 현재는 그렇지만 앞으로는 이를 차단할 것입니다. 권장되는 방법은 this.Context.SendAsync 메서드를 사용하는 것입니다.

질문: 온프레미스 데이터 게이트웨이에서 사용자 지정 코드를 사용할 수 있습니까?
답변: 현재 사용할 수 없습니다.

Virtual Network 지원

Virtual Network에 연결된 Power Platform 환경에서 커넥터를 사용하는 경우 다음과 같은 제한 사항이 적용됩니다.

  • Context.SendAsync는 퍼블릭 엔드포인트를 사용하므로 Virtual Network에 노출된 프라이빗 엔드포인트의 데이터에 액세스할 수 없습니다.

일반적으로 알려진 문제 및 제한 사항

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
}

다음 단계

처음부터 사용자 지정 커넥터 만들기

피드백 제공

커넥터 플랫폼 관련 문제 또는 새로운 기능 아이디어에 대한 피드백을 주셔서 정말 감사합니다. 피드백을 제공하려면 문제 제출 또는 커넥터 관련 도움말 보기로 이동하여 피드백 유형을 선택하십시오.