Kuinka luoda verkkopalvelin Node.js:ssä HTTP-moduulin avulla
Johdanto
Kun katsot verkkosivua selaimessasi, teet pyynnön toiselle tietokoneelle internetissä, joka sitten palauttaa sinulle verkkosivun vastauksena. Se tietokone, johon olet yhteydessä internetin kautta, on web-palvelin. Web-palvelin vastaanottaa HTTP-pyyntöjä asiakkaalta, kuten selaimestasi, ja tarjoaa HTTP-vastauksen, kuten HTML-sivun tai JSONin API:sta.
Node.js:n http-moduuli tarjoaa kaiken tarvitsemasi verkkopalvelimen rakentamiseen ilman ulkoisia riippuvuuksia. Se käsittelee saapuvat pyynnöt, jäsentää HTTP-otsikot, hallitsee yhteyksiä ja lähettää vastauksia—kaiken yksinkertaisten JavaScript-rajapintojen kautta.
http-moduuli on Node.js:n sisäänrakennettu ratkaisu HTTP-palvelimien ja -asiakkaiden luomiseen. Toisin kuin Express.js:n kaltaiset kehykset, se on osa Node.js:n ydintä, mikä tarkoittaa, ettei asennusta tai riippuvuuksia tarvita. Tämä tekee siitä ihanteellisen kevyeen sovelluskehitykseen, HTTP:n perusteiden opetteluun tai tilanteisiin, joissa halutaan täysi hallinta pyyntöjen käsittelyssä.
Paljon ohjelmistoja on mukana, jotta palvelin voi palauttaa verkkosivun. Nämä ohjelmistot jaetaan yleensä kahteen luokkaan: frontend- ja backend-ohjelmistoihin. Frontend-koodi käsittelee sisällön esitystapaa, kuten navigointipalkin väriä ja tekstin tyyliä. Backend-koodi käsittelee sitä, miten dataa vaihdetaan, käsitellään ja tallennetaan. Koodi, joka käsittelee selaimesi verkkopyyntöjä tai kommunikoi tietokannan kanssa, hallitaan pääasiassa backend-koodin avulla.
Node.js mahdollistaa kehittäjille JavaScriptin käytön back-end-koodin kirjoittamiseen, vaikka perinteisesti sitä on käytetty selaimessa front-end-koodin kirjoittamiseen. Frontendin ja backendin yhdistäminen näin vähentää verkkopalvelimen tekemiseen tarvittavaa vaivaa, mikä on tärkeä syy siihen, miksi Node.js on suosittu valinta back-end-koodin kirjoittamiseen.
Tämä opetusohjelma näyttää, kuinka rakennetaan tuotantovalmiita verkkopalvelimia Node.js:iin sisältyvän http-moduulin avulla. Rakennat palvelimia, jotka käsittelevät reititystä, jäsentävät pyyntöjen sisältöä, tarjoavat staattisia tiedostoja, toteuttavat asianmukaisen virheenkäsittelyn ja toimivat sekä CommonJS- että ES-moduulisyntaksin kanssa.
Yksinkertaista Node.js-sovellusten käyttöönottaminen Linux-Console.net-sovellusalustalla. Ota Node käyttöön suoraan GitHubista minuuteissa.
Keskeiset kohdat
Ennen kuin sukellat mukaan, tässä on mitä saavutat:
Luo HTTP-palvelimia Node.js:n ydintä
http-moduulia käyttäen ilman kehyksiä:
Opit, kuinka voit pystyttää täysin toimivan HTTP-verkkopalvelimen käyttämällä pelkästään Node.js:n ydintähttp-moduulia. Tämä tarkoittaa ulkopuolisten riippuvuuksien tai kehysten välttämistä, mikä antaa sinulle syvemmän ymmärryksen siitä, miten palvelimet todella toimivat matalalla tasolla, sekä mahdollistaa kevyemmät ja joustavammat toteutukset omiin käyttötarkoituksiisi.Käsittele erilaisia sisältötyyppejä (JSON, HTML, CSV, staattiset tiedostot) asianmukaisilla otsikoilla:
Opit työskentelemään useiden sisältötyyppien kanssa, vastaamaan HTML:llä verkkosivuille, JSON:lla API:ille, CSV-tiedostoilla tai tarjoamaan staattisia resursseja, kuten kuvia ja skriptejä. Ymmärrät, miten asettaa HTTP-otsikot, kutenContent-Type, oikein varmistaen, että selaimet ja asiakkaat tulkitsevat vastauksesi oikein ja tarjoavat saumattoman käyttökokemuksen.Toteuta reititys käyttämällä pyyntö-URL:n jäsentämistä ja switch-lauseita:
Reititys on olennainen osa web-palvelimia, sillä sen avulla voit tarjota eri logiikkaa eri URL-osoitteille ja päätepisteille. Näet tekniikoita saapuvien pyyntöjen URL-osoitteiden jäsentämiseen ja palvelinlogiikan rakenteistamiseen (esimerkiksi switch-lauseilla) tarjotaksesi mukautettuja vastauksia polkujen ja HTTP-menetelmien perusteella, simuloiden perustasolla sitä, mitä suuremmat kehykset tekevät taustalla.Käsittele POST-pyyntöjä jäsentämällä pyyntörunkoja ja käsittelemällä erilaisia sisältötyyppejä:
Käyttäjien lähettämien tietojen käsittely on avainasemassa kaikissa interaktiivisissa sovelluksissa. Opit käsittelemään POST-pyyntöjä, mukaan lukien pyyntörungon lukemisen ja jäsentämisen raakadatana, JSONina tai muina formaateina. Tämä osio käsittelee saapuvan datan puskurointia, turvallista jäsentämistä ja vastaanotetun kuorman vastaamista, tarjoten rakennuspalikat käyttäjävuorovaikutukselle tai API-kehitykselle.Palvele staattisia tiedostoja tehokkaasti tiedostojärjestelmästä:
Monet verkkopalvelimet tarvitsevat staattisten resurssien—kuvien, tyylitiedostojen, JavaScript-tiedostojen—suoraa tarjoamista käyttäjille. Opit, kuinka lukea tiedostoja levyltä ja lähettää ne tehokkaasti HTTP:n kautta, hallita tiedostopolkuja vastuullisesti turvallisuusongelmien välttämiseksi sekä tutkia optimointeja, kuten tiedostovirtausta, suorituskyvyn parantamiseksi suuremmille tiedostoille.Toteuta virheenkäsittely asianmukaisilla HTTP-tilakoodeilla ja lokituksella:
Vankka palvelin käsittelee virheet sujuvasti. Näet, kuinka virheet—kuten virheelliset pyynnöt tai puuttuvat tiedostot—havaitaan, kirjataan ja niihin vastataan käyttäen standardoituja HTTP-tilakoodeja, kuten 400, 404 ja 500. Opit myös peruslokituksen käytännöt, jotta ongelmat voidaan jäljittää ja käyttäjät saavat informatiivisia virheilmoituksia ilman, että arkaluontoista tietoa paljastuu.-
Käytä modernia JavaScriptiä sekä CommonJS- (
require) että ES Modules (import) -syntaksilla:
Node.js tukee kahta moduulijärjestelmää: CommonJS:ää ja ES Moduleja. Harjoittelet sekärequire()- ettäimport-lauseiden käyttöä ja opit, milloin ja miten kumpaakin tyyliä käytetään. Tämä ei ainoastaan takaa yhteensopivuutta eri Node.js-projektien kanssa, vaan auttaa myös kirjoittamaan modernia, selkeää ja ylläpidettävää JavaScript-koodia. Käytä tuotannon parhaimpia käytäntöjä, mukaan lukien tiedostovälimuisti, virheenkorjaus ja turvallisuus:
Lopuksi käsittelet olennaisia parhaita käytäntöjä Node.js-verkkopalvelimien käyttöönotossa todellisissa ympäristöissä. Tämä sisältää tiedostovälimuistin hyödyntämisen suorituskyvyn parantamiseksi, virheresilienssistrategioiden toteuttamisen palvelimen kestävyyden varmistamiseksi sekä perus turvallisuustoimien (kuten syötteen puhdistamisen ja asianmukaisten HTTP-otsikoiden käytön) soveltamisen sovellusten ja käyttäjien suojaamiseksi.
HTTP-moduulin ymmärtäminen
http-moduuli on Node.js:n matalan tason rajapinta HTTP-viestintään. Se tarjoaa:
- Palvelimen luominen:
http.createServer()luo HTTP-palvelininstanssin - Pyyntöjen käsittely: Saapuvat pyynnöt esitetään
IncomingMessage-olioina - Vastausten hallinta: Lähtevät vastaukset käyttävät
ServerResponse-olioita - Suoratoistotuki: Sekä pyynnöt että vastaukset ovat suoratoistoja, mikä mahdollistaa tehokkaan tiedonsiirron
Miksi käyttää HTTP-moduulia Expressin sijaan? Vaikka Express.js tarjoaa käteviä abstraktioita, natiivi http-moduuli antaa sinulle täydellisen hallinnan, pienemmät pakettikoot ja syvällisen ymmärryksen HTTP:n perusteista. Se on perusta, jonka päälle kehykset rakentuvat. Yksinkertaisille API:lle, mikropalveluille tai oppimistarkoituksiin se on usein kaikki, mitä tarvitset.
Moduuli on tapahtumapohjainen: kun pyyntö saapuu, palvelin lähettää 'request'-tapahtuman ja kutsuu pyyntökuuntelijafunktiotasi. Tämä suunnittelu mahdollistaa Node.js:n käsitellä monia samanaikaisia yhteyksiä tehokkaasti yhdellä säikeellä tapahtumasilmukan kautta.
Keskeiset käsitteet, jotka on ymmärrettävä:
- Ei-estävä I/O:
http-moduuli käyttää Node.js:n tapahtumasilmukkaa käsitelläkseen useita yhteyksiä luomatta erillisiä säikeitä jokaiselle pyynnölle. Tämä tekee siitä erittäin tehokkaan I/O-sidonnaisissa operaatioissa. - Virtaukset: Pyyntö- ja vastausobjektit periytyvät Node.js-virroista. Tämä tarkoittaa, että suuria tietomääriä voidaan käsitellä paloina ilman, että kaikkea ladataan muistiin – mikä on ratkaisevan tärkeää tiedostojen latausten käsittelyssä tai suurten tiedostojen tarjoamisessa.
- Otsikot ja tilakoodit: Moduuli antaa suoran hallinnan HTTP-otsikoihin (
res.setHeader()) ja tilakoodeihin (res.writeHead()), mahdollistaen tarkan HTTP-semanttiikan. - Ei maagista reititystä: Toisin kuin kehyksissä, mukana ei ole sisäänrakennettua reititystä. Sinun täytyy itse käsitellä
req.urljareq.methodmäärittääksesi, miten vastaat—mikä antaa täydellisen läpinäkyvyyden siihen, miten reititys todellisuudessa toimii.
Kun kutsut http.createServer(), Node.js luo taustalla TCP-palvelimen, joka kuuntelee saapuvia yhteyksiä, jäsentää HTTP-protokollaviestejä ja lähettää tapahtumia, joita koodisi voi käsitellä. Tämän matalan tason toiminnan ymmärtäminen auttaa sinua virheiden selvittämisessä ja tekee mahdolliseksi tietoon perustuvien arkkitehtuurivalintojen tekemisen.
Edellytykset
Varmista, että Node.js on asennettu kehityskoneellesi. Tämä opas toimii Node.js 20.x tai 22.x LTS -versioiden kanssa (suositeltu). Node.js:n asentamiseksi macOS:lle tai Ubuntulle, seuraa ohjeita kohdissa Kuinka asentaa Node.js ja luoda paikallinen kehitysympäristö macOS:llä tai Kuinka asentaa Node.js Ubuntulle.
Node.js-alusta tukee web-palvelimien luomista suoraan käyttövalmiina. Perustietojen hallinta Node.js:stä on hyödyllistä. Tutustu oppaaseemme siitä, kuinka kirjoittaa ja suorittaa ensimmäinen ohjelmasi Node.js:ssä.
Asynkronista ohjelmointia käytetään joissakin osioissa. Jos olet uusi asynkronisen koodin tai
fs-moduulin kanssa, katso Kuinka kirjoittaa asynkronista koodia Node.js:ssä.-
Tuotantoon keskittyvillä osioilla Linux-prosessinhallinnan (systemd/PM2) ja käänteisten välityspalvelimien (kuten Nginx) tuntemus auttaa soveltaessa käyttöönotto-ohjeita.
Vaihe 1 — Perus HTTP-palvelimen luominen
Rakenna minimaalinen HTTP-palvelin, joka vastaa kaikkiin pyyntöihin pelkällä tekstillä. Tämä perusta pätee riippumatta siitä, käytätkö Expressiä vai rakennat palvelimen alusta alkaen. Kun Node.js kutsuu http.createServer(), se luo TCP-palvelimen, joka kuuntelee porttia. Jokainen saapuva yhteys laukaisee pyyntökuuntelijafunktiosi.
Luo projektikansiosi:
mkdir first-servers
cd first-serversLuo nyt tiedosto, johon koodi sijoitetaan:
touch hello.jsAvaa tiedosto tekstieditorissa. Käytämme nano-editoria, koska se on saatavilla terminaalissa:
nano hello.jsAloitamme lataamalla http-moduulin, joka on vakiona kaikissa Node.js-asennuksissa. Lisää seuraava rivi tiedostoon hello.js:
const http = require("http");http-moduuli sisältää funktion palvelimen luomiseen, jonka näemme myöhemmin. Jos haluat oppia lisää Node.js-moduuleista, tutustu artikkeliimme Kuinka luoda Node.js-moduuli.
Seuraava askeleemme on määritellä kaksi vakioa, isäntä ja portti, johon palvelimemme sidotaan:
...
const host = 'localhost';
const port = 8000;Kuten aiemmin mainittiin, verkkopalvelimet hyväksyvät pyyntöjä selaimilta ja muilta asiakkailta. Voimme olla vuorovaikutuksessa verkkopalvelimen kanssa syöttämällä verkkotunnuksen, joka DNS-palvelin muuntaa IP-osoitteeksi. IP-osoite on yksilöllinen numerosarja, joka tunnistaa koneen verkossa, kuten internetissä. Lisätietoja verkkotunnusten käsitteistä saat lukemalla artikkelimme Johdatus DNS-termeihin, komponentteihin ja käsitteisiin.
Arvo localhost on erityinen yksityinen osoite, jota tietokoneet käyttävät viitatessaan itseensä. Se vastaa yleensä sisäistä IP-osoitetta 127.0.0.1 ja on käytettävissä vain paikallisella tietokoneella, ei millekään liittymillemme paikallisverkoille tai internetissä.
Portti on numero, jota palvelimet käyttävät päätepisteenä tai "ovena" IP-osoitteeseemme. Esimerkissä käytämme web-palvelimellemme porttia 8000. Portteja 8080 ja 8000 käytetään tyypillisesti oletusportteina kehityksessä, ja useimmissa tapauksissa kehittäjät käyttävät niitä mieluummin kuin muita portteja HTTP-palvelimille.
Kun sitomme palvelimemme tähän isäntään ja porttiin, voimme käyttää palvelintamme avaamalla http://localhost:8000 paikallisessa selaimessa.
Lisätään erityinen funktio, jota Node.js:ssä kutsumme pyyntölähettimeksi. Tämä funktio on tarkoitettu käsittelemään saapuvaa HTTP-pyyntöä ja palauttamaan HTTP-vastaus. Tällä funktiolla on oltava kaksi argumenttia: pyyntöobjekti ja vastausobjekti. Pyyntöobjekti tallentaa kaikki saapuvan HTTP-pyynnön tiedot. Vastausobjektia käytetään HTTP-vastausten palauttamiseen palvelimelle.
Haluamme, että ensimmäinen palvelimemme palauttaa tämän viestin aina, kun joku käyttää sitä: "Ensimmäinen palvelimeni!".
Lisätään seuraavaksi tuo toiminto:
...
const requestListener = function (req, res) {
res.writeHead(200);
res.end("My first server!");
};Funktiolle annettaisiin yleensä nimi sen mukaan, mitä se tekee. Esimerkiksi jos loisimme pyynnön kuuntelijafunktion, joka palauttaa kirjalistan, nimittäisimme sen todennäköisesti listBooks(). Koska tämä on esimerkkitapaus, käytämme yleisnimeä requestListener.
Kaikki pyyntökuuntelijafunktiot Node.js:ssä hyväksyvät kaksi argumenttia: req ja res (voimme nimetä ne eri tavalla, jos haluamme). Käyttäjän lähettämä HTTP-pyyntö tallennetaan Request-objektiin, joka vastaa ensimmäistä argumenttia, req. HTTP-vastaus, jonka palautamme käyttäjälle, muodostetaan olevien toimenpiteiden kautta toisen argumentin, res, Response-objektin kanssa.
Ensimmäinen rivi res.writeHead(200); asettaa vastauksen HTTP-tilakoodin. HTTP-tilakoodit ilmaisevat, kuinka hyvin HTTP-pyyntö on käsitelty palvelimen toimesta. Tässä tapauksessa tilakoodi 200 vastaa "OK"-tilaa. Jos olet kiinnostunut oppimaan erilaisista HTTP-koodeista, joita verkkopalvelimesi voi palauttaa ja niiden merkityksistä, oppaamme How To Troubleshoot Common HTTP Error Codes on hyvä paikka aloittaa.
Seuraava funktiorivi, res.end("My first server!");, kirjoittaa HTTP-vastauksen takaisin sitä pyytäneelle asiakkaalle. Tämä funktio palauttaa kaiken datan, jonka palvelimen on tarkoitus palauttaa. Tässä tapauksessa se palauttaa tekstidataa.
Lopulta voimme nyt luoda palvelimemme ja käyttää pyyntönkuuntelijaamme:
...
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});Tallenna ja poistu nano-editorista painamalla CTRL+X.
Ensimmäisellä rivillä luomme uuden server-olion http-moduulin createServer()-funktion avulla. Tämä palvelin vastaanottaa HTTP-pyyntöjä ja välittää ne requestListener()-funktiollemme.
Sen jälkeen, kun olemme luoneet palvelimemme, meidän on sidottava se verkkosoitteeseen. Teemme sen käyttämällä server.listen()-metodia. Se hyväksyy kolme argumenttia: port, host ja callback-funktion, joka suoritetaan, kun palvelin alkaa kuunnella.
Kaikki nämä argumentit ovat valinnaisia, mutta on hyvä idea ilmoittaa selkeästi, mitä porttia ja isäntää haluamme web-palvelimen käyttävän. Kun otetaan käyttöön web-palvelimia eri ympäristöissä, on tiedettävä, millä portilla ja isännällä ne toimivat, jotta voidaan määrittää kuormantasapainotus tai DNS-aliaskirjaus.
Takaisinkutsufunktio kirjauttaa viestin konsoliimme, jotta voimme tietää, milloin palvelin alkoi kuunnella yhteyksiä.
Tässä on täydellinen hello.js-tiedosto sisäisillä kommenteilla, jotka selittävät jokaisen osan:
// Import the built-in http module - no installation needed
const http = require("http");
// Define where the server will listen
const host = 'localhost'; // Only accessible from this machine
const port = 8000; // Common development port
// Request listener: handles every incoming HTTP request
const requestListener = function (req, res) {
// Set HTTP status code to 200 (OK)
res.writeHead(200);
// Send the response body and close the connection
res.end("My first server!");
};
// Create the server with our request listener
const server = http.createServer(requestListener);
// Start listening for connections
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});Tämä koodi luo toimivan HTTP-palvelimen alle 20 rivissä. Joka kerta, kun asiakas muodostaa yhteyden—riippumatta URL-osoitteesta tai HTTP-metodista—palvelin vastaa tekstillä "Ensimmäinen palvelimeni!" ja 200-tilakoodilla.
Huomautus: Vaikka requestListener() ei käytäkään req-objektia, sen on silti oltava funktion ensimmäinen argumentti.
Alle viisitoista koodiriviä riittää nyt verkkopalvelimen luomiseen. Katsotaanpa sitä toiminnassa ja testataan ohjelma päästä päähän suorittamalla se:
node hello.jsKonsolissa näemme tämän tulosteen:
Server is running on http://localhost:8000Huomaa, että kehotus häviää. Tämä johtuu siitä, että Node.js-palvelin on pitkäkestoinen prosessi. Se sulkeutuu vain, jos se kohtaa virheen, joka aiheuttaa sen kaatumisen ja lopettamisen, tai jos lopetamme Node.js-prosessin, joka pyörittää palvelinta.
Avaa erillinen terminaali-ikkuna ja kommunikoimme palvelimen kanssa käyttämällä cURLia, komentorivityökalua tietojen lähettämiseen ja vastaanottamiseen verkosta. Syötä komento tehdäksemme HTTP GET -pyynnön käynnissä olevalle palvelimellemme:
curl http://localhost:8000Kun painamme ENTER, päätelaitteemme näyttää seuraavan tulosteen:
My first server!Olemme nyt perustaneet palvelimen ja saaneet ensimmäisen palvelinvastauksen.
Puretaanpa, mitä tapahtui, kun testasimme palvelinta. Käyttäen cURLia, lähetimme GET-pyynnön palvelimelle osoitteessa http://localhost:8000. Node.js-palvelimemme kuunteli yhteyksiä tuosta osoitteesta. Palvelin välitti pyynnön requestListener()-funktiolle. Funktio palautti tekstidataa tilakoodilla 200. Palvelin lähetti sitten vastauksen takaisin cURL:lle, joka näytti viestin terminaalissamme.
Tuotannossa tyypillisesti et sido palvelinta localhost-osoitteeseen—sen sijaan sidot sen 0.0.0.0-osoitteeseen, jotta se hyväksyy yhteydet mistä tahansa verkkoliitännästä. Kuitenkin paikallisessa kehityksessä localhost on turvallisempi ja estää ulkoisen pääsyn kehityspalvelimeesi. Lisäksi, jos portti 8000 on jo käytössä, saat EADDRINUSE-virheen—valitse yksinkertaisesti toinen portti tai lopeta prosessi, joka käyttää kyseistä porttia komennolla lsof -i :8000 (macOS/Linux).
Ennen kuin jatkamme, lopetetaan käynnissä oleva palvelimemme painamalla CTRL+C. Tämä keskeyttää palvelimen suorituksen ja vie meidät takaisin komentorivikehotteeseen.
Useimmilla verkkosivustoilla, joita vierailemme, tai käyttämissämme API-rajapinnoissa palvelimen vastaukset eivät yleensä ole pelkkää tekstiä. Yleisiä vastausformaatteja ovat HTML-sivut ja JSON-data. Seuraavassa vaiheessa opimme, miten palautetaan HTTP-vastauksia yleisimmissä verkkosivuilta kohtaamissamme dataformaatteissa.
Jos haluat oppia tekemään HTTP-pyyntöjä Node.js:stä (asiakaspäässä), tutustu oppaaseemme siitä, kuinka luoda HTTP-asiakas Core HTTP:llä Node.js:ssä.
Vaihe 2 — Eri sisältötyyppien palauttaminen
Vastaus, jonka palautamme verkkopalvelimelta, voi olla monessa eri muodossa. JSON ja HTML mainittiin aiemmin, ja voimme myös palauttaa muita tekstimuotoja, kuten XML ja CSV. Lopuksi verkkopalvelimet voivat palauttaa myös ei-tekstimuotoista dataa, kuten PDF-tiedostoja, pakattuja tiedostoja, ääntä ja videota.
Tässä artikkelissa opit, kuinka voit palauttaa seuraavat tietotyypit tavallisen tekstin lisäksi, jonka juuri palautimme:
- JSON
- CSV
- HTML
Kolme tietotyyppiä ovat kaikki tekstipohjaisia, ja ne ovat suosittuja muotoja sisällön toimittamiseen verkossa. Monet palvelinpuolen kehityskielet ja -työkalut tukevat näiden eri tietotyyppien palauttamista. Node.js:n yhteydessä meidän täytyy tehdä kaksi asiaa:
- Aseta HTTP-vastauksissamme
Content-Type-otsake asianmukaisella arvolla. - Varmista, että
res.end()saa tiedot oikeassa muodossa.
Katsotaanpa tätä toiminnassa muutamien esimerkkien avulla. Tässä ja myöhemmissä osioissa kirjoittamamme koodi on monella tapaa samanlaista kuin aiemmin kirjoittamamme koodi. Suurin osa muutoksista on requestListener()-funktiossa. Luodaan tiedostot tällä "pohjakoodilla", jotta myöhemmät osiot olisi helpompi seurata.
Luo uusi tiedosto nimeltä html.js. Tätä tiedostoa käytetään myöhemmin HTML-tekstin palauttamiseen HTTP-vastauksessa. Laitamme tänne mallikoodin ja kopioimme sen muihin palvelimiin, jotka palauttavat erilaisia tyyppejä.
Kirjoita terminaaliin seuraava:
touch html.jsAvaa nyt tämä tiedosto tekstieditorissa:
nano html.jsKopioidaan "mallikoodi." Syötä tämä nano-ohjelmaan:
const http = require("http");
const host = 'localhost';
const port = 8000;
const requestListener = function (req, res) {};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});Tallenna ja poistu html.js-tiedostosta CTRL+X-näppäinyhdistelmällä, ja palaa sitten terminaaliin.
Kopioidaan nyt tämä tiedosto kahteen uuteen tiedostoon. Ensimmäinen tiedosto palauttaa CSV-tietoja HTTP-vastauksessa:
cp html.js csv.jsToinen tiedosto palauttaa JSON-vastauksen palvelimella:
cp html.js json.jsJäljellä olevat tiedostot ovat myöhempiä harjoituksia varten:
cp html.js htmlFile.js
cp html.js routes.jsOlemme nyt valmiita jatkamaan harjoituksiamme. Aloitetaan JSONin palauttamisesta.
Palvelimen JSON
JavaScript Object Notation, yleisesti tunnettu nimellä JSON, on tekstipohjainen tiedonvaihtoformaatti. Kuten nimestä voi päätellä, se on johdettu JavaScript-oliosta, mutta se on kielestä riippumaton, mikä tarkoittaa, että sitä voi käyttää mikä tahansa ohjelmointikieli, joka osaa jäsentää sen syntaksin.
JSONia käyttävät yleisesti API:t tietojen vastaanottamiseen ja palauttamiseen. Sen suosio johtuu siitä, että sen tietojen siirron koko on pienempi kuin aiempien tietojenvaihtostandardien, kuten XML:n, sekä saatavilla olevista työkaluista, joiden avulla ohjelmat voivat jäsentää JSONia ilman liiallista vaivaa. Jos haluat oppia lisää JSONista, voit lukea oppaamme JavaScriptin JSONin kanssa työskentelystä.
Avaa json.js-tiedosto nano-editorilla:
nano json.jsKun lähetät JSONia, sinun on asetettava Content-Type-otsikko arvoon application/json. Ilman sitä selaimet ja API-asiakkaat käsittelevät vastauksen tavallisena tekstinä, mikä aiheuttaa fetch().json()-kutsun epäonnistumisen. Aseta otsikot ennen writeHead()-kutsua — kun otsikot on lähetetty, niitä ei voi muuttaa.
Yleinen virhe: res.writeHead(200)-kutsuminen ennen res.setHeader()-kutsua lukitsee otsikot. Jos yrität asettaa otsikoita writeHead()-kutsun jälkeen, niitä ei oteta huomioon. Kutsu aina ensin setHeader(), sitten writeHead().
Muokkaa requestListener()-funktiota:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "application/json");
res.writeHead(200);
res.end(`{"message": "This is a JSON response"}`);
};
...Tilauskoodi 200 ilmaisee onnistumisen. Vastausrunko sisältää kelvollisen JSONin.
Miksi käyttää JSON.stringify()-metodia manuaalisten merkkijonojen sijaan? JSONin kirjoittaminen käsin on altis virheille. Jos viestissäsi on lainausmerkkejä, rivinvaihtoja tai erikoismerkkejä, ne täytyy paeta käsin. JSON.stringify() hoitaa tämän automaattisesti:
const data = {
message: "This is a JSON response",
timestamp: Date.now(),
note: 'He said "hello"' // Quotes automatically escaped
};
res.setHeader("Content-Type", "application/json");
res.writeHead(200);
res.end(JSON.stringify(data));Varo ympyräviittauksia: Jos objektisi viittaa itseensä, JSON.stringify() heittää: TypeError: Converting circular structure to JSON. Tämä tapahtuu usein tietokantakyselyiden tulosten kanssa. Varmista aina tietorakenteesi oikeellisuus.
mitä tapahtuu, jos yrität lähettää objektin suoraan? http-moduuli hyväksyy vain merkkijonoja tai Buffereita. Jos yrität res.end({ message: "test" }), Node.js antaa virheen: TypeError [ERR_INVALID_ARG_TYPE]: Ensimmäisen argumentin on oltava joko merkkijono tai Buffer. Objektit täytyy ensin muuntaa JSON-merkkijonoiksi.
Varmista, että otsikko asetettiin oikein: Käytä -i-valitsinta curlin kanssa nähdäksesi vastausotsikot:
curl -i http://localhost:8000Sinun pitäisi nähdä otsikoissa Content-Type: application/json. Jos näet text/plain tai ei lainkaan Content-Typea, olet joko kutsunut setHeader() vasta writeHead() jälkeen tai unohtanut sen kokonaan.
Yleinen virheenkorjaustilanne: Jos frontendin JavaScript saa undefined kutsuttaessa fetch('/api').then(r => r.json()), tarkista otsikot komennolla curl -i. Content-Type-otsikon on oltava tarkalleen application/json (herkkä kirjainkoolle joillekin tiukoille parseroinneille).
Tallenna ja poistu tiedostosta json.js painamalla CTRL+X. Nyt ajetaan palvelin komennolla node:
node json.jsToisessa päätelaitteessa päästäkäämme palvelimelle käyttämällä cURLia:
curl http://localhost:8000Kun painamme ENTER, näemme seuraavan tuloksen:
{"message": "This is a JSON response"}Olemme nyt onnistuneesti palauttaneet JSON-vastauksen, aivan kuten monissa suosituissa API:issa, joiden kanssa luomme sovelluksia. Muista lopettaa käynnissä oleva palvelin painamalla CTRL+C, jotta voimme palata normaaliin komentorivipyyntöön. Seuraavaksi tarkastellaan toista suositun tiedonpalautusmuotoa: CSV.
CSV:n tarjoaminen
Comma Separated Values (CSV) -tiedostomuoto on tekstistandardi, jota käytetään yleisesti taulukkomuotoisen datan tarjoamiseen. Useimmissa tapauksissa jokainen rivi erotetaan rivinvaihdolla, ja jokainen rivin kohde erotetaan pilkulla.
Avaa työkalualueellamme csv.js-tiedosto tekstieditorilla:
nano csv.jsLisätään seuraavat rivit requestListener()-funktioomme:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/csv");
res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
};
...Tällä kertaa meidän Content-Type osoittaa, että palautettavana on CSV-tiedosto, koska arvoksi on asetettu text/csv. Toinen otsake, jonka lisäämme, on Content-Disposition. Tämä otsake kertoo selaimelle, miten tiedot näytetään, erityisesti selaimessa tai erillisenä tiedostona.
Kun palautamme CSV-vastauksia, useimmat nykyaikaiset selaimet lataavat tiedoston automaattisesti, vaikka Content-Disposition-otsikkoa ei olisi asetettu. Kuitenkin CSV-tiedoston palauttamisen yhteydessä meidän tulisi silti lisätä tämä otsikko, sillä se mahdollistaa CSV-tiedoston nimen asettamisen. Tässä tapauksessa ilmoitamme selaimelle, että kyseinen CSV-tiedosto on liite ja se tulisi ladata. Kerromme sitten selaimelle, että tiedoston nimi on oceanpals.csv.
Kirjoitetaan CSV-tiedot HTTP-vastaukseen:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/csv");
res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
res.writeHead(200);
res.end(`id,name,email\n1,Sammy Shark,shark@ocean.com`);
};
...Palauta 200/OK -tila CSV-datan kanssa. Pilkku erottaa arvot kussakin sarakkeessa ja rivinvaihtomerkki (\n) erottaa rivit. Meillä on kaksi riviä: otsikkorivi ja yksi datarivi.
CSV-muotoiluun liittyvä sudenkuoppa: Yksinkertainen esimerkki yllä toimii, mutta tuotantokäyttöön tarkoitettu CSV:n luominen vaatii poikkeusten käsittelyä. Jos datassasi on pilkkuja (kuten "Smith, John") tai lainausmerkkejä, ne rikkovat CSV-muodon. Oikea CSV-enkoodaus käärii arvot lainausmerkkeihin ja kaksoistaa sisäiset lainausmerkit: "Smith, John" muuttuu CSV:ssä muotoon "Smith, ""John""". Tuotantokäyttöön käytä kirjastoa kuten csv-stringify tai käsittele arvot oikein, jotta jäsentämisvirheitä ei synny.
Mitä tapahtuu ilman Content-Disposition-otsikkoa? Useimmat selaimet lataavat tiedoston, jos Content-Type: text/csv on asetettu, mutta tiedostonimi saattaa olla URL-polku tai jokin geneerinen nimi. attachment;filename=oceanpals.csv-otsikko varmistaa, että selain tallentaa tiedoston tietyn nimellä. Ilman attachment-asetusta jotkut selaimet yrittävät näyttää CSV-tiedoston suoraan, mikä näyttää usein sekavalta.
Testaamme tätä palvelinta selaimessa. Tallenna csv.js ja poistu editorista CTRL+X-näppäimellä.
Käynnistä palvelin Node.js-komennolla:
node csv.jsAvaa toinen terminaali ja yhdistetään palvelimeen käyttäen cURLia:
curl http://localhost:8000Konsoli näyttää tämän:
id,name,email
1,Sammy Shark,shark@ocean.comJos menemme selaimellamme osoitteeseen http://localhost:8000, CSV-tiedosto ladataan. Sen tiedoston nimi on oceanpals.csv.
Poistu käynnissä olevasta palvelimesta painamalla CTRL+C palataksesi tavalliseen komentorivikehotteeseen.
Kun olemme palauttaneet JSONin ja CSV:n, olemme käsitelleet kahta suosituinta tapausta API:lle. Siirrytään nyt siihen, miten palautamme tietoja verkkosivustoille, joita ihmiset katsovat selaimessa.
Palvelimen HTML
HTML (HyperText Markup Language) on yleisin formaatti, jota käytetään, kun haluamme käyttäjien olevan vuorovaikutuksessa palvelimemme kanssa verkkoselaimen kautta. Se luotiin jäsentämään verkkosisältöä. Verkkoselaimet on suunniteltu näyttämään HTML-sisältöä sekä kaikkia tyylejä, jotka lisäämme CSS:llä, toisella front-end-verkkoteknologialla, jonka avulla voimme muuttaa verkkosivujemme ulkoasua.
Avataan html.js uudelleen tekstieditorillamme:
nano html.jsMuokkaa requestListener()-funktiota palauttamaan sopiva Content-Type-otsikko HTML-vastetta varten:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/html");
};
...Palataan nyt HTML-sisältöön käyttäjälle. Lisää korostetut rivit tiedostoon html.js, jotta se näyttää tältä:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(`<html><body><h1>This is HTML</h1></body></html>`);
};
...Lisäämme ensin HTTP-tilakoodin. Sitten kutsumme response.end() -metodia merkkijonon argumentilla, joka sisältää kelvollista HTML:ää. Kun käytämme palvelintamme selaimessa, näemme HTML-sivun, jossa on yksi otsikkotagi, joka sisältää This is HTML.
Tallenna ja poistu painamalla CTRL+X. Nyt ajetaan palvelin komennolla node:
node html.jsNäemme Server is running on http://localhost:8000, kun ohjelmamme on käynnistynyt.
Siirry nyt selaimeen ja käy osoitteessa http://localhost:8000. Sivumme näyttää tältä:
Lopetetaan käynnissä oleva palvelin painamalla CTRL+C ja palataan tavalliseen komentorivipyyntöön.
On tavallista, että HTML kirjoitetaan tiedostoon erillään palvelinpuolen koodista, kuten Node.js-ohjelmistamme. Seuraavaksi katsotaan, miten voimme palauttaa HTML-vastauksia tiedostoista.
Vaihe 3 — HTML-sivun tarjoaminen tiedostosta
Voimme tarjota HTML:ää merkkijonoina Node.js:ssä käyttäjälle, mutta on suositeltavampaa ladata HTML-tiedostoja ja tarjota niiden sisältö. Näin HTML-tiedoston kasvaessa meidän ei tarvitse ylläpitää pitkiä merkkijonoja Node.js-koodissamme, mikä pitää koodin tiiviimpänä ja antaa meille mahdollisuuden työskennellä verkkosivustomme eri osa-alueiden parissa itsenäisesti. Tämä "huoltoalueiden erottelu" on yleistä monissa web-kehitysympäristöissä, joten on hyvä tietää, kuinka HTML-tiedostoja ladataan tukemaan tätä Node.js:ssä.
HTML-tiedostojen palvelemiseksi lataamme HTML-tiedoston fs-moduulilla ja käytämme sen tietoja kirjoittaessamme HTTP-vastaustamme.
Ensiksi luomme HTML-tiedoston, jonka verkkopalvelin palauttaa. Luo uusi HTML-tiedosto:
touch index.htmlAvaa nyt index.html tekstieditorissa:
nano index.htmlVerkkosivumme tulee olemaan minimaalinen. Sillä on oranssi tausta ja sen keskellä näkyy tervehdysteksti. Lisää tämä koodi tiedostoon:
<!DOCTYPE html>
<head>
<title>My Website</title>
<style>
*,
html {
margin: 0;
padding: 0;
border: 0;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
position: relative;
background-color: rgb(236, 152, 42);
}
.center {
width: 100%;
height: 50%;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: "Trebuchet MS", Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 144px;
}
p {
font-size: 64px;
}
</style>
</head>
<body>
<div class="center">
<h1>Hello Again!</h1>
<p>This is served from a file</p>
</div>
</body>
</html>Tämä yksittäinen verkkosivu näyttää kaksi tekstiriviä: Hello Again! ja This is served from a file. Rivien pitäisi näkyä sivun keskellä, toinen toisen yläpuolella. Ensimmäinen tekstirivi näytetään otsikkona, joten se on suuri. Toinen tekstirivi tulee näkymään hieman pienempänä. Kaikki teksti on valkoista ja verkkosivulla on oranssi tausta.
Vaikka tämä artikkeli tai sarja ei käsittele aihetta laajasti, jos olet kiinnostunut oppimaan lisää HTML:stä, CSS:stä ja muista front-end-verkkoteknologioista, voit tutustua Mozillan Getting Started with the Web -oppaaseen.
Se on kaikki, mitä tarvitsemme HTML:ään, joten tallenna ja sulje tiedosto painamalla CTRL+X. Voimme nyt siirtyä palvelinkoodiin.
Tässä harjoituksessa työskentelemme tiedoston htmlFile.js parissa. Avaa se tekstieditorilla:
nano htmlFile.jsKoska meidän täytyy lukea tiedosto, aloitetaan tuomalla fs-moduuli:
const http = require("http");
const fs = require('fs').promises;
...Tämä moduuli sisältää readFile()-funktion, jota käytämme HTML-tiedoston lataamiseen paikan päällä. Tuomme lupausversion noudattaen nykyaikaisia JavaScriptin parhaita käytäntöjä. Käytämme lupauksia, sillä ne ovat syntaktisesti tiiviimpiä kuin palautuskutsut, joita jouduttaisiin käyttämään, jos määrittäisimme fs vain require('fs')-komennolla. Lisätietoja asynkronisen ohjelmoinnin parhaista käytännöistä voit lukea oppaastamme How To Write Asynchronous Code in Node.js.
Haluamme, että HTML-tiedostomme luetaan, kun käyttäjä pyytää järjestelmäämme. Aloitetaan muokkaamalla requestListener()-funktiota tiedoston lukemiseksi:
...
const requestListener = function (req, res) {
fs.readFile(__dirname + "/index.html")
};
...Käytämme fs.readFile()-metodia tiedoston lataamiseen. Sen argumenttina on __dirname + "/index.html". Erityinen muuttuja __dirname sisältää absoluuttisen polun siihen paikkaan, jossa Node.js-koodi suoritetaan. Tämän jälkeen liitämme /index.html, jotta voimme ladata aikaisemmin luomamme HTML-tiedoston.
Palataan nyt HTML-sivulle, kun se on ladattu:
...
const requestListener = function (req, res) {
fs.readFile(__dirname + "/index.html")
.then(contents => {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(contents);
})
};
...Jos fs.readFile() -lupaus onnistuu, se palauttaa datansa. Käytämme then()-metodia tämän tilanteen käsittelemiseen. contents-parametri sisältää HTML-tiedoston datan.
Aloitamme asettamalla Content-Type-otsikon arvoon text/html kertoaksemme asiakkaalle, että palautamme HTML-dataa. Sen jälkeen kirjoitamme tilakoodin osoittaaksemme, että pyyntö onnistui. Lopuksi lähetämme asiakkaalle lataamamme HTML-sivun, jonka data on contents-muuttujassa.
Metodi fs.readFile() voi joskus epäonnistua, joten meidän tulisi käsitellä tämä tapaus, kun saamme virheen. Lisää tämä requestListener()-funktioon:
...
const requestListener = function (req, res) {
fs.readFile(__dirname + "/index.html")
.then(contents => {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(contents);
})
.catch(err => {
res.writeHead(500);
res.end('Internal Server Error');
return;
});
};
...Tallenna tiedosto ja sulje nano painamalla CTRL+X.
Kun lupaus kohtaa virheen, se hylätään. Käsittelemme sen catch()-menetelmällä. Se asettaa tilakoodin 500, mikä tarkoittaa, että sisäinen virhe on tapahtunut, ja lähettää käyttäjäystävällisen virheilmoituksen. Käytännössä tuotantoympäristössä sinun ei pitäisi koskaan paljastaa käyttäjille raakavirhetietoja, sillä se voi vuotaa arkaluonteista tietoa.
Suorita palvelimemme node komennolla:
node htmlFile.jsSiirry verkkoselaimella osoitteeseen http://localhost:8000. Näet tämän sivun:
Olet nyt palauttanut HTML-sivun palvelimelta käyttäjälle. Voit lopettaa käynnissä olevan palvelimen painamalla CTRL+C. Näet terminaalin kehotteen palaavan, kun teet niin.
Kun kirjoitat koodia tällaisella tavalla tuotannossa, et välttämättä halua ladata HTML-sivua joka kerta, kun saat HTTP-pyynnön. Vaikka tämä HTML-sivu on suunnilleen 800 tavun kokoinen, monimutkaisemmat verkkosivustot voivat olla megatavuja suuria. Suuret tiedostot voivat kestää hetken latautua. Jos sivustosi odottaa paljon liikennettä, voi olla parasta ladata HTML-tiedostot käynnistyksen yhteydessä ja tallentaa niiden sisältö. Kun ne on ladattu, voit määrittää palvelimen ja saada sen kuuntelemaan pyyntöjä tietyssä osoitteessa.
Demonstroidaksemme tätä menetelmää, katsotaanpa, kuinka voimme muokata palvelintamme tehokkaammaksi ja skaalautuvammaksi.
HTML:n tehokas tarjoaminen
Sen sijaan, että lataamme HTML:n jokaista pyyntöä varten, tässä vaiheessa lataamme sen kerran alussa. Pyyntö palauttaa tiedot, jotka lataimme käynnistyksen yhteydessä.
Avaa terminaalissa Node.js-skripti uudelleen tekstieditorilla:
nano htmlFile.jsAloitetaan lisäämällä uusi muuttuja ennen kuin luomme requestListener()-funktion:
...
let indexFile;
const requestListener = function (req, res) {
...Kun suoritamme tämän ohjelman, tämä muuttuja sisältää HTML-tiedoston sisällön.
Nyt säädetään requestListener()-funktiota uudelleen. Tiedoston lataamisen sijaan se palauttaa nyt indexFile-muuttujan sisällön:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(indexFile);
};
...Seuraavaksi siirrämme tiedoston lukemislogiikan requestListener()-funktiosta palvelimen käynnistykseen. Tee seuraavat muutokset luodessamme palvelinta:
...
const server = http.createServer(requestListener);
fs.readFile(__dirname + "/index.html")
.then(contents => {
indexFile = contents;
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});
})
.catch(err => {
console.error(`Could not read index.html file: ${err}`);
process.exit(1);
});Tallenna tiedosto ja sulje nano painamalla CTRL+X.
Koodi, joka lukee tiedoston, on samanlainen kuin mitä kirjoitimme ensimmäisellä yrityksellämme. Kun kuitenkin onnistumme lukemaan tiedoston, tallennamme sen sisällön nyt globaaliin indexFile-muuttujaamme. Sen jälkeen käynnistämme palvelimen listen()-metodilla. Tärkeää on, että tiedosto ladataan ennen palvelimen suorittamista. Näin requestListener()-funktio palauttaa varmasti HTML-sivun, sillä indexFile ei ole enää tyhjä muuttuja.
Virheenkäsittelijämme on myös muuttunut. Jos tiedostoa ei voida ladata, sieppaamme virheen ja tulostamme sen konsoliimme. Sen jälkeen lopetamme Node.js-ohjelman exit()-funktiolla ilman, että käynnistämme palvelinta. Näin voimme nähdä, miksi tiedoston lukeminen epäonnistui, korjata ongelman ja käynnistää palvelimen uudelleen.
Olemme nyt luoneet erilaisia verkkopalvelimia, jotka palauttavat käyttäjälle erilaisia tietotyyppejä. Tähän mennessä emme ole käyttäneet mitään pyyntöön liittyvää tietoa päätääksemme, mitä palautetaan. Meidän täytyy käyttää pyyntötietoja, kun asetamme erilaisia reittejä tai polkuja Node.js-palvelimessa, joten seuraavaksi katsotaan, miten ne toimivat yhdessä.
Vaihe 4 — Reittien hallinta HTTP-pyyntöobjektin avulla
Useimmilla sivustoilla, joita vierailemme, tai API:illa, joita käytämme, on yleensä enemmän kuin yksi päätepiste, jotta voimme käyttää erilaisia resursseja. Hyvä esimerkki olisi kirjahallintajärjestelmä, jota saatettaisiin käyttää kirjastossa. Sen ei tarvitsisi hallita vain kirjatietoja, vaan myös kirjailijatietoja helpottamaan luettelointia ja hakua.
Vaikka kirjojen ja kirjailijoiden tiedot liittyvät toisiinsa, ne ovat kaksi eri objektia. Näissä tapauksissa ohjelmistokehittäjät yleensä koodaavat jokaisen objektin eri päätepisteisiin osoittaakseen API-käyttäjälle, minkä tyyppisen datan kanssa he ovat tekemisissä.
Luodaan uusi palvelin pienelle kirjastolle, joka palauttaa kahta erilaista tietotyyppiä. Jos käyttäjä menee palvelimemme osoitteeseen /books, hän saa JSON-muodossa kirjalistan. Jos hän menee /authors, hän saa JSON-muodossa listan tekijätiedoista.
Tähän asti palvelin palauttaa saman vastauksen jokaiseen pyyntöön. Useiden resurssien tukemiseksi tallenna tietosi muuttujiin ja käytä ehdollista reititystä. Esimerkiksi:
const books = JSON.stringify([
{ title: "The Alchemist", author: "Paulo Coelho", year: 1988 },
{ title: "The Prophet", author: "Kahlil Gibran", year: 1923 }
]);
const authors = JSON.stringify([
{ name: "Paulo Coelho", countryOfBirth: "Brazil", yearOfBirth: 1947 },
{ name: "Kahlil Gibran", countryOfBirth: "Lebanon", yearOfBirth: 1883 }
]);
const requestListener = function (req, res) {
res.setHeader("Content-Type", "application/json");
switch (req.url) {
case "/books":
res.writeHead(200);
res.end(books);
break;
case "/authors":
res.writeHead(200);
res.end(authors);
break;
default:
res.writeHead(404);
res.end(JSON.stringify({error:"Resource not found"}));
}
}Testaa komennolla curl http://localhost:8000/books tai /authors nähdäksesi vastaavat tiedot. Mikä tahansa muu URL palauttaa 404-virheen.
Ongelma yksinkertaisen merkkijonon vertailun kanssa: req.url on raakamerkkijono, joka sisältää kaiken domainin jälkeen—polut, kyselymerkkijonot ja fragmentit. Jos joku vierailee osoitteessa /books?id=123, sinun case "/books" ei osu kohdalle, koska req.url on /books?id=123, ei /books. Kyselyparametrit rikkovat reitityksen: /books?sort=name ei osu /books. URL-koodatut merkit voivat rikkoa reitityksen: /books%20reviews (välilyönti koodattuna %20) ei osu /books reviews.
Käytä sen sijaan URL-APIa: URL-API erottaa polun automaattisesti kyselyparametreista, dekoodaa URL-koodatut merkit ja käsittelee reunatapaukset oikein.
const host = req.headers.host || 'localhost:8000';
const url = new URL(req.url, `http://${host}`);
switch (url.pathname) {
case '/books':
// Validate HTTP method - only GET allowed for this endpoint
if (req.method !== 'GET') {
res.writeHead(405, { 'Allow': 'GET' });
return res.end(JSON.stringify({ error: 'Method not allowed' }));
}
res.writeHead(200);
return res.end(books);
case '/authors':
if (req.method !== 'GET') {
res.writeHead(405, { 'Allow': 'GET' });
return res.end(JSON.stringify({ error: 'Method not allowed' }));
}
res.writeHead(200);
return res.end(authors);
default:
res.writeHead(404);
return res.end(JSON.stringify({ error: 'Resource not found' }));
}Miksi tarkistaa HTTP-metodi? Sama polku voi tukea eri HTTP-menetelmiä (GET tietojen hakemiseen, POST tietojen luomiseen). Ilman tätä tarkistusta POST-pyyntö osoitteeseen /books palauttaisi kirjojen listan uuden kirjan luomisen sijaan, mikä rikkoo REST API -käytäntöjä. 405 Method Not Allowed -tilakoodi ja Allow-otsake kertovat asiakkaalle, mitkä metodit ovat sallittuja tällä päätepisteellä.
Kyselymerkkijonon käsittely: URL-rajapinta erottaa automaattisesti polun (/books) kyselyparametreista (?id=123), joten /books?id=123 ohjautuu oikein kirjojen päätepisteeseen, samalla kun voit tarvittaessa käyttää url.searchParams.get('id').
Testaa erilaisia tilanteita:
curl http://localhost:8000/books
curl http://localhost:8000/authors
curl http://localhost:8000/books?id=123 # Query string doesn't break routing
curl -X POST http://localhost:8000/books # Returns 405 Method Not Allowed
curl http://localhost:8000/nonexistent # Returns 404Vaihe 5 — POST-pyyntöjen käsittely ja pyyntöjen runkojen jäsentäminen
Tähän asti olemme käsitelleet vain GET-pyyntöjä. Useimmat API:t tarvitsevat mahdollisuuden vastaanottaa tietoja asiakkailta, mikä tapahtuu tyypillisesti POST-pyyntöjen kautta. http-moduuli vaatii pyyntöjen runkojen käsittelyn manuaalisesti, sillä ne vastaanotetaan tietovirtoina.
Kriittinen oivallus: Toisin kuin GET-pyynnöissä, joissa data tulee URL-osoitteessa, POST-pyyntöjen rungot saapuvat virtana. Tämä tarkoittaa, että data ei saavu kerralla – se tulee paloina. Jos yrität jäsentää JSONia ennen kuin kaikki palat ovat saapuneet, saat epätäydellistä tai väärinmuodostunutta dataa. Sinun on ensin kerättävä kaikki palat ja sen jälkeen jäsennettävä ne.
Näin tapahtuu, jos yrität analysoida liian aikaisin:
// WRONG - Body isn't complete yet
req.on('data', chunk => {
const data = JSON.parse(chunk); // Will fail - chunk is only part of the body
});
// CORRECT - Collect all chunks first
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', () => {
const data = JSON.parse(body); // Now body is complete
});Luo palvelin, joka hyväksyy POST-pyynnöt JSON-datalla. Luo uusi tiedosto:
touch post.js
cp routes.js post.jsAvaa post.js:
nano post.jsTässä on tuotantovalmiiksi tarkoitettu JSON-runkojen jäsentäjä, joka käsittelee turvallisuuteen liittyvät seikat:
const MAX_BODY = 1 * 1024 * 1024; // 1MB limit prevents memory exhaustion
function parseJSONBody(req, res) {
return new Promise((resolve, reject) => {
let body = '';
let received = 0;
// Validate Content-Type before processing
const contentType = req.headers['content-type'] || '';
if (!contentType.startsWith('application/json')) {
res.writeHead(415, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Unsupported Content-Type' }));
return reject(new Error('Unsupported Content-Type'));
}
// Collect data chunks
req.on('data', chunk => {
received += chunk.length;
// Prevent memory exhaustion from huge payloads
if (received > MAX_BODY) {
res.writeHead(413, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Payload too large' }));
req.destroy(); // Stop receiving more data
return reject(new Error('Payload too large'));
}
body += chunk.toString(); // Convert Buffer to string
});
// All chunks received, now parse
req.on('end', () => {
try {
if (!body) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Empty request body' }));
return reject(new Error('Empty request body'));
}
const data = JSON.parse(body);
// Validate it's an object (not a string, number, etc.)
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON object' }));
return reject(new Error('Invalid JSON object'));
}
resolve(data);
} catch (parseError) {
// JSON.parse() throws for malformed JSON
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Malformed JSON' }));
console.error('JSON parse error:', parseError.message);
}
});
// Handle network errors
req.on('error', (error) => {
console.error('Request error:', error.message);
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Request error' }));
});
});
}Miksi tarkistaa Content-Type? Jos asiakas lähettää lomaketietoja käyttämällä Content-Type: application/x-www-form-urlencoded, niiden jäsentäminen JSON-muodossa epäonnistuu. 415 Unsupported Media Type -tilakoodi kertoo asiakkaalle, että heidän Content-Type ei ole hyväksyttävä.
Miksi rajoittaa kehon kokoa? Ilman rajoitusta haitallinen asiakas voisi lähettää gigatavuja dataa, mikä kuluttaisi palvelimesi muistin loppuun. 413 Payload Too Large -vastaus ja req.destroy() lopettavat yhteyden välittömästi, kun raja ylittyy.
Miksi validoida jäsennelty tulos? JSON.parse('"hello"') onnistuu, mutta palauttaa merkkijonon, ei oliota. Koodisi saattaa odottaa, että data.property toimii, mutta merkkijonoilla ei ole ominaisuuksia. Tarkistus varmistaa, että saat todellisen objektin.
Käytä tätä funktiota POST-käsittelijässäsi:
const requestListener = async function (req, res) {
if (req.method === 'POST' && req.url === '/books') {
const bookData = await parseJSONBody(req, res);
// Now use bookData safely
// ...
}
};Vaihe 6 — Staattisten tiedostojen tarjoaminen
Monet verkkosovellukset tarvitsevat staattisten resurssien, kuten kuvien, CSS-tiedostojen, JavaScript-tiedostojen tai muiden resurssien, tarjoamista. Vaikka http-moduulilla ei ole sisäänrakennettua staattisten tiedostojen tarjoamista kuten Expressin express.static, sen voi toteuttaa käyttämällä fs- ja path-moduuleja.
Kriittinen tietoturvaongelma: Jos käytät naiiviesti path.join(__dirname, 'public', req.url) ilman tarkistusta, hyökkääjä voi päästä käsiksi mihin tahansa tiedostoon palvelimellasi hakemistojen läpikulkuhyökkäyksillä. Pyyntö kuten GET /../../../etc/passwd voisi paljastaa järjestelmätiedostoja. path.resolve()-tarkistus varmistaa, että ratkaistu polku ei koskaan pääse public-hakemiston ulkopuolelle.
Tässä on, mitä voi mennä pieleen ilman asianmukaista polun tarkistusta:
// VULNERABLE - Allows directory traversal
const filePath = path.join(__dirname, 'public', req.url);
// Request: /../../../etc/passwd
// Resolves to: /etc/passwd (outside public directory!)
// SECURE - Validates path stays within public directory
const safePath = path.resolve(publicDir, '.' + reqPath);
if (!safePath.startsWith(publicDir)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
// Request: /../../../etc/passwd
// safePath: /home/user/app/public/../../../etc/passwd
// Resolved: /etc/passwd
// Check fails because /etc/passwd doesn't start with /home/user/app/publicLuo turvallinen staattinen tiedostopalvelin:
touch static.jsAvaa static.js:
nano static.jsTarjoa staattisia tiedostoja tehokkaasti ja turvallisesti suoratoistamalla fs.createReadStream()-funktiolla ja käyttämällä turvallista polunratkaisua:
const http = require("http");
const fs = require("fs");
const path = require("path");
const host = 'localhost';
const port = 8000;
const mimeTypes = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.pdf': 'application/pdf'
};
const publicDir = path.resolve(__dirname, 'public');
const requestListener = (req, res) => {
// Normalize the request path
let reqPath = req.url === '/' ? '/index.html' : req.url;
// Strip query string - ?id=123 shouldn't affect file path
reqPath = reqPath.split('?')[0];
// Resolve path and check it stays within publicDir
// The '.' prefix prevents absolute paths from being resolved incorrectly
const safePath = path.resolve(publicDir, '.' + reqPath);
// Critical security check: ensure resolved path is within publicDir
if (!safePath.startsWith(publicDir)) {
res.writeHead(403, { 'X-Content-Type-Options': 'nosniff' });
res.end('Forbidden');
return;
}
// Check if file exists and is actually a file (not a directory)
fs.stat(safePath, (err, stat) => {
if (err || !stat.isFile()) {
res.writeHead(404, { 'X-Content-Type-Options': 'nosniff' });
res.end('File not found');
return;
}
// Determine MIME type from file extension
const ext = path.extname(safePath).toLowerCase();
const contentType = mimeTypes[ext] || 'application/octet-stream';
res.setHeader("Content-Type", contentType);
res.setHeader("X-Content-Type-Options", "nosniff"); // Prevents MIME sniffing attacks
res.writeHead(200);
// Stream file instead of loading into memory
// Critical for large files - won't consume all memory
const stream = fs.createReadStream(safePath);
stream.pipe(res);
stream.on('error', () => {
// File might be deleted between stat() and createReadStream()
res.writeHead(500, { 'X-Content-Type-Options': 'nosniff' });
res.end('Server error');
});
});
};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
console.log(`Serving static files from: ${publicDir}`);
});Luo public-hakemisto ja lisää testitiedosto:
mkdir public
echo "<h1>Static File Served!</h1>" > public/index.htmlAja palvelin:
node static.jsVieraile selaimellasi osoitteessa http://localhost:8000 nähdäksesi palvelimen tarjoaman staattisen tiedoston.
Miksi käyttää suoratoistoa fs.readFile()-funktion sijaan? 100MB videon kohdalla fs.readFile() lataa koko tiedoston muistiin ennen sen lähettämistä. fs.createReadStream() -funktiolla data virtaa suoraan levyltä verkkoon pieninä osina, ja muistia käytetään vain vähän riippumatta tiedoston koosta.
Miksi tarkistaa stat.isFile()? Jos joku pyytää hakemiston polkua, kuten /images/, fs.stat() onnistuu, mutta isFile() palauttaa false. Ilman tätä tarkistusta yrität striimata hakemistoa, mikä epäonnistuu. Tarkistus varmistaa, että tarjoat vain todellisia tiedostoja.
Testaa polun läpikulun suojaus:
curl http://localhost:8000/../../../etc/passwd
# Should return 403 Forbidden, not the system fileVaihe 7 — Virheenkäsittely ja lokitus
Vankka virheenkäsittely on tärkeää tuotantopalvelimilla. http-moduuli ei tarjoa automaattista virheenkäsittelyä, joten sinun täytyy toteuttaa se itse.
Oikea virheenkäsittely sisältää asynkronisten virheiden sieppaamisen, niiden asianmukaisen kirjaamisen, käyttäjäystävällisten virhevastauksien lähettämisen sekä palvelimen kaatumisen estämisen käsittelemättömien virheiden vuoksi. Parannetaan palvelintamme kattavalla virheenkäsittelyllä ja kirjaamisella:
const http = require("http");
const fs = require('fs').promises;
const host = 'localhost';
const port = 8000;
// Simple logging utility
const log = {
info: (msg) => console.log(`[INFO] ${new Date().toISOString()} - ${msg}`),
error: (msg, err) => console.error(`[ERROR] ${new Date().toISOString()} - ${msg}`, err),
warn: (msg) => console.warn(`[WARN] ${new Date().toISOString()} - ${msg}`)
};
const requestListener = async function (req, res) {
try {
log.info(`${req.method} ${req.url} - ${req.headers['user-agent'] || 'Unknown'}`);
if (req.url === '/') {
try {
const data = await fs.readFile(__dirname + '/index.html');
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(data);
} catch (error) {
log.error('Failed to read index.html', error);
res.writeHead(500);
res.end('<h1>Internal Server Error</h1><p>Unable to load page.</p>');
}
} else if (req.url === '/error') {
throw new Error('Simulated error for testing');
} else {
res.writeHead(404);
res.end('<h1>Not Found</h1><p>The requested resource was not found.</p>');
}
} catch (error) {
log.error('Unhandled error in request handler', error);
const errorMessage = process.env.NODE_ENV !== 'production'
? error.message
: 'An unexpected error occurred';
res.writeHead(500);
res.end(`<h1>Internal Server Error</h1><p>${errorMessage}</p>`);
}
};
const server = http.createServer(requestListener);
// Handle server-level errors
server.on('error', (error) => {
if (error.code === 'EADDRINUSE') {
log.error(`Port ${port} is already in use`);
} else {
log.error('Server error', error);
}
process.exit(1);
});
// Handle client connection errors
server.on('clientError', (err, socket) => {
log.warn('Client error: ' + err.message);
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(port, host, () => {
log.info(`Server is running on http://${host}:${port}`);
});Siisti sammutus (SIGTERM/SIGINT)
Kun suoritetaan prosessinhallintatyökalujen, kuten PM2:n tai Dockerin, alla, käsittele lopetussignaaleja sulkeaksesi palvelimen hallitusti:
const connections = new Set();
server.on('connection', socket => {
connections.add(socket);
socket.on('close', () => connections.delete(socket));
});
const shutdown = (signal) => {
log.warn(`${signal} received: shutting down gracefully...`);
server.close(() => {
log.info('Server closed.');
process.exit(0);
});
setTimeout(() => {
log.warn('Forcing socket close...');
connections.forEach(s => s.destroy());
process.exit(1);
}, 10000).unref();
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));Näytetyt tärkeimmät virheenkäsittelykäytännöt:
- Try-catch-lohkot asynkronisten operaatioiden ympärillä
- Palvelintason virheenkäsittelijät yhteys- ja porttiongelmiin
- Asiakaspään virheiden käsittely virheellisille pyynnöille
- Loki aikaleimoilla ja vakavuustasoilla
- Ympäristöä huomioivat virheilmoitukset (älä paljasta pinojen jälkiä tuotannossa)
- Hienovaraiset virhevastaukset sopivilla HTTP-tilakoodeilla
Parhaat käytännöt: Tuotannossa kannattaa käyttää jäsenneltyjä lokikirjastoa, kuten Winston tai Pino, ja lähettää kriittiset virheet virheenseurantapalveluihin, kuten Sentryyn tai Rollbariin. Tässä esimerkissä käytetään yksinkertaista konsolilokkeja opetustarkoituksiin.
Vaihe 8 — ES-moduulien (ESM) syntaksin käyttäminen
Moderni Node.js tukee ES-moduuleja (ESM) käyttämällä import/export-syntaksia CommonJS:n require/module.exports-sijaan. Käyttääksesi ESM:ää, nimeä tiedostosi joko .mjs-päätteellä tai aseta "type": "module" tiedostoon package.json.
ESM-syntaksi käyttää import-komentoa require-komennon sijaan ja export-komentoa module.exports-komennon sijaan. Tässä on sama peruspalvelin käyttäen ES-moduulisyntaksia:
Luo server.mjs:
touch server.mjs
nano server.mjsimport http from 'http';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
// ES Modules don't have __dirname, so we recreate it
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const host = 'localhost';
const port = 8000;
const requestListener = async (req, res) => {
try {
if (req.url === '/') {
const data = await fs.readFile(path.join(__dirname, 'index.html'));
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(data);
} else {
res.writeHead(404);
res.end('Not found');
}
} catch (error) {
console.error('Error:', error);
res.writeHead(500);
res.end('Server error');
}
};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});Suorita se:
node server.mjsTärkeimmät erot: ESM:ssä __dirname ei ole käytettävissä—se täytyy johtaa import.meta.url -osoitteesta. Lisäksi fs-operaatioiden tiedostopolut saattavat vaatia eksplisiittistä resoluutiota. Muuten HTTP-moduulin API pysyy ennallaan.
Vaihtoehtoisesti luo package.json tiedosto "type": "module" -asetuksella:
{
"type": "module"
}Sitten käytä .js-tiedostoja ESM-syntaksilla:
import http from 'http';
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('ESM Server');
});
server.listen(8000);Tuotantoon liittyvät näkökohdat
Siirryttäessä kehityksestä tuotantoon tulee huomioida useita tärkeitä seikkoja:
HTTPS-tuki
http-moduuli palvelee vain HTTP:tä. HTTPS:ää varten käytä vastaavasti https-moduulia:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('path/to/private-key.pem'),
cert: fs.readFileSync('path/to/certificate.pem')
};
const server = https.createServer(options, requestListener);Turvallisuus: Älä koskaan tallenna yksityisiä avaimia tai sertifikaatteja versionhallintaan. Käytä tuotannossa ympäristömuuttujia tai turvallisia salaisuuksien hallintapalveluita.
Käänteisen välityspalvelimen määrittäminen
Tuotannossa Node.js -palvelimet toimivat tyypillisesti käänteisten välityspalvelimien, kuten Nginx:n tai Apachen, takana. Välityspalvelin hoitaa SSL-päätöksen, staattiset tiedostot ja kuormantasauksen. Node.js -palvelimesi tulisi:
- Kuuntele vain
localhosttai127.0.0.1 - Lue
X-Forwarded-For-otsikot asiakas-IP-osoitteita varten - Luota välityspalvelimen otsikoihin vain omalta välityspalvelimeltasi
Katso oppaamme siitä, kuinka määrittää Nginx käänteisenä välityspalvelimena Ubuntussa asennuksen yksityiskohtia varten.
Prosessien hallinta
Tuotannossa käytä prosessinhallintatyökaluja, kuten PM2, hoitamaan:
- Automaattinen uudelleenkäynnistys kaatumisten yhteydessä
- Nollaseisokkiaikaisten käyttöönottojen hallinta
- Lokien hallinta
- Klusteritila useiden CPU-ytimien hyödyntämiseen
Lue lisää oppaastamme Kuinka asentaa Node.js-sovellus tuotantokäyttöön Ubuntulla.
Suorituskyvyn optimointi
Tiedostojen välimuisti: Lataa staattiset tiedostot käynnistyksen yhteydessä (kuten vaiheessa 3 on esitetty) sen sijaan, että lukisit ne levyltä jokaisella pyynnöllä. Tämä vähentää merkittävästi I/O-kuormitusta.
Suoratoista suuria tiedostoja: Yli 1 Mt tiedostoille käytä
fs.createReadStream()ja ohjaa se vastaukseen sen sijaan, että lataisit koko tiedoston muistiin.Yhteyspoolaus: Tietokantayhteyksille käytä yhteyspoolia välttääksesi uusien yhteyksien luomisen aiheuttaman ylimääräisen kuormituksen jokaista pyyntöä kohden.
Pakkaus: Käytä
zlib-moduulia tekstivastausten gzip-pakkaukseen kaistanleveyden säästämiseksi:const zlib = require('zlib'); const gzip = zlib.createGzip(); res.setHeader('Content-Encoding', 'gzip'); gzip.pipe(res); gzip.write(data); gzip.end();Ryhmätila: Käytä Node.js
cluster-moduulia käynnistämään työprosessit, joiden määrä vastaa prosessoriytimiä, maksimoiden läpimenon CPU-intensiivisissä operaatioissa.Pyyntöaikakatkaisut: Aseta kohtuulliset aikakatkaisuarvot estääksesi hitaita asiakkaita kuluttamasta resursseja:
server.setTimeout(30000); // 30 secondsMuistin seuranta: Seuraa muistin käyttöä vuotojen havaitsemiseksi varhaisessa vaiheessa. Tuotantoympäristössä seuraa pinon kokoa ja toteuta hallitut uudelleenkäynnistykset, jos muisti kasvaa rajattomasti.
Välimuistin käyttö ja pakkaamisen parhaat käytännöt
Parantaaksesi suorituskykyä ja lyhentääksesi vasteaikoja, käytä välimuistia ja pakkausta tarvittaessa.
Ota välimuisti käyttöön staattisille tiedostoille:
LisääCache-Control- jaETag-otsikot staattisiin tiedostovastauksiisi. Esimerkiksi:const stat = fs.statSync(safePath); res.setHeader('Cache-Control', 'public, max-age=86400'); // cache for 1 day res.setHeader('ETag', `"${stat.size}-${stat.mtime.getTime()}"`);Selaimet ja sisältöjakeluverkot (CDN) käyttävät välimuistissa olevia kopioita uudelleen, kunnes ETag tai välimuistin kesto vanhenee.
Käytä gzip- tai Brotli-pakkausta:
Tekstipohjaiset vastaukset, kuten HTML, CSS, JS ja JSON, pakkaantuvat erittäin hyvin. Voit käyttää pakkausta Node:nzlib-moduulilla:const zlib = require('zlib'); const stream = fs.createReadStream(safePath); res.setHeader('Content-Encoding', 'gzip'); stream.pipe(zlib.createGzip()).pipe(res);Tämä vähentää kaistanleveyden käyttöä ja nopeuttaa toimitusta hitaammilla yhteyksillä oleville asiakkaille.
Vältä dynaamisten päätepisteiden liiallista välimuistittamista:
API:iden tai usein päivittyvien tietojen kohdalla aseta lyhyempiä välimuistin säilytysaikoja (esim.max-age=60) tai käytä tarvittaessano-store.
Nämä optimoinnit täydentävät staattisten tiedostojen suoratoistoa ja siistiä sulkemista, auttaen Node.js-palvelintasi tarjoamaan korkean suorituskyvyn samalla kun se pysyy turvallisena ja ennustettavana.
Käytä Node.js:n http-moduulia minimoitujen API:en, mukautetun reitityksen tai HTTP:n sisäisten toimintojen opetteluun.
Käytä Express/Fastifya, kun tarvitset middlewareä, mallinnusta tai nopeaa prototyyppien rakentamista.
Yleisimpien ongelmien vianmääritys
Portti on jo käytössä (EADDRINUSE)
Virhe: Virhe: listen EADDRINUSE: osoite on jo käytössä :::8000
Ratkaisu:
- Etsi porttia käyttävä prosessi:
lsof -i :8000(macOS/Linux) tainetstat -ano | findstr :8000(Windows) - Lopeta prosessi tai käytä eri porttia
- Lisää virheenkäsittely (kuten vaiheessa 7 on esitetty) tämän sieppaamiseksi asianmukaisesti
Pyyntöviestiä ei vastaanotettu
Ongelma: POST-pyynnön runko on tyhjä tai määrittelemätön
Ratkaisu: Pyyntöjen rungot ovat virtoja—ne täytyy kerätä paloittain. Käytä aina kuvattua mallia vaiheessa 5:
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', () => {
// Now parse body
});CORS-virheet selaimessa
Ongelma: Selain estää pyyntöjä CORS-politiikan takia
Ratkaisu: Lisää CORS-otsikot vastaukseesi:
res.setHeader("Access-Control-Allow-Origin", "*"); // Or specific domain
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");Tietoturva: "*"-merkin käyttäminen Access-Control-Allow-Origin-asetuksessa sallii kaikkien alkuperien pääsyn. Tuotantoympäristössä määritä etusivusi domain selkeästi.
Staattiset tiedostot palauttavat 404:n
Ongelma: Staattisia tiedostoja ei löydy, vaikka ne ovat olemassa
Ratkaisu:
- Tarkista tiedostopolun ratkaisu
- Varmista, että
__dirnameosoittaa oikeaan hakemistoon - Varmista, että tiedoston käyttöoikeudet sallivat lukemisen
- Varmista, että polun puhdistus ei poista kelvollisia merkkejä
Usein Kysytyt Kysymykset
Mikä on HTTP-moduuli Node.js:ssä?
http-moduuli on Node.js:n sisäänrakennettu moduuli HTTP-palvelinten ja -asiakkaiden luomiseen. Se tarjoaa matalan tason rajapinnat HTTP-pyyntöjen ja -vastausten käsittelyyn ilman ulkoisia riippuvuuksia. Se on osa Node.js:n ydintä, joten se on saatavilla kaikissa Node.js-asennuksissa.
Kuinka luon yksinkertaisen verkkopalvelimen Node.js:ssä?
Luo palvelin käyttämällä http.createServer() ja pyyntökuuntelijafunktiota:
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World');
});
server.listen(8000);Tämä luo palvelimen, joka kuuntelee porttia 8000 ja vastaa kaikkiin pyyntöihin sanomalla "Hello World".
Mikä on ero HTTP-moduulin ja Express.js:n välillä?
http-moduuli on Node.js:n ydintoiminnallisuus, joka tarjoaa matalan tason HTTP-käsittelyä. Express.js on http-moduulin päälle rakennettu kehys, joka lisää reitityksen, middleware-tuen, templaten ja muita käteviä ominaisuuksia. http-moduuli antaa täydellisen hallinnan, mutta vaatii enemmän manuaalista asetusta, kun taas Express.js tarjoaa abstraktioita lisäriippuvuuksien kustannuksella.
Käytä http-moduulia kun:
- Kevyiden API:iden tai mikropalveluiden rakentaminen
- HTTP:n perusteiden oppiminen
- Haluat nollariippuvuuksia
- Täysi hallinta pyyntöjen/vastausten käsittelyyn
Käytä Express.js:ää, kun:
- Monimutkaisten sovellusten rakentaminen, joissa on paljon reittejä
- Tarvitset middleware-ekosysteemin
- Nopea prototypointi on etusijalla
- Haluat yhteisön standardien mukaisia kuvioita
Laajan johdatuksen Express.js:ään ja siihen, kuinka se yksinkertaistaa verkkokehitystä, saat katsomalla oppaamme Node.js Expressin perusteet.
Voinko rakentaa REST-rajapinnan Node.js:llä ilman Expressiä?
Kyllä, ehdottomasti. http-moduuli riittää REST-rajapintojen rakentamiseen. Sinun täytyy käsitellä itse:
- Reittien jäsentäminen (käyttäen
req.urljareq.method) - Pyynnön rungon jäsentäminen (virtapätkien kerääminen)
- Vastausten muotoilu
- Virheenkäsittely
Yksinkertaisille API-rajapinnoille tämä on usein suositeltavampaa kuin kehysriippuvuuksien lisääminen. Katso tämän oppaan vaiheet 4 ja 5 reitityksen ja POST-pyyntöjen käsittelyesimerkeistä.
Kuinka käsittelen eri reittejä käyttäen Node.js:n HTTP-moduulia?
Jäsennä req.url ja käytä ehdollista logiikkaa tai switch-lauseketta:
const requestListener = (req, res) => {
switch (req.url) {
case '/':
res.writeHead(200);
res.end('Home page');
break;
case '/about':
res.writeHead(200);
res.end('About page');
break;
default:
res.writeHead(404);
res.end('Not found');
}
};Monimutkaisemmassa reitityksessä voit jäsentää URL-polkuja ja kyselymerkkijonoja käyttäen url-moduulia sekä käsitellä erilaisia HTTP-menetelmiä.
Kuinka lähetän HTML- tai JSON-vastauksia HTTP-moduulilla?
Aseta Content-Type-otsake asianmukaisesti ja lähetä tiedot:
HTML:
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end('<h1>Hello</h1>');JSON:
res.setHeader('Content-Type', 'application/json');
res.writeHead(200);
res.end(JSON.stringify({ message: 'Hello' }));Content-Type-otsake kertoo asiakkaalle, miten vastaustiedot tulisi tulkita.
Mitä porttia minun pitäisi käyttää paikalliselle Node.js-palvelimelle?
Yleisiä kehitysportteja ovat:
- 8000 tai 8080: Yleinen HTTP-kehitys
- 3000: Suosittu esimerkiksi Expressin kaltaisten kehysten kanssa
- 5000: Myös yleinen
Mikä tahansa portti yli 1024 toimii ei-juurikäyttäjille. Portit alle 1024 vaativat järjestelmänvalvojan oikeudet. Tarkista saatavuus komennolla lsof -i :PORT (macOS/Linux) ennen käyttöä.
Kuinka voin käsitellä 404-virheitä Node.js HTTP-palvelimessa?
Lisää oletustapaus reitityslogiikkaasi:
const requestListener = (req, res) => {
if (req.url === '/') {
res.writeHead(200);
res.end('Home');
} else {
res.writeHead(404);
res.end('Not Found');
}
};Aseta aina tilakoodiksi 404 olemattomille reiteille noudattaaksesi HTTP-standardeja.
Kuinka tarjoan staattisia tiedostoja ilman Expressiä?
Käytä fs-moduulia tiedostojen lukemiseen ja path-moduulia turvalliseen polkujen ratkaisuun:
const fs = require('fs').promises;
const path = require('path');
const requestListener = async (req, res) => {
const filePath = path.join(__dirname, 'public', req.url);
try {
const data = await fs.readFile(filePath);
res.writeHead(200);
res.end(data);
} catch (error) {
res.writeHead(404);
res.end('File not found');
}
};Varmista aina polkujen kelpoisuus estääksesi hakemistojen läpikäyntihyökkäykset (katso Vaihe 6 turvallista toteutusta varten).
Voinko käyttää ES-moduuleja (import) HTTP-moduulin kanssa Node.js:ssä?
Kyllä, Node.js tukee ES-moduuleja. Käytä jompaakumpaa:
- Tiedosto, jonka tiedostopääte on
.mjs:
import http from 'http';
// ... rest of code- Aseta
"type": "module"tiedostossapackage.json, sitten käytä.js-tiedostoja:
{
"type": "module"
}Huomaa, että ES-moduuleilla ei ole __dirname-muuttujaa—hanki se import.meta.url-muuttujasta, kuten vaiheessa 8 on esitetty.
Kuinka jäsentelen POST-pyynnön sisältöä HTTP-moduulilla?
POST-runko saapuu virtana. Kerää palat, sitten jäsennä:
const parseBody = (req) => {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', () => {
try {
resolve(JSON.parse(body)); // For JSON
} catch (error) {
reject(error);
}
});
req.on('error', reject);
});
};Tämä malli toimii JSON:lle, lomakedatalle tai mille tahansa tekstipohjaiselle sisältötyypille.
Onko HTTP-moduuli sopiva tuotantokäyttöön?
Kyllä, http-moduuli on tuotantovalmis ja sitä käyttävät monet Node.js-sovellukset. Kuitenkin harkitse:
- Prosessinhallinta: Käytä PM2:ta tai vastaavaa automaattiseen uudelleenkäynnistykseen
- Käänteinen välityspalvelin: Suorita Nginxin/Apachen takana SSL:lle ja kuormantasaukselle
- Virheenkäsittely: Toteuta kattava virheenkäsittely ja lokitus
- Tietoturva: Vahvista syötteet, puhdista polut, aseta kokorajoitukset pyynnöille
- Suorituskyky: Välimuisti staattiset tiedostot, käytä klusterointia prosessorikehykseen sidotuissa toiminnoissa
Monet tuotantosovellukset käyttävät http-moduulia suoraan, erityisesti mikropalvelut ja kevyet API:t. Yksityiskohtaista ohjeistusta tuotantokäyttöön ottamiseen varten löydät artikkelista Kuinka asentaa Node.js-sovellus tuotantokäyttöön Ubuntu 22.04:ssä.
Johtopäätös
Tässä opetusohjelmassa olet rakentanut tuotantovalmiita Node.js HTTP -palvelimia alusta alkaen. Aloitit perustekstivastauksella, sitten etenit palvelemaan useita sisältötyyppejä (JSON, CSV ja HTML). Toteutit reitityksen eri URL-osoitteiden käsittelemiseksi, jäsensit POST-pyyntöjen rungot, palvelit staattisia tiedostoja turvallisesti ja lisäsit kattavan virheenkäsittelyn ja lokituksen. Olet myös tutustunut sekä CommonJS- että ES-moduulisyntakseihin sekä tuotantoon liittyviin käyttöönottoasioihin.
Olet nyt varustautunut luomaan web-palvelimia, jotka käsittelevät monenlaisia vaatimuksia ilman kehyksiin perustuvia riippuvuuksia. Tämän tiedon avulla voit:
- Rakenna kevyitä REST-rajapintoja mikropalveluille
- Palvele monisivuisia verkkosivustoja asianmukaisella reitityksellä
- Käsittele tiedostojen latauksia ja siirtoja
- Toteuta mukautettu väliohjelmalogiikka
- Tee perusteltuja päätöksiä siitä, milloin kehykset tuovat lisäarvoa verrattuna tarpeettomaan monimutkaisuuteen
Seuraavat vaiheet:
- Sukella syvemmälle Node.js:n perusteisiin: Palaa How To Code in Node.js -sarjaan saadaksesi kattavan käsittelyn
- Tutustu viralliseen dokumentaatioon: Node.js:n HTTP-moduulin dokumentaatio sisältää yksityiskohtaiset API-viitteet ja edistyneet käyttötapaukset
- Kun tarvitset kehysominaisuuksia: Harkitse Express.js:n perusteita nopeaan kehitykseen, jossa on tuki middlewarelle ja mallipohjille
Näiden HTTP-perusteiden ymmärtäminen tekee sinusta pätevämmän Node.js-kehittäjän. Riippumatta siitä, käytätkö kehyksiä vai työskenteletkö suoraan http-moduulin kanssa, ymmärrät nyt ne taustamekaniikat, jotka tukevat kaikkia Node.js-verkkosovelluksia. Tämä tieto auttaa sinua virheiden korjaamisessa, suorituskyvyn optimoinnissa ja tehokkaasti skaalautuvien järjestelmien suunnittelussa.
Kiitos, että opiskelet Linux-Console.net -yhteisön kanssa.

