Skip to content

Business Central + Azure Functions: Sichere und robuste API-Integration mit AL und C#

Praxis-Tutorial · AL · C# · Azure

Eine Business-Central-Schnittstelle sollte nicht nur funktionieren, sondern Fehler zuverlässig überstehen. Wir betrachten Architektur, Beispielcode, Sicherheit, Idempotenz und Monitoring.

Warum diese Architektur?

Ein erfolgreicher HTTP-Aufruf ist noch keine zuverlässige Integration. Wenn ein Request aus Business Central abbricht, kann das Zielsystem ihn trotzdem verarbeitet haben. Ein erneuter Versand erzeugt dann möglicherweise doppelte Datensätze. Dieses Tutorial erklärt eine robuste Architektur für Business Central, Azure Functions und .NET – unabhängig von konkreten Kundenprojekten.

Architektur und Verantwortlichkeiten

Business Central (AL)
  → HTTPS / authentifizierte API
Azure Function (.NET isolated)
  → Validierung → persistenter Idempotency-Key
  → Queue / Datenbank
Worker → Zielsystem
  → Status + Correlation ID + Monitoring

Die Azure Function übernimmt Eingabeprüfung und Übergabe, ein Worker verarbeitet langsamere Aufgaben. Eine HTTP-Antwort 202 Accepted bestätigt nur die Annahme, nicht die fertige Verarbeitung. Die Nachricht muss vor dieser Antwort dauerhaft gespeichert sein.

Der HTTP-Vertrag

POST /api/orders
Content-Type: application/json
Idempotency-Key: tenant1-order-10042-v1
x-functions-key: <secret>

{
  "documentNo": "10042",
  "customerNo": "C10000",
  "amount": 199.95,
  "currency": "EUR"
}

Ein stabiler, fachlicher Schlüssel verhindert unbeabsichtigte Doppelverarbeitung. Für produktive Installationen muss der Schlüssel Mandant, Umgebung, Vorgang, Dokument und gegebenenfalls Version unterscheiden.

Azure Function in C#

Minimaler HTTP-Endpunkt im .NET-isolated-Worker. Das Beispiel ist ein Lernbeispiel: Die persistente Speicherung und Queue sind bewusst noch nicht implementiert.

using System.Net;
using System.Text.Json;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;

public record OrderDto(string DocumentNo, decimal Amount);

public class OrderEndpoint
{
    [Function("Orders")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post",
          Route = "orders")] HttpRequestData request)
    {
        OrderDto? order;
        try {
            order = await JsonSerializer.DeserializeAsync<OrderDto>(
                request.Body,
                new JsonSerializerOptions {
                    PropertyNameCaseInsensitive = true
                });
        } catch (JsonException) {
            return request.CreateResponse(HttpStatusCode.BadRequest);
        }
        if (order is null || string.IsNullOrWhiteSpace(order.DocumentNo))
            return request.CreateResponse(HttpStatusCode.BadRequest);

        // TODO: Idempotency-Key atomar speichern und Auftrag
        // dauerhaft in Queue/DB schreiben, bevor 202 gesendet wird.
        return request.CreateResponse(HttpStatusCode.Accepted);
    }
}

Function Keys sind ein einfacher Schutzmechanismus, aber nicht gleichwertig mit vollständiger Authentifizierung und Autorisierung. Für sensible produktive Integrationen kommen beispielsweise Entra ID, API Management und eingeschränkter Netzwerkzugriff infrage.

AL: HttpClient mit Fehlerbehandlung

In AL muss zuerst geprüft werden, ob der Transport geklappt hat. Erst danach kann der HTTP-Status ausgewertet werden:

codeunit 50120 "Order API Demo"
{
    procedure CallService()
    var
        Client: HttpClient;
        Response: HttpResponseMessage;
    begin
        // Beispiel-GET zum Testen der Verbindung.
        // Produktive POST-Requests benötigen Content,
        // Authentifizierung und einen Idempotency-Key.
        if not Client.Get('https://example.com/health', Response) then
            Error('HTTP transport failed.');

        if not Response.IsSuccessStatusCode() then
            Error('Service returned HTTP %1.',
                  Response.HttpStatusCode());
    end;
}

Wichtig: Business Central blockiert ausgehende HTTP-Requests einer Erweiterung standardmäßig. In der Erweiterungsverwaltung muss Allow HttpClient Requests für die jeweilige Extension aktiviert werden. Zugangsdaten gehören nicht fest in den AL-Code.

Idempotenz und Retry

