Discover R.iT Blog

Microsoft Dynamics 365 CRM mit einer Web-API koppeln: (k)ein einfacher Weg mit CORS

Geschrieben von Bastian Nowak | 29.03.19 11:00

Zu einem angepassten CRM-System gehören meist auch Form-Skripte. Diese können neben den Aufruf von Workflows und Aktionen auch mit Web API’s kommunizieren. Hierbei greift allerdings die Same-Origin-Policy, welche Domain- und Port-Übergreifende anfragen blockieren. In diesem Blogbeitrag möchten wir Ihnen näherbringen, wie derartige Probleme vermieden werden können.

Die Grundlage
In unserem Beispiel möchten wir aus einem CRM-Form-Skript einen  Web-API-Endpunkt aufrufen. Dazu verwenden wir folgenden JavaScript-Code im Form-Skript: 

    jQuery.ajax({
        type: "POST",
        url: "http://devcrm.rit.local:4435/api/" + controller + "/" + func,
        data: data,
        xhrFields: {
            withCredentials: true
        },
        success: successCallback,
        error: errorCallback
    });

Die Web-API lauscht auf einen anderen Port – Was nun ein Problem darstellt. Anfragen gelangen zwar an die Web-API, jedoch kann die Antworte nicht im JavaScript-Code des Form-Skripts ausgewertet werden. Ein Blick in die Konsole des Browsers (i.d.R. über F12 aufrufbar) offenbart das Problem. Man wird auf fehlende Header-Felder in der Server-Antwort (Response) hingewiesen.
 
Die Lösung
Um dieses Problem zu beheben, müssen ein paar Serverseitige Anpassungen durchgeführt werden. Konkret bedeutet das Folgendes:
-    Die Serverantwort muss den Header „Access-Control-Allow-Origin“ besitzen, welcher den Wert der aktuellen anfragenden Domain (Origin) beinhaltet (in diesem Fall die Domain des CRM).
-    Die Serverantwort muss den Header „Access-Control-Allow-Methods“ besitzen, welcher alle zulässigen HTTP-Methoden (Verben) beinhaltet. In unserem Fall ist GET und POST ausreichend.
-    Sofern Credentials wie Session-Cookies und NTLM-Authentifizierungs-Header übermittelt werden sollen, muss in den Ajax-Parametern das Feld „withCredentials“ auf „true“ gesetzt werden (siehe oben) und die Serverantwort den Header „Access-Control-Allow-Credentials“ beinhalten.
Da es sehr aufwendig ist diese Header-Felder manuell in jeder Controller-Funktion zu setzen, haben wir uns eine wiederverwendbare Funktion geschrieben.

        /// <summary>
        /// Erlaubt den Domainübergreifenden Zugriff auf die WebAPI
        /// </summary>
public static void HandleCorsRequest(HttpRequest request, HttpResponse response, List<string> allowedOrigins)
        {
            string currentOrigin = request.Headers["Origin"];

            //Prüfen, ob Anfragen aus dieser Quelle erlaubt sind
            if (allowedOrigins.Contains(currentOrigin))
                response.Headers.Add("Access-Control-Allow-Origin", currentOrigin);

            response.Headers.Add("Access-Control-Allow-Methods", "GET, POST");
            response.Headers.Add("Access-Control-Allow-Credentials", "true");
        }

Diese Funktion kann nun zentral in der Global.asax.cs aufgerufen werden und wird somit bei jedem Request verwendet.

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            List<string> allowedOrigins = new List<string>(new[]
            {
                "http://devcrm.rit.local:5555",
            });

            HandleCorsRequest(Request, Response, allowedOrigins);
        }

Fazit
Wenn man die nötigen Schritte zur Behebung von CORS-Problemen befolgt, kann die Implementierung sehr unkompliziert sein.