Condividi tramite


Scrivere codice in un connettore personalizzato

Il codice personalizzato trasforma i payload di richieste e risposte oltre l'ambito di modelli di criteri esistenti. Il codice utilizzato avrà la precedenza sulla definizione senza codice.

Per ulteriori informazioni, consultare Creare un connettore personalizzato da zero.

Classe di script

Il codice deve implementare un metodo denominato ExecuteAsync, che viene chiamato durante il runtime. Puoi creare altri metodi in questa classe come necessario e chiamarli dal metodo ExecuteAsync. Il nome della classe deve essere Script e deve implementare ScriptBase.

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

Definizione di classi e interfacce di supporto

Le seguenti classi e interfacce sono referenziate dalla classe Script. Possono essere utilizzate per il test e la compilazione locali.

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

Esempi

Script Hello World

Questo script di esempio restituirà sempre Hello World come risposta per tutte le richieste.

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

L'esempio seguente utilizza del testo per la corrispondenza con l'espressione regex e restituisce il risultato della corrispondenza nella risposta.

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 di inoltro

L'esempio seguente inoltra la richiesta in ingresso al 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 di inoltro e trasformazione

L'esempio seguente inoltra la richiesta in ingresso e trasforma la risposta restituita dal 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;
}

Spazi dei nomi supportati

Non tutti gli spazi dei nomi C# sono supportati. Attualmente, puoi utilizzare soltanto le funzioni dei seguenti spazi dei nomi.

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;

Esempi di GitHub

Per esempi nel connettore DocuSign , vai a Power Platform Connettori su GitHub.

Domande frequenti sul codice personalizzato

Per saperne di più sul codice personalizzato, vai al Passaggio 4: (facoltativo) Utilizza il supporto per il codice personalizzato.

D: È possibile utilizzare più script per connettore personalizzato?
A: No, è supportato solo un file di script per connettore personalizzato.

D: Ricevo un errore interno del server durante l'aggiornamento del mio connettore personalizzato. Quale potrebbe essere il problema?
A: Molto probabilmente si tratta di un problema di compilazione del codice. In futuro, visualizzeremo l'elenco completo degli errori di compilazione per migliorare questa esperienza. Per ora, come soluzione alternativa, consigliamo di utilizzare le classi di supporto per testare localmente gli errori di compilazione.

D: Posso aggiungere la registrazione al mio codice e ottenere una traccia per il debug?
A: Al momento no, ma in futuro verrà aggiunto il supporto.

D: Nel frattempo, come posso testare il mio codice?
A: Esegui un test in locale e assicurati di poter compilare il codice utilizzando solo gli spazi dei nomi forniti in spazi dei nomi supportati. Per informazioni sui test locali, vai a Scrivi codice in un connettore personalizzato.

D: Ci sono dei limiti?
R: Sì. L'esecuzione dello script deve essere completata entro 2 minuti e la dimensione del file di script non può superare 1 MB. Questo nuovo timeout di 2 minuti si applica a tutti i connettori personalizzati appena creati. Per i connettori personalizzati esistenti, i clienti devono aggiornare il connettore per applicare il nuovo timeout.

D: Posso creare il mio client http tramite codice script?
A: Al momento sì, ma in futuro bloccheremo questa funzione. Il metodo consigliato è quello di utilizzare this.Context.SendAsync .

D: Posso utilizzare codice personalizzato con il gateway dati locale?
A: Al momento no.

Supporto di Rete virtuale

Quando il connettore viene utilizzato in un Power Platform ambiente collegato a una rete virtuale, si applicano delle limitazioni:

  • Context.SendAsync usa un endpoint pubblico, quindi non può accedere ai dati dagli endpoint privati esposti nella rete virtuale.

Problemi noti e limitazioni generali

L'intestazione OperationId può essere restituita in formato codificato base64 in alcune regioni. Se il valore di OperationId è richiesto per un'implementazione, questo deve essere decodificato in base64 per l'uso in maniera simile alla seguente.

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
}

Passaggio successivo

Crea un connettore personalizzato da zero

Fornire commenti

L'invio da parte degli utenti di feedback sui problemi riscontrati con la piattaforma di connettori o di idee su nuove funzionalità è molto apprezzato. Per inviare un feedback, vai su Segnala problemi o ottieni assistenza con i connettori e seleziona il tipo di feedback.