Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialAmber Diehl
15,402 PointsError "write after end" being thrown when calling the /username route.
I am getting the above error when trying to get a valid or invalid user name. I should mention that the page renders perfectly but when I return to the terminal, the server has crashed. Below is a sample trace and the related code.
Trace
events.js:182
throw er; // Unhandled 'error' event
^
Error: write after end
at write_ (_http_outgoing.js:620:15)
at ServerResponse.write (_http_outgoing.js:615:10)
at Object.view (/Users/amberdiehl/js_development/TreehouseNodeServer/renderer.js:32:11)
at Profile.<anonymous> (/Users/amberdiehl/js_development/TreehouseNodeServer/router.js:28:13)
at emitOne (events.js:115:13)
at Profile.emit (events.js:210:7)
at IncomingMessage.<anonymous> (/Users/amberdiehl/js_development/TreehouseNodeServer/profile.js:38:36)
at emitNone (events.js:110:20)
at IncomingMessage.emit (events.js:207:7)
at endReadableNT (_stream_readable.js:1057:12)
app.js
const http = require('http');
var router = require('./router');
const hostname = '127.0.0.1';
const port = 8000;
//1. Create web server
const server = http.createServer((request, response) => {
router.home(request, response);
router.user(request, response);
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
router.js
var Profile = require("./profile.js");
var renderer = require("./renderer.js");
var commonHeader = {"Content-Type": "text/html"} // was {"Content-Type": "text/plain"}
function homeRoute(request, response){
if (request.url === '/') {
response.writeHead(200, commonHeader);
renderer.view('search', {}, response);
}
}
function userRoute(request, response){
var userName = request.url.replace('/', '');
if (userName.length > 0) {
var values = {};
var studentProfile = new Profile(userName);
studentProfile.on("end", function(profileJSON) {
values["avatarUrl"] = profileJSON.gravatar_url;
values["username"] = profileJSON.profile_name;
values["badges"] = profileJSON.badges.length;
values["javaScriptPoints"] = profileJSON.points.JavaScript;
response.writeHead(200, commonHeader);
renderer.view('profile', values, response);
});
studentProfile.on("error", function(error){
values["errorMessage"] = error.message;
response.writeHead(200, commonHeader);
renderer.view('search', values, response);
});
}
}
module.exports.home = homeRoute;
module.exports.user = userRoute;
renderer.js
var fs = require('fs');
function mergeValues(values, content){
for (var key in values) {
content = content.replace("{{" + key + "}}", values[key]);
}
return content;
}
function view(templateName, values, response) {
// If error, render error template first and pass to content
if ('errorMessage' in values) {
var errorTemplate = fs.readFileSync('./templates/error.html', 'utf8');
errorTemplate = mergeValues(values, errorTemplate);
values["errorTemplate"] = errorTemplate;
} else {
values["errorTemplate"] = '';
}
// Get content template and merge data values
var contentTemplate = fs.readFileSync('./templates/' + templateName + '.html', 'utf8');
contentTemplate = mergeValues(values, contentTemplate);
// Get base template and merge content template
var baseTemplate = fs.readFileSync('./templates/base.html', 'utf8');
values["body"] = contentTemplate;
baseTemplate = mergeValues(values, baseTemplate);
// Write response
response.write(baseTemplate);
response.end();
}
module.exports.view = view;
Regarding the templates (the html files located in the "views" folder that I placed in a "templates" folder), I have created a base.html which is similar to but not the same as the 'header.html' in the course, the key difference being that it includes a {{body}} variable and the closing html tag.
In turn, either profile.html or search.html are being rendered into the {{body}} variable. And finally, the search.html has a {{errorTemplate}} tag that is replaced with the error div (and message) if the user profile is not found.
I can share the contents of these templates, of course, if that would be of value but since they're rendering did not include them.
Thanks in advance to any help!
1 Answer
Amber Diehl
15,402 PointsOkay, I think I solved this (though it's a hack). Hopefully, this may save someone else a lot of time.
I moved the logging of the request.url to the server before the routes were executed and saw that /favicon.ico was being executed (though not all the time, from what I can tell). That route would be executed immediately after a legit user profile request and was causing the failure.
I added this to the app.js and all is working:
if (request.url === '/') {
router.home(request, response);
} else if (request.url != 'favicon.ico') {
router.user(request, response);
}
There must be a better way; implementing something similar to Django's url.py and using regular expressions to invoke views. :)
Gabbie Metheny
33,778 PointsI had the same problem and did a similar hack!
I made my change inside the https.get()
in profile.js
(coded by Andrew, we never touch this file, although I had already gone in to make it more ES6-friendly) because the custom error message I was getting came from this file, with favicon.ico
being popped in as the username... >_<
There was an error getting the profile for favicon.ico. (Not Found)
if (res.statusCode !== 200 && username !== 'favicon.ico') {
req.abort();
profileEmitter.emit('error', new Error(`There was an error getting the profile for ${username}. (${res.statusMessage})`));
}
Glad to see I wasn't the only one having favicon problems, and glad you figured it out!
Amber Diehl
15,402 PointsAmber Diehl
15,402 PointsActually, I have more data based on using some console.log information.
It appears that a request to the server is being made before I can even finish typing in the url. If you look at the log statements below you'll see that the call to my profile succeeded and the server didn't crash. But then when I try adding a number to my name WITHOUT PRESSING ENTER, it seems to have made a request anyway and on the heels of that another one with 123 which crashes.
I am truly stumped about why this would be happening. Help!!
Below the first /amberdiehl is from the request.url that is within the "home" route. There is no "home url executed" as would be expected. The second /amberdiehl is within the userRoute and the following console message is correct. But the next bit I don't get--I never typed /amberdiehl1 and pressed enter.