Serve una pagina web che visualizza le informazioni sulla richiesta HTTP inviate dal client
Per imparare a programmare un server web in Node e JavaScript, ho deciso di implementare un semplice server web che fa solo una cosa:
- Visualizza i dati della richiesta HTTP come una tabella HTML.
I dati effettivi da visualizzare sono arbitrari. In questo esempio sto visualizzando il contenuto dell'intestazione, insieme al metodo, all'URL e alle versioni http.
Ecco la soluzione funzionante:
const http = require('http');
const port = 3344;
const htmlCSS = `
<style>
body {
font-family: sans-serif;
}
td {
border: 1px solid black;
padding: 0.5rem;
}
table {
margin: 0 auto;
}
</style>
`;
const server = http.createServer((req, res) => {
const {
headers,
method,
url
} = req;
console.log(`\n${headers['user-agent']}\n${method}\n${url}`);
console.log(new Date());
console.log();
writeResponse(req, res);
});
server.listen(port);
function writeResponse(req, res) {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');
writeBody(req, res);
res.end();
}
function writeBody(req, res) {
const htmlTitle = '<title>Your Request Is</title>';
const htmlEncode = '<meta charset="UTF-8">';
const htmlHead = `<head>${htmlTitle}${htmlCSS}${htmlEncode}</head>`;
const htmlTop = `<html lang="en">${htmlHead}<body>`;
const htmlBot = '</body></html>';
res.write(htmlTop);
writeTable(req, res);
res.write(htmlBot);
}
function writeTable(req, res) {
const htmlTableTop = '<table><tbody>';
const htmlTableBot = '</tbody></table>';
res.write(htmlTableTop);
writeAllRows(req, res);
res.write(htmlTableBot);
}
function writeAllRows(req, res) {
const {
httpVersion,
httpVersionMinor,
httpVersionMajor,
headers,
method,
url
} = req;
for (const key in headers) {
writeRow(res, key, headers[key]);
}
writeRow(res, 'method', method);
writeRow(res, 'url', url);
writeRow(res, 'httpVersion', httpVersion);
writeRow(res, 'httpVersionMinor', httpVersionMinor);
writeRow(res, 'httpVersionMajor', httpVersionMajor);
}
function writeRow(res, key, val) {
const entry = `<tr><td>${key}</td><td>${val}</td></tr>`;
res.write(entry);
}
Mentre ho alcune domande particolari, non vedo l'ora di ricevere tutti i tipi di feedback! Quindi, se vedi qualcosa che può essere migliorato, qualsiasi commento è molto apprezzato!
- Posso utilizzare altri moduli per facilitare questo lavoro?
- Per un lavoro così semplice, vale la pena lavorare con HTML e CSS come file separati?
- Come vengono normalmente gestiti HTML e CSS all'interno dei file JS (modifiche, aggiunte, ecc.)?
- Quanto bene sto gestendo la costruzione di HTML e CSS?
- È possibile farlo in modo più conciso?
- Cosa faresti diversamente per migliorarlo in qualche modo?
Risposte
Posso utilizzare altri moduli per facilitare questo lavoro?
Puoi utilizzare alcuni framework web come fastify o express . Ciò faciliterebbe molto il codice
Per un lavoro così semplice, vale la pena lavorare con HTML e CSS come file separati?
Supponendo che il tuo progetto crescerà, è un buon approccio spostare del "testo statico" in un file separato.
Come vengono normalmente gestiti HTML e CSS all'interno dei file JS (modifiche, aggiunte, ecc.)?
Hai realizzato un rendering lato server (SSR) con le tue mani. Dal momento che sarebbe difficile gestire manualmente tutti quei pezzi di stringhe e sarebbe anche pericoloso per qualche iniezione di script, potresti usare alcuni fremework come vue o altri sistemi di template come manubriojs o altri - qui un elenco
Quanto bene sto gestendo la costruzione di HTML e CSS?
Potrebbero esserci dei problemi di sicurezza per collegamenti del genere se inizi a elaborare questi valori:
http://localhost:3000/?user=%3Cimg%20src=%27aaa%27%20onerror=alert(1)%3E
il framework ti protegge da questi casi di solito.
Questo codice:
const htmlTitle = '<title>Your Request Is</title>'
const htmlEncode = '<meta charset="UTF-8">'
const htmlHead = `<head>${htmlTitle}${htmlCSS}${htmlEncode}</head>`
const htmlTop = `<html lang="en">${htmlHead}<body>`
produrre ogni volta la stessa stringa di output, quindi potresti farlo una volta invece di ogni richiesta: questo stresserà meno il Garbage Collector di nodejs per il sito ad alto throughput.
È possibile farlo in modo più conciso?
Potresti scrivere meno codice usando alcune Object
e Array
funzioni:
function stringTemplates (request) {
return `<html lang="en">
<head>
<title>Your Request Is</title>
</head>
<body>
<table><tbody>
${Object.entries(request.headers).map(processLine).join('')}
${['httpVersion', 'httpVersionMinor', 'httpVersionMajor', 'method', 'url'].map(prop => processLine([prop, request[prop]])).join('')}
</tbody></table>
</body>
</html>
`
}
function processLine ([header, value]) {
return `<tr><td>${header}</td><td>${value}</td></tr>`
}
Cosa faresti diversamente per migliorarlo in qualche modo?
Questo mostra solo GET, ma per imparare lo standard HTTP sarebbe utile mostrare in:
- parametri di interrogazione
- parametro form (con codifica diversa)
Cosa faresti diversamente per migliorarlo in qualche modo?
Non sono sicuro di quanto sia costoso, ma mirerei a ridurre al minimo il numero di volte in cui res.write()
viene chiamato. Per questo avrei le funzioni per generare righe restituire una stringa e raccogliere le stringhe per ogni riga in un array, quindi unire l'array usando Array.prototype.join()per passare a una singola chiamata a res.write()
.
Il codice fa buon uso di const
per mantenere limitato l'ambito delle variabili ed evitare riassegnazioni accidentali. Può anche essere utilizzato per valori costanti che non dovrebbero mai cambiare durante il runtime.
Le costanti possono essere dichiarate con lettere maiuscole o minuscole, ma una convenzione comune consiste nell'usare lettere tutte maiuscole. 1
Nomi come htmlCSS
potrebbe essere HTML_CSS
, port
potrebbe essere PORT
. In questo modo chiunque legga il codice saprà che quei valori non dovrebbero essere modificati. Tieni presente che qualsiasi oggetto/array dichiarato con const
non è immutabile a meno che non sia racchiuso in una chiamata a Object.freeze().