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.