Problem Statement
In many SAP projects, we need to integrate external services such as map servers, custom search widgets, or analytics dashboards directly into a Fiori application.
Often, these services return complete HTML content including scripts (JavaScript, AJAX calls, etc.) instead of just raw data.
While this works fine when tested standalone in a browser or even inside SAP Business Application Studio (BAS) preview, the same approach fails when deployed in Fiori Launchpad (FLP) because of Content Security Policy (CSP) restrictions.
Typical errors you might see:
Refused to execute inline script because it violates the following Content Security Policy directive...Failed to launch 'sapevent:...' because the scheme does not have a registered handler.oIframe.contentDocument is null
SAP Fiori Launchpad enforces strict CSP headers to ensure security
This means:
- Inline
<script>in HTML strings is blocked unless explicitly whitelisted. - Custom schemes like
sapevent:don’t work out-of-the-box. - iframes inside
sap.ui.core.HTMLmay load markup but block scripts or form submissions. - Content that works standalone often fails when running inside FLP due to these policies.
This creates a problem:
How do we still render dynamic HTML + JS inside a Fiori app without breaking CSP rules?
Solution
After exploring multiple approaches (sanitizing HTML, iframe srcdoc, sandboxing), the most reliable option is to:
Host the external HTML + JavaScript on the SAP system itself (e.g., as a BSP application) and then load it in the Fiori app via an <iframe> or navigation.
Steps:
1. Create a BSP application in the backend ABAP system
Upload the HTML/JS content (or wrap the API response) into an index.html page.
Ensure it is accessible at /sap/bc/bsp/sap/zwrapper/index.html
Sample ABAP Code for index.html
<%@page language="abap"%>
<%
DATA: lv_html TYPE string,
lv_payload TYPE string,
lv_response TYPE string.
" Get payload from query param or request body
lv_payload = escape( request->get_form_field( 'payload' ) ).
" Create HTTP client to externa API
DATA(lo_client) = NEW cl_http_client( ).
cl_http_client=>create_by_url(
EXPORTING
url = 'https://external-api.com.au/test/api'
IMPORTING
client = lo_client
).
lo_client->request->set_method( 'POST' ).
lo_client->request->set_content_type( 'application/json' ).
lo_client->request->set_cdata( lv_payload ).
lo_client->send( ).
lo_client->receive( ).
" Get response (HTML)
lv_response = lo_client->response->get_cdata( ).
" Output as-is
response->set_header_field( name = 'Content-Type' value = 'text/html; charset=UTF-8' ).
response->set_cdata( lv_response ).
%>
2. In your UI5 app, load the BSP page in an iframe instead of embedding raw HTML (If no Dynamic parameter has to be passed)
If you don’t have to pass any dynamic parameters then you can directly add bsp page path to HTML
<core:HTML id="sampleHTML" content='<iframe id="sampleIframe"
src="/sap/bc/bsp/sap/zwrapper/index.html"
width="100%" height="600px" frameborder="0"></iframe>' />
3. In your UI5 app, load the BSP page and pass dynamic parameters, in an iframe instead of embedding raw HTML
In many scenario we may have to pass the dynamic parmeters to our BSP index page, in those cases better set HTML Content from the controller.
var sPayload = encodeURIComponent(JSON.stringify(oPayload));
var sUrl = "/sap/bc/bsp/sap/zwrapper/index.html?payload=" + sPayload;
this.byId("searchHTML").setContent("<iframe src='" + sUrl + "' width='100%' height='600'></iframe>");
4. Handle Form Submission / Events
We have to add the event listener to the form for embedded HTML Content.
- Wait until the iframe finished loading.
- Grabbed the DOM reference of the iframe content.
- Attached an event listener directly to the
<form>element inside the iframe.
this.oSampleHTML.attachAfterRendering(
this._htmlDataLoaded.bind(this)
); _htmlDataLoaded: function(){
var oIframe = this.getView().getDomRef().querySelector("#sampleIframe");
if (oIframe) {
// Attach onload listener to iframe
oIframe.onload = function() {
// Now the iframe content is fully loaded
var iframeDoc = oIframe.contentDocument || oIframe.contentWindow.document;
if (iframeDoc) {
var oForm = iframeDoc.querySelector("#formID");
if (oForm) {
console.log("Form found inside iframe");
oForm.addEventListener("submit", function (e) {
e.preventDefault(); // prevent actual POST
var inputValue = iframeDoc.querySelector("#formData").value;
// Setup Address Data
this._takeYourAction(inputValue);
}.bind(this));
} else {
console.error("Form not found in iframe content");
}
}
}.bind(this);
}
}Key Takeaways
- Directly embedding HTML strings with
<script>inside SAPUI5 apps will fail under FLP CSP. - Always prefer hosting the HTML/JS inside a BSP app or static resource → then embed via
<iframe>. - Use
encodeURIComponentwhen passing JSON payloads via query parameters. - For cross-frame communication, add an event listener for HTML content.
This way, you respect SAP’s CSP rules without disabling them, while still integrating complex external HTML + JS applications seamlessly into Fiori Launchpad.