Pisanie kodu w łączniku niestandardowym

Niestandardowy kod przekształca żądania i odpowiedzi poza zakresem istniejących szablonów polityki. Gdy używany jest kod, ma on pierwszeństwo przed definicją bez kodu.

Aby uzyskać więcej informacji, przejdź do strony Tworzenie łącznika niestandardowego od podstaw.

Klasa skryptu

Kod musi zaimplementować metodę ExecuteAsync, która jest wywoływana w czasie wykonywania. W razie potrzeby można utworzyć inne metody w tej klasie i wywołać je za pomocą metody ExecuteAsync. Nazwa klasy musi być Skryptem i musi implementować ScriptBase.

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

Definicje klas pomocy technicznej i interfejsów

Do klas skryptów odwołują się następujące klasy i interfejsy. Można ich używać do lokalnego testowania i kompilacji.

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

Przykłady

Skrypt Hello World

Ten przykładowy skrypt zawsze zwraca Hello World jako odpowiedź dla wszystkich żądań.

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

Skrypt regex

Poniższa próbka pobiera pewien tekst do dopasowania oraz wyrażenie regex i zwraca wynik dopasowania w odpowiedzi.

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

Skrypt przesyłania dalej

Poniższy przykładowy przykład przesyła żądanie przychodzące do wewnętrznej bazy danych.

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

Skrypt przesyłania dalej i przekształcenia

Poniższy przykład przesyła żądanie przychodzące i przekształci odpowiedź zwróconą z wewnętrznej bazy danych.

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

Obsługiwane przestrzenie nazw

Nie wszystkie przestrzenie nazw języka C# są obsługiwane. Obecnie można używać funkcji tylko z następujących przestrzeni nazw.

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;

Przykłady GitHub

Przykłady łącznika DocuSign znajdziesz na stronie łączniki Power Platform w GitHub.

Kod niestandardowy – FAQ

Aby dowiedzieć się więcej o kodzie niestandardowym, przejdź do kroku 4: (opcjonalnie) Użycie pomocy technicznej dotyczącej niestandardowego kodu.

Pyt. Czy na łącznik niestandardowy można używać wielu skryptów?
Odp.: Nie, obsługiwany jest tylko jeden plik skryptu dla każdego łącznika niestandardowego.

Pyt. W przypadku aktualizowania mojego łącznika niestandardowego jest wyświetlany komunikat o błędzie serwera wewnętrznego. Jaki może być problem?
Odp. Najprawdopodobniej jest to problem podczas kompilowania kodu. W przyszłości wyświetlimy pełną listę błędów kompilacji, aby poprawić jakość tego doświadczenia. W celu rozwiązania tego problemu zalecamy użycie klas pomocy technicznej do lokalnego testowania błędów kompilacji.

Pyt. Czy można dodać rejestrowanie do kodu i uzyskać śledzenie na potrzeby debugowania?
Odp.: Obecnie nie, ale obsługa tej pomocy zostanie dodana w przyszłości.

Pyt. Jak w tym momencie mogę przetestować kod?
Odp.: Przetestuj go lokalnie i upewnij się, że kod można przygotować przy użyciu tylko przestrzeni nazw dostępnych w obsługiwanych przestrzeniach nazw. Aby uzyskać informacje o testowaniu lokalnym, przejdź do tematu Pisanie kodu w łączniku niestandardowym.

Pyt. Czy są jakieś ograniczenia?
Odpowiedź: Tak. Skrypt musi zakończyć wykonywanie w ciągu 5 sekund, a rozmiar pliku skryptu nie może być większy niż 1 MB.

Pyt. Czy w kodzie skryptowym można utworzyć własnego klienta http?
Odp.: Obecnie tak, ale w przyszłości zostanie to zablokowane. Zaleca się korzystanie z tej metody Metoda context.SendAsync.

Pyt.: Czy mogę używać kodu niestandardowego z lokalną bramą danych?
Odp.: Obecnie, nie.

Pomoc techniczna usługi Virtual Network

Gdy łącznik jest używany w środowisku Power Platform połączonym z siecią wirtualną, obowiązują ograniczenia:

  • Context.SendAsync używa publicznego punktu końcowego, dlatego nie może uzyskać dostępu do danych z prywatnych punktów końcowych ujawnionych w sieci wirtualnej.

Ogólne znane problemy i ograniczenia

Nagłówek OperationId może zostać zwrócony w formacie zakodowanym base64 w niektórych regionach. Jeśli wartość OperationId jest wymagana do implementacji, w celu jej użycia powinna ona zostać zdekodowana z formatu Base64 w sposób podobny do następującego.

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
}

Następny krok

Tworzenie łącznika niestandardowego od podstaw

Przekazywanie opinii

Jesteśmy wdzięczni za opinie na temat problemów z platformą łączników oraz pomysły na nowe funkcje. Aby przekazać opinię, przejdź na stronę Przesyłanie problemów lub uzyskiwanie pomocy dotyczącej łączników i wybierz typ opinii.