Escrever código em um conector personalizado

O código personalizado transforma cargas de solicitação e resposta além do escopo dos modelos de política existentes. Quando o código é usado, ele tem precedência sobre a definição sem código.

Para obter mais informações, acesse Criar um conector personalizado do zero.

Classe Script

Seu código precisa implementar um método chamado ExecuteAsync, que é chamado durante o tempo de execução. Você pode criar outros métodos nessa classe conforme necessário e chamá-los a partir do método ExecuteAsync. O nome da classe deve ser Script e deve implementar ScriptBase.

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

Definição de classes e interfaces de suporte

As classes e interfaces a seguir são referenciadas pela classe Script. Elas podem ser usadas para teste e compilação local.

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);
}

Exemplos

Script Hello World

Esse script de exemplo sempre retorna Hello World como resposta para todas as solicitações.

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;
}

Script Regex

O exemplo a seguir usa algum texto para fazer a correspondência e a expressão regex e retorna o resultado da correspondência na resposta.

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;
}

Script de encaminhamento

O exemplo a seguir encaminha a solicitação de entrada para o back-end.

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;
}

Script de encaminhamento e transformação

O exemplo a seguir encaminha a solicitação de entrada e transforma a resposta retornada do back-end.

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;
}

Namespaces com suporte

Nem todos os namespaces em C# têm suporte. No momento, você pode usar funções somente a partir dos namespaces a seguir.

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;

Exemplos do GitHub

Para exemplos no conector do DocuSign, acesse Conectores do Power Platform no GitHub.

Perguntas frequentes sobre código personalizado

Para saber mais sobre código personalizado, acesse Etapa 4: (opcional) Usar o suporte de código personalizado.

P: É possível usar vários scripts por conector personalizado?
R: Não, há suporte somente a um arquivo de script por conector personalizado.

P: Estou recebendo um erro interno do servidor ao atualizar meu conector personalizado. O que poderia ser o problema?
R: Provavelmente, isso é um problema de compilação do código. No futuro, exibiremos a lista completa de erros de compilação para melhorar essa experiência. Recomendamos usar as classe de suporte para testar os erros de compilação localmente, por enquanto, como uma solução alternativa.

P: Posso adicionar registro em log ao meu código e obter um rastreamento para depuração?
R: No momento, não, mas o suporte a isso será adicionado no futuro.

P: Como posso testar meu código enquanto isso?
R: Teste-o localmente e certifique-se de que possa compilar o código usando somente os namespaces fornecidos em namespaces de suporte. Para obter informações sobre testes locais, acesse Escrever código em um conector personalizado.

P: Existem limites?
R: Sim. Seu script deve terminar a execução em 5 segundos e o tamanho do arquivo de script não pode ser superior a 1 MB.

P: Posso criar meu próprio cliente http no código de script?
R: No momento, sim, mas vamos bloquear isso no futuro. A maneira recomendada é usar o método this.Context.SendAsync.

P: Posso usar código personalizado com o gateway de dados local?
R: No momento, não

Suporte à Rede Virtual

Quando o conector é usado em um ambiente do Power Platform vinculado a uma rede virtual, limitações se aplicam:

  • Context.SendAsync usa um ponto de extremidade público, portanto, ele não pode acessar dados de pontos de extremidade privados expostos na rede virtual.

Limitações e problemas gerais conhecidos

O cabeçalho da OperationId pode ser retornado no formato codificado em Base64 em determinadas regiões. Se o valor da OperationId for necessário para uma implementação, ela deverá ser decodificada em Base64 para uso semelhante ao seguinte:

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
}

Próxima etapa

Criar um conector personalizado do zero

Enviar comentários

Agradecemos muito os comentários sobre problemas com nossa plataforma de conectores ou novas ideias de recursos. Para fornecer comentários, acesseEnviar problemas ou obter ajuda com conectores e selecione o tipo de comentário.