Technical Articles
UI5: solving CORS issue – during local development – with proxy server
Quicklinks:
Proxy Server
Quick Ref
Sample Code
Dear friends, please try to stay calm when you see this error message:
Access to XMLHttpRequest at ‘file://…/webapp/manifest.json’ … has been blocked by CORS policy
I know what you’re thinking:
Why is life so difficult? I only wanted to open my UI5 app – it is only a very silly first test app
No reason to get angry…
Why not??? How can you stay calm?
Because I found how to solve
Ahh, and before that?
I was sooooo angry
Aha…
So angry that I had to find the solution
In google…
I found lots of many of questions, but no satisfying answers
And what did the non-satisfying answer say?
It said e.g. to use a proxy server
And how to do that?
Exactly, that’s the point.
Can we learn it here…?
Please go ahead, read this blog, it is for free and the code is simple
Some background information about the problem?
That can be found nicely explained in the internet, I don’t want to copy that…
Overview
We create a very small ui5 app, open it in browser and see the behated error
Then we create a proxy server, open our app with it – and see the beloved ui
Note:
If you want to use app router, see this blog
Prerequisites
To reduce the effort, we choose Node.js to create the server
So you need to have Node.js installed on your machine
See here for little help, no need to be familiar with node.js development
Step 1: Create UI5 App
In this tutorial, we’re creating a little silly app, using the silly sample code in appendix, but you can also use your own silly application
The only condition: it must fail with above error.
To create a new application, we create a root folder, e.g.
C:\tmp_cors_1
Inside that root folder, we create a folder webapp
The project structure looks like this:
This folder contains the files, which we copy from the appendix section
As mentioned, it is just a silly app displaying some silly text
Test it: open the app
To open a web app, we usually just double-click the index.html file, or drag & drop it to a browser window
However, if we try to open our silly little app, we get an empty page and the developer tools give the error message:
Access … has been blocked by CORS policy
The error message gives us a little hint: it says that CORS is (only) supported for “http protocol”
Solution: we need to serve our app by a little silly proxy server
Step 2: Create Proxy Server
Such proxy server can be very easily created with Node.js and express
Summarizing:
We create a node app which starts a server.
That server does nothing than to serve a static file
That’s all
Now guess WHICH file will be that static file in our example…???
It will be our index.html and all other files in webapp folder
To add one little silly requirement:
We want that the proxy server should be located in a separate folder, not in the application folder
As such, we create a folder with name proxyserver, located inside our root project folder tmp_cors_1 next to our webapp
Inside the proxyserver folder, we create 2 files:
package.json
and
app.js
Refer to screenshot for structure and content:
package.json
We paste the following content into the package.json file
{
"dependencies": {
"express": "^4.17.1"
}
}
This minimalistic content just defines a dependency to the express module, which is responsible for easily creating a server
To install the dependency:
Open command prompt, navigate to proxyserver folder and invoke
npm install –save
This will create a folder node_modules and copy all required libraries into it
app.js
The content of the application file
const express = require('express');
const app = express();
const path = require('path');
const parentFolderPath = path.resolve(__dirname, '..')
// add middleware to serve static file
app.use(express.static(path.join(parentFolderPath, 'webapp')))
// start the server
app.listen(8080, () => {
console.log('Serving file "../webapp/index.html". Open at "localhost:8080". Use Ctr+C to shutdown');
})
Note that we aren’t defining any endpoint.
No
app.get(‘/productList’)
or similar
We just don’t need it.
Express is doing that for us
This is the relevant line:
app.use(express.static(path.join(parentFolderPath,'webapp')))
We’re using express-middleware:
app.use(…)
And we’re using convenience functionality offered by express, to serve static content:
express.static(someFolder)
The static statement expects a folder as first param
We don’t need to pass our static file, because index.html is served by default
See docu
We could place our app.js next to the webapp folder, then we could just write
express.static(someFolder)
making things easy
But that would be little ugly, to have proxyserver logic mixed with the frontend logic
That’s why we wanted to have a separate folder for the proxyserver
And so we need one more line of code, to compute the path to the webapp folder
We step one level up:
const parentFolderPath = path.resolve(__dirname, ‘..’)
then one level down
path.join(parentFolderPath, ‘webapp’)
Note:
One big advantage of this little additional effort is:
It makes it easy to reuse the proxyserver
We can just copy & paste the proxyserver folder next to any other webapp folder
If you have deeper nested folder structure, just add additional folder to the join statement:
path.join(parentFolderPath,’projectfolder’, ‘webapp’)
Finally, let the server start up and listen at a port of our choice
app.listen(8080, () => {
console.log('Serving file "../webapp/index.html". Open at "localhost:8080". Use Ctr+C to shutdown');
})
After saving the file, we can start the server
We open command prompt and step into the proxyserver folder
Then, to run our app, we execute the following command:
node app.js
As a result we can see our log output
Now we’re ready to open our web application
Test it: open the app
We open our browser and type this address:
localhost:8080
As a result, we can see our beautiful (but silly) app UI:
Our browser screen is not (fully) empty and there’s (almost) no error in the log
That’s it.
We can now work on our app UI. Whatever change we do in the UI, we don’t need to restart the server, only refresh (or reload) the browser
To stop the server, we press Ctrl+ C in the command prompt.
Summary
In this little silly tutorial, we’ve learned how to create a little server
It is used to host our ui5 application
Like that, our app runs on a server
This solves CORS error
However, I know that you will run into more CORS errors
How do you know?
I know it, because that happened to me
You were angry…?
Yes…For that reason, I’m already preparing the next blog
Let me finish, then I’ll prepare the error
Stay tuned 😉
Where is it?
Thanks for your patience, it is finally published
Links
https://nodejs.org/en/
https://www.npmjs.com/package/express
http://expressjs.com/
http://expressjs.com/en/starter/static-files.html
Quick Reference
To run your ui5 app locally, on a local server, created with node.js:
– Create folder (next to your app) with 2 files:
package.json: define dependency, then run npm install
app.json: server code to serve static file
– Start the node server with node app.js
– Open browser at localhost:8080 to see your app
Appendix: Sample Project Files
For your convenience, see the project structure again:
UI5 App
The folder structure:
C:\tmp_cors_1
webapp
App.view.xml
webapp
Component.js
index.html
manifest.json
The content of the files:
App.view.xml
<mvc:View xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
<App id="app">
<pages>
<Page title="List of Products">
<content>
<Title level="H1" text="List of Products coming soon" />
</content>
</Page>
</pages>
</App>
</mvc:View>
Component.js
sap.ui.define(["sap/ui/core/UIComponent"],
function (UIComponent) {
return UIComponent.extend("com.local.prodapp.Component", {
metadata : {
manifest: "json"
},
init : function () {
UIComponent.prototype.init.apply(this, arguments);
},
});
});
index.html
<!DOCTYPE html>
<html>
<head>
<script id="sap-ui-bootstrap"
src="https://ui5.sap.com/resources/sap-ui-core.js"
data-sap-ui-theme="sap_belize"
data-sap-ui-resourceroots='{"com.local.prodapp": "./"}'
data-sap-ui-oninit="module:sap/ui/core/ComponentSupport">
</script>
</head>
<body class="sapUiBody" id="content">
<div data-sap-ui-component data-name="com.local.prodapp" ></div>
</body>
</html>
manifest.json
{
"sap.ui5": {
"rootView": {
"viewName": "com.local.prodapp.App",
"type": "XML"
},
"dependencies": {
"libs": {
"sap.ui.core": {},
"sap.m": {}
}
}
}
}
Proxyserver App
The folder structure:
C:\tmp_cors_1
proxyserver
app.js
package.json
The content of the files:
package.json
{
"dependencies": {
"express": "^4.17.1"
}
}
app.js
const express = require('express');
const app = express();
const path = require('path');
const parentFolderPath = path.resolve(__dirname, '..')
// add middleware to serve static file
app.use(express.static(path.join(parentFolderPath, 'webapp')))
// start the server
app.listen(8080, () => {
console.log('Serving file "../webapp/index.html". Open at "localhost:8080". Use Ctr+C to shutdown');
})
valid approach if you really want to go all the way per pedes 🙂
for better dev efficiency, use the official ui5 server (from https://github.com/SAP/ui5-server/blob/master/README.md) with the proxy server middleware (from https://github.com/petermuessig/ui5-ecosystem-showcase/blob/master/packages/ui5-middleware-simpleproxy/README.md)
you get more bang for the buck right out of the box (from local sapui5 node modules to live reloading).
best, v.
Hi Volker,
thanks for the comment and for the links.
That's what we've been waiting for, so long...;-)
Nevertheless, this tutorial shows how to write own local proxy, generic, simple, for any webapp, also non-ui5
Cheers,
Carlos
I know that CORS prevent us from access data from a remote server, that's why we need that proxy.
What I still don't get it is why we have this same issue loading manifest.json or XML views, since it is located in our own machine.
Hi Alexandre,
Yes, the browser is really very strict and it doesn't care if the browser and the app and the app and its files are running on the same laptop - the browser interprets it as a request to a resource - and it is still a different origin (file protocol, localhost port etc)
Thank You Carlos Roggan.
Worked like a charm 🙂
Super, Sai Kowluri thanks for the feedback, good to know !