-1

I'm having trouble getting my NodeJS server to properly return the auxiliary files an HTML files needs. Instead of returning them to their appropriate places within the files, it does something really screwy.

Here's the code in question:

index.js:

var http = require('http');
var url = require('url');
var fs = require('fs');
var path = require('path');


var findFile = (function(basename) {
    var results = {
        contentType: '',
        data: ''
    };

    var dataCSS = {
        dir: '../CSS/',
        contentType: 'text/css'
    };
    var dataHTML = {
        dir: '../HTML/',
        contentType: 'text/html'
    };
    var dataJS = {
        dir: '../JS/',
        contentType: 'text/javascript'
    };
    return function(basename) {
        switch (path.extname(basename)) {
            case '.css':
                fs.readFile(dataCSS.dir + basename, function(err, data) {
                    results = {
                        contentType: dataCSS.contentType,
                        data: data
                    };
                });
                break;
            case '.html':
                fs.readFile(dataHTML.dir + basename, function(err, data) {
                    results = {
                        contentType: dataHTML.contentType,
                        data: data
                    };
                });
                break;
            case '.js':
                fs.readFile(dataJS.dir + basename, function(err, data) {
                    results = {
                        contentType: dataJS.contentType,
                        data: data
                    };
                });
                break;
            default:
                fs.readFile(dataHTML.dir + 'Home.html', function(err, data) {
                    results = {
                        contentType: dataHTML.contentType,
                        data: data
                    };
                });
                break;
        }
        return results;
    };
})();

http.createServer(function(req, res) {
    var myUrl = url.parse(req.url);
    var basename = path.basename(myUrl.path);
    console.log('request url: ' + req.url);

    if (req.url !== '/') {
        console.log('File requested: ' + basename);
    } else {
        basename = "Home.html";
    }
    console.log("Basename: " + basename);
    var data = findFile(basename);
    console.log(data);
    res.writeHead(200, { 'ContentType': data.contentType });
    res.write(data.data);
    res.end();
}).listen(8080);

Home.html:

<!DOCTYPE html>
<html lang="en-US">

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
    <link rel="stylesheet" type="text/css" href="../CSS/myApp.css">
    <script src="../JS/myApp.js"></script>
    <script src="../JS/myCtrl.js"></script>
</head>

<body>    
    <h2>myApp Test</h2>
    <div class="myApp" ng-app="myApp" ng-controller="myCtrl" ng-init="quantity = 10; cost = 5" my-Directive>
        <p>Color: <input type="text" style="background-color:{{myColor}}" ng-model="myColor" value="{{myColor}}"></p>
        <p>Total in dollar (raw exp): {{quantity * cost}}</p>
        <p>Total in dollar (span): <span ng-bind="quantity * cost"></span></p>
        <p> First Name: <input type="text" ng-model="firstName"></p>
        <p> Last Name: <input type="text" ng-model="lastName"></p>
        <h1>Hello, {{firstName + " " + lastName}}</h1>
    </div>
    Are you even changing?!
</body>
</html>

A current run of my code does this:

  1. I use "node index.js" in the console to start the server.
  2. I navigate to "localhost:8080" in my browser for the first time, it shows nothing; there's an error in the dev window that says that I did not format the headers properly. This isn't true since I set it in the findFile method on each hit to the server. A console readout first looks like this:

enter image description here

The dev window in Firefox looks like this:

Dev window look of "loaded auxiliary files

  1. The next refresh loads the HTML page, but without any influence from the auxiliary files, even though the logging shows everything has been requested:

enter image description here

enter image description here console log at this point:

enter image description here

  1. Subsequent refreshes turns the page into a roulette with each of the separate files representing a number on the wheel. Instead of the HTML page, one of the others might display instead, with the looks of a text file. I'll spare the pictures; I feel like I've done too many as it is.

What am I doing wrong here? Is there a different way I should be doing this? Should I even have to do this at all? Please send help.

Thank you.

Skello
  • 333
  • 2
  • 15
  • One might first ask why are you using a bare bones http server rather than a simple, non-opinionated framework like Express that does a lot of the dirty work for you and makes your code a lot simpler and a lot less susceptible to these type of errors. – jfriend00 Apr 26 '18 at 01:49
  • 2
    I see you also have asynchronous issues. You're using `fs.readFile()` which is asynchronous, but expecting to return it's result from your function which will NEVER work. See [How do I return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call/14220323#14220323) for reference. – jfriend00 Apr 26 '18 at 01:50
  • Don't you think, you should group all the static js and CSS files in one folder and serve them directly without reading file and then sending response to client? – Nitishkumar Singh Apr 26 '18 at 01:53
  • @NitishkumarSingh - You can't do that. The browser requests these resources one at a time. – jfriend00 Apr 26 '18 at 01:53
  • Sorry my words were confusing, I wanted to say that rather than reading CSS and js he can serve them directly too. – Nitishkumar Singh Apr 26 '18 at 01:59
  • Did I mention I was brand-spanking new, and was nodeJS from w3schools? – Skello Apr 26 '18 at 02:21

1 Answers1

0

fs.readFile is asynchronous, meaning the callback you give to it doesn't happen immediately, it happens sometime later. Particularly, it happens after the findFile function returns the value. See this related question for more details.

In order for findFile to work correctly, it needs to receive a callback to call whenever any of the fs.readFile operations are finished. Also look into promises and async/await as a cleaner alternative to using callbacks.

function findFile(basename, done) {
  switch (path.extname(basename)) {
    case ".css":
      fs.readFile(dataCSS.dir + basename, function(err, data) {
        var results = {
          contentType: dataCSS.contentType,
          data: data,
        }

        // this function is the `function(results)` we call with down there
        done(results)
      })
      break

    case ".html":
    // same as above

    case ".js":
    // same as above

    default:
    // same as above
  }
}

function requestHandler(req, res) {
  // same as before

  findFile(basename, function(results) {
    console.log(results)
    res.writeHead(200, { ContentType: results.contentType })
    res.write(results.data)
    res.end()
  })
}

http.createServer(requestHandler).listen(8080)
kingdaro
  • 11,528
  • 3
  • 34
  • 38