사용자 지정 커넥터에서 사용자 지정 코드 사용

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

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

스크립트 클래스

코드는 런타임 중에 실행되는 메서드 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);
}

샘플

헬로 월드 스크립트

이 샘플 스크립트는 항상 모든 요청에 대한 응답으로 헬로 월드 반환합니다.

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

중요합니다

현재 지원되는 .NET 버전은 .NET Standard 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 커넥터로 이동하십시오.

사용자 지정 코드 FAQ

사용자 지정 코드에 대한 자세한 내용은 4단계: (선택 사항) 사용자 지정 코드 지원 사용

Q: 사용자 지정 커넥터당 여러 스크립트를 사용할 수 있나요?A: 아니요, 사용자 지정 커넥터당 하나의 스크립트 파일만 사용할 수 있습니다.

Q: 사용자 지정 커넥터를 업데이트할 때 내부 서버 오류가 발생합니다. 무엇이 문제가 될 수 있습니까? A: 대부분의 경우 코드를 컴파일하는 데 문제가 있습니다. 나중에 시스템은 이 환경을 개선하기 위해 컴파일 오류의 전체 목록을 표시합니다. 지금은 지원 클래스를 사용하여 컴파일 오류를 로컬로 테스트하여 해결 방법으로 사용합니다.

Q: 내 코드에 로깅을 추가하고 디버깅에 대한 추적을 가져올 수 있나요?A: 현재는 아니지만 이 기능에 대한 지원은 나중에 추가될 예정입니다.

Q: 그 동안 내 코드를 테스트하려면 어떻게 해야 하나요?A: 로컬로 테스트하고 지원되는 네임스페이스에 제공된 네임스페이스만 사용하여 코드를 컴파일할 수 있는지 확인합니다. 로컬 테스트에 대한 자세한 내용은 사용자 지정 커넥터의 코드 작성을 참조하세요.

Q: 제한이 있나요?A: 예. 스크립트는 2분 이내에 실행을 완료해야 하며 스크립트 파일의 크기는 1MB를 초과할 수 없습니다. 이 새로운 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
}

다음 단계:

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

피드백 제공

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