Ein Timeout ist ein ungewisser Ausgang, nicht automatisch eine fehlgeschlagene Verarbeitung. Wiederhole einen Request nur mit demselben Idempotency-Key oder frage zunächst seinen Status ab.

  • 400/422: Datenvalidierung korrigieren, nicht blind wiederholen.
  • 401/403: Authentifizierung und Berechtigungen prüfen.
  • 409: Konflikt beim Idempotenz-Key oder bei der Version klären.
  • 429/503: begrenzte Retries mit Backoff, Jitter und gegebenenfalls Retry-After.
  • Timeout: zuerst an bereits erfolgte Verarbeitung denken.

Der Schlüssel muss persistent mit Request-Hash, Status und Ergebnis gespeichert werden. Eine atomare Reservierung verhindert Race Conditions zwischen parallel laufenden Function-Instanzen.

Monitoring, Tests und Go-live

Pro Operation sollten Correlation ID, Status, technische Fehlerkategorie und Dauer nachvollziehbar sein. Keine Secrets oder ungekürzten personenbezogenen Daten in Logs speichern. Vor dem Produktivbetrieb teste mindestens doppelte Requests, einen Timeout nach Speicherung, ungültige JSON-Daten, 429-Antworten und ein ausgefallenes Zielsystem.

Fazit: Nicht der HTTP-Aufruf, sondern der klar definierte Ablauf aus Validierung, Sicherheit, persistenter Idempotenz, Retry-Regeln und Monitoring macht eine ERP-Integration belastbar.

Quellen und weiterführende Dokumentation

Schritt für Schritt: Von einer Buchung bis zur Verarbeitung

Nehmen wir an, Business Central soll einen gebuchten Verkaufsauftrag an ein Fremdsystem übergeben. Zuerst entsteht in BC ein fachlicher Auftrag zur Übertragung. Dieser Auftrag hat eine eindeutige Kennung und einen Zustand wie „Offen“, „Angenommen“, „Verarbeitet“ oder „Fehler“. Diese Trennung ist wichtig: Die Buchung in BC und die Verarbeitung im Zielsystem sind zwei verschiedene Vorgänge und können nicht durch eine gemeinsame Datenbanktransaktion abgesichert werden.

  1. Datensatz vorbereiten: BC stellt die zu übertragenden Felder zusammen und speichert den aktuellen Übertragungszustand. Der Inhalt wird auf Pflichtfelder und Wertebereiche geprüft.
  2. Schlüssel festlegen: Für denselben fachlichen Vorgang verwendet BC bei jedem Retry denselben Idempotency-Key. Eine fachlich neue Version benötigt dagegen eine neue Kennung.
  3. Annahme absichern: Die Function validiert die Anfrage, reserviert den Schlüssel atomar und schreibt den Auftrag dauerhaft in einen Speicher beziehungsweise eine Queue.
  4. Antwort senden: Erst nach erfolgreicher dauerhafter Annahme liefert die API 202 Accepted und eine Vorgangs-ID zurück.
  5. Verarbeitung und Rückmeldung: Ein Worker ruft das Zielsystem auf und aktualisiert den Status. BC fragt diesen Status über einen geschützten Endpunkt ab oder verarbeitet eine abgesicherte Rückmeldung.

Warum ein erfolgreicher HTTP-Status nicht ausreicht

Ein häufiges Missverständnis: 202 Accepted bedeutet nicht, dass der Auftrag im Zielsystem angekommen ist. Die Antwort bestätigt lediglich, dass die Anwendung die Arbeit angenommen hat. Deshalb benötigt die Schnittstelle eine nachvollziehbare Statusabfrage wie GET /api/operations/{operationId}. Sie sollte mindestens operationId, status, lastUpdatedUtc und bei Fehlern einen technischen Fehlercode zurückgeben. Personenbezogene Daten oder vertrauliche Belegdetails gehören nicht ungefiltert in die Statusantwort.

Idempotenz richtig implementieren: Zustände und konkurrierende Requests

Stell dir vor, zwei Job-Queue-Läufe senden denselben Auftrag fast gleichzeitig. Ein einfaches „Datensatz vorhanden?“-Prüfen genügt nicht: Beide Prozesse könnten gleichzeitig „nein“ lesen und anschließend zwei Aufträge anlegen. Die Reservierung muss deshalb atomar erfolgen, beispielsweise über einen eindeutigen Datenbankschlüssel und einen einzelnen Insert-Vorgang. Der gespeicherte Eintrag enthält außerdem einen Hash des normalisierten Request-Inhalts. Wird derselbe Schlüssel mit anderem Inhalt benutzt, antwortet die API mit einem Konflikt, statt möglicherweise falsche Daten still zu übernehmen.

