Cannot display EJS page in node.js web app - javascript

I have a simple nodejs application with folder structure like this (not a full structure, but enough for this question).
index.js
views
- index.ejs
- about.ejs
In index.js, I have
app.use("/", (request, response) => {
response.render('index')
})
app.use("/about", (request, response) => {
response.render('about')
})
// Also tried with extension (index.ejs and about.ejs)
In index.ejs, I have
About
The problem
When I go to root url (localhost:port/), index.ejs is displayed.
In index.ejs, I click on the link About, the url is changed to (localhost:port/about), but about.ejs is not rendered. However, checking F12 sources in browser (Microsoft Edge Chrominum), about.ejs is in there.
When I go to the link directly by typing the url to url bar, index.js is displayed. The behavior (in F12/sources) is the same compared step 2.
When I comment out (app.use("/") block in index.js, about.ejs is accessible and displayed on screen.
Note, the navigation works perfectly if they are html pages and served by node.js server. Everything has been test only on local server.
Question
What is the cause of this and how to fix?
Why do I use EJS?
I want to create a dynamic front end based on DB query.
There seems to be multiple questions about this on Stackoverflow, I have tried multiple of them and none fix my issue, or clearly explain the cause.

As #Quentin answered, the issue is due to my use of use. For the request, which is GET in this case, I have to use get function, and it fixes the problem.
use is applicable when I want to decompose my index.js or use middleware.

Related

Handle "Loading chunk failed" errors with Lazy-loading/Code-splitting

We are developing a Vue.js application based on Vue CLI 3 with Vue Router and Webpack. The routes are lazy-loaded and the chunk file names contain a hash for cache busting. In general, everything is working fine.
However, there is a problem during the deployment. Steps to reproduce are the following.
User opens the application (let's assume route "/"), thus the main chunk file is loaded.
We change something in the application and deploy a new version.
Old chunk files are removed
New chunk files are being added (i.e. hashes in the chunk file names change)
User clicks a link to another route (e.g. "/foo")
An error occurs as the application tries to load a chunk file that has been renamed: Error: "Loading CSS chunk foo failed.
(/assets/css/foo.abc123.css)" (this might be CSS or JavaScript)
What is the best way to avoid errors like this?
One approach that should work is just to retain old chunk files and delete them at a later time. That, however, complicates the deployment of new versions as you need to keep track of old versions and always also deploy the old chunk files with the new version.
Another (naive) approach is to just reload as soon as such an error is detected (e.g. Vue Lazy Routes & loading chunk failed). It somewhat works, but it reloads the old route, not the new one. But at least it ensure that consecutive route changes work again.
Any other ideas? Maybe there is something in webpack that could fix this?
DoNOT cache the entry file(usually index.html).
We add:
expires 0;
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate';
in our nginx server config.
Then, after you refreshed the client's code, you can use the vue-router's error hook to detect the error and do something properly.
As long as you have a versioned API, you can use the old app files (just leave them on the server and delete after a vew days).
You will get problems as soon as your API changes during deployments.
I assume, you deploy a new API each time you deploy new JS code.
Then you can:
Pass on the API version (simply use the git hash) to the application as header with every response (JS resources, CSS, API requests, 404 responses)
Store the API version in your main JS entry point (or make it accessible somehow, e.g. as generated constant)
On each server response, check if the Server version matches your main client version.
If it does not: Display a prominent warning to the user (like the cookie banners) that he should reload the page (=> allows the user to save chnages in hope the API did not change for that save button).
For async components, we display normal 'not found' messages if loading fails, together with a reload button that appears instead of the component. Reloading without user interaction will cause a lot of confusion.

Why is Angular Universal necessary?

So the obvious answer is that its necessary because it serves routed paths from the server, so that we don't get 404s.
However solutions like angular-cli-ghpages solves this by adding a script to the app that parses parameters returned in a 404 that will then reroute the app to the correct state.
So just curious are there any drawbacks to this and why would this not be used in general instead of solutions like Angular Universal or Rendertron?
For example this is what spa-github-pages says:
A quick SEO note - while it's never good to have a 404 response, it appears based on Search Engine Land's testing that Google's crawler will treat the JavaScript window.location redirect in the 404.html file the same as a 301 redirect for its indexing. From my testing I can confirm that Google will index all pages without issue, the only caveat is that the redirect query is what Google indexes as the url. For example, the url example.tld/about will get indexed as example.tld/?p=/about. When the user clicks on the search result, the url will change back to example.tld/about once the site loads.
Because of two main things:
First page load speed;
SEO
Robots do not run javascript, so they parse what the get from server and than the Universal comes around.
Even using --aot builded app served by ghpages with a 404 page that is a clone from the index, the client/robot still needs to get the first files, parse them and finally mount the final view. Gh-pages do not serve the final html state.

External javascript in html is sent incorrectly to the server

I'm running a Node.js server along with an Angular frontend. One of the Angular dependencies I'm using requires me to import a javascript file into my html page, by the name of swing.js. However, when I try to do this, it sends the required file as an http request to the server, resulting in requests that look like the following:
http://localhost:3000/home/me/app/node_modules/angular-swing/dist/swing.js
Obviously, this comes up as a 404. As an alternative, I've tried changing
<script src="/home/me/app/node_modules/angular-swing/dist/swing.js"></script>
into
<script src="swing.js"></script>
and then on the server-side, doing:
app.get('swing.js', function(req, res){
res.sendFile('home/me/app/node_modules/angular-swing/dist/swing.js');
});
This works a little more, but then the file doesn't run properly, as I'm assuming it's no longer in the npm environment it needs to be in. I've tried multiple iterations of changing
<script src="/home/me/app/node_modules/angular-swing/dist/swing.js"></script>
into something that uses periods (.) to represent more relative paths, but that doesn't appear to work either. Overall, I'm very stuck, and would appreciate any insight. If it's of any use, I'm also using:
app.use(express.static(__dirname+'/public'));
Making my comments into an answer...
node.js does not serve any files by default so any script files that you need sent from your server to the client upon request need an explicit route to handle them or they need some generic route that knows how to handle all the requested script files.
swing.js in the client is not part of any "NPM environment". It's running the browser at that point, not in any NPM enviornment. It's possible that swing.js itself needs some other scripts or resources that also need routes and that's why it doesn't work after you make an explicit route for it. You can probably examine the specific errors in the console to give you a clue why it isn't working.
You may also want to read this: How to include scripts located inside the node_modules folder?

Angular Removed # tags in urls. But not working as expected

I just removed # tag from my url of angular single page app.
I did like.
$locationProvider.html5Mode(true);
And It worked fine.
My problem is when I directly enter any url to the browser it showing a 404 error. And its working fine when I traverse throughout the app through links.
Eg: www.example.com/search
www.example.com/search_result
www.example.com/project_detail?pid=19
All these url's are working fine. But when I directly enter any of the above url's into my browser it showing a 404 error.
Please any thoughts on it.
Thanks in advance.
Well i had a similar problem. The server side implementation included Spring in my case.
Routing on client side ensures that all the url changes are resolved on the client side. However, When you directly enter any such url in the browser, the browser actually goes to the server for retrieving a web page corresponding to the url.
Now in your case, since these are VIRTUAL urls, that are meaningful on the client side, the server throws 404.
You can capture page not found exception at your server side
implementation, and redirect to the default page [route] in your app.
In Spring, we do have handlers for page not found exceptions, so i
guess they'll be available for your server side implementation too.
When using the History API you are saying:
"Here is a new URL. The other JavaScript I have just run has transformed the page into the page you would have got by visiting that URL."
This requires that you write server side code that will build the page in that state for the other URLs. This isn't a trivial thing to do and will usually require a significant amount of work.
However, in exchange for that work you get robustness and performance. When one of those URLs is visited it will:
work even if the JS fails for any reason (such as a dropped network connection or a client (such as a search engine) that doesn't support JS)
load faster than loading the homepage and then transforming it with JS
You need to use rewrite rules. Angular is an single page app, so all your request should go to the same file(index.html). You could do this by creating an .htaccess.
Assuming your main page is index.html.
Something like this (not tested):
RewriteRule ^(.)*$ / [L,QSA]
L flag means that if the rule matches, don't execute the next RewriteRule.
QSA means that the URL query parameters are also passed with the rewrited url.
More info about htaccess: http://httpd.apache.org/docs/2.2/howto/htaccess.html

Why won't the client receive new versions of this script in the public folder?

In my project there is a public folder and a script inside it: public/worker.js, which contains a piece of code:
alert('foo');
I call this script using a Worker:
new Worker('worker.js');
I launch Meteor and connect to my app. foo is alerted.
If I change the public/worker.js code to anything else:
alert('bar');
The server refreshes the clients, the client refreshes the page but won't get the new code, instead using the old one (alerting foo instead of the new shiny bar). Clearing the cache then refreshing fixes the issue. CTRL+F5 does not fix this cache issue, it does not seem to work for this kind of script call (at least not on the version of Firefox I tested it with).
Why is this happening, exactly?
How can I prevent it?
You should alter the response header for the file. Maybe this gets you going: Explicit HTTP Response Headers for files in Meteor's public directory
The script is cached and the browser does not pull the new version from the server.
We need to edit the header of the requests for the files in the /workers folder, using the following code server-side (I wrapped it in a package with api.use('webapp')):
WebApp.rawConnectHandlers.use('/workers', function(req, res, next) {
res.setHeader('cache-control', 'must-revalidate');
next();
});
Using WebApp.connectHandlers did not work, the callback was never called, so I used rawConnectHandlers instead.
I am not 100% sure it is the best way to go, but it works.
I've not found exactly why, but browsers (at least Chrome) seem to treat worker scripts differently to other Javascript files with regards to caching on refresh of the page, even if the headers sent from the server are the same. Refreshing the page makes the browser check for new scripts referenced in script tags, but not those used as a worker.
The way I've fixed this is that at build time, I include a version number/build time/md5 of the file contents in the file name, so it will end up something like worker.12333.js. The advantage of this is that if each filename references a file that essentially immutable, you can set far-future expires headers... So instead of telling the browser to never cache the worker script, it can cache it forever. https://github.com/felthy/grunt-cachebuster is one such tool that does this for Javascript included via script tags, but there are probably others.
The issue with this is that there must be some mechanism to tell the Javascript the updated filename, so it knows to call new Worker('worker.12333.js');. I'm not sure if existing available tools handle that, but the way I do it is to just use the project build time in seconds as the unique key for all the files
<html build-time="12333">
...
and then access it via Javascript so it can work out the latest worker script filename. It's not perfect, but it's fairly simple. You can probably come up with other mechanisms depending on your requirements.

Categories

Resources