Bei exakt gleicher Wiederholung liefert die API den bereits bekannten Vorgang und Status zurück. Dadurch wird ein verlorengegangenes HTTP-Response beherrschbar: BC kann denselben Request noch einmal senden, ohne dass dadurch automatisch eine zweite fachliche Verarbeitung entstehen soll. Beachte aber, dass Idempotenz am Eingang noch keine „Exactly once“-Garantie für das entfernte Zielsystem bedeutet. Auch der Worker benötigt einen geeigneten Wiederanlaufmechanismus und möglichst einen eigenen eindeutigen Geschäftsschlüssel im Zielsystem.

Die AL-Seite: Fehlerklassen statt pauschalem Error

Im kurzen Codebeispiel oben wird bei einem Fehler sofort Error(...) ausgelöst. In einem interaktiven Test ist das verständlich; für einen automatisierten Integrationsprozess sollte man den Fehler jedoch strukturiert protokollieren und gegebenenfalls einen späteren Wiederholungsversuch planen. Unterscheide drei Fälle:

  • Kein HTTP-Response: Transportfehler, DNS-Problem, Timeout oder blockierte ausgehende Anfragen. Das fachliche Ergebnis ist möglicherweise unbekannt.
  • HTTP-Antwort mit 4xx: Meist liegt ein dauerhaftes Problem der Anfrage oder Berechtigung vor; ein Retry ohne Änderung hilft normalerweise nicht.
  • HTTP-Antwort mit 429/5xx: Ein vorübergehendes Problem ist möglich. Verwende begrenzte Wiederholungen, Backoff und die Vorgangskennung.

Die AL-Integration sollte zudem die zulässige Anzahl paralleler Aufrufe begrenzen. In produktiven Erweiterungen gehören API-Adressen, Authentifizierungsverfahren und Timeouts in eine zentrale Konfiguration. Geheimnisse sollten über sichere Secret-Speicherung beziehungsweise einen externen Authentifizierungsdienst genutzt werden und dürfen nicht in Quellcode, Protokollen oder Beispieldaten landen.

Authentifizierung: Function Key oder Microsoft Entra ID?

Ein Function Key ist ein gemeinsam verwendetes Geheimnis. Er identifiziert nicht automatisch einen einzelnen Anwender und bietet keine fein abgestuften Rollen. Für vertrauliche Integrationen ist eine Prüfung von Microsoft-Entra-ID-Zugriffstoken oder eine entsprechende vorgeschaltete API-Schicht meist geeigneter. Wichtig sind die Prüfung von Signatur, Aussteller, Zielgruppe und der tatsächlich erlaubten Berechtigung sowie das Prinzip der geringsten Rechte. Beschränke zusätzlich Netzwerkzugriffe, wenn der Betriebsfall es zulässt.

Monitoring in Azure und Business Central

Lege für jeden Auftrag eine Korrelations-ID fest, die BC, Function, Queue-Verarbeitung und Zielsystem zusammenführt. Trenne fachliche Fehler („Kundennummer unbekannt“) von technischen Fehlern („Dienst nicht erreichbar“). In Application Insights können Fehlerquote, Laufzeit und Warteschlangenalter überwacht werden; in BC sollte der Benutzer oder Administrator erkennen können, welche Dokumente offen, erfolgreich oder dauerhaft fehlgeschlagen sind. Ein fehlgeschlagener Vorgang braucht einen dokumentierten Prozess für erneutes Anstoßen und manuelle Klärung.

Testfälle, die vor dem Livegang nicht fehlen dürfen

  1. Gleichen Auftrag zweimal gleichzeitig senden: Es darf nur eine fachliche Verarbeitung entstehen.
  2. HTTP-Verbindung nach der dauerhaften Annahme abbrechen: Ein erneuter Request mit identischem Schlüssel muss denselben Vorgang liefern.
  3. Gleichen Schlüssel mit anderem Inhalt senden: Der Fehler muss eindeutig sein.
  4. Fehlerhafte Daten und fehlende Berechtigungen simulieren: Es darf keine automatische Endlosschleife entstehen.
  5. Queue oder Zielsystem vorübergehend deaktivieren: Nachrichten müssen erhalten bleiben und wieder aufgenommen werden können.
  6. Geheimnisse, E-Mail-Adressen und Beleginhalte in Logs prüfen: Keine unnötigen sensiblen Inhalte protokollieren.

Hinweis zur Umsetzbarkeit: Das C#-Snippet weiter oben zeigt weiterhin nur Validierung und eine beispielhafte HTTP-Antwort. Der produktive Einsatz erfordert zusätzlich die hier beschriebenen persistente Idempotenz, Queue-Verarbeitung, Authentifizierung und Statusspeicherung. Verwende den Ausschnitt nicht unverändert als vollständigen Integrationsdienst.

Kommentare (0)

Noch keine Kommentare. Schreibe den ersten Beitrag zur Diskussion.

Kommentar schreiben