Du har opprettet et flatt filsystem Content Management System (CMS) ved hjelp av Go. Det neste trinnet er å ta det samme idealet og lage en webserver ved hjelp av Node.js. Jeg vil vise deg hvordan du laster inn biblioteker, oppretter serveren og kjører serveren.
Dette CMS vil bruke datastrukturen for nettsiden som beskrevet i første veiledning, Bygg en CMS: Structure and Styling. Derfor, last ned og installer denne grunnleggende strukturen i en ny katalog.
Den enkleste måten å installere Node.js på Mac, er med Homebrew. Hvis du ikke har installert Homebrew ennå, vil opplæringen Homebrew Demystified: OS Xs Ultimate Package Manager vise deg hvordan.
For å installere Node.js med Homebrew, skriv denne instruksjonen til en terminal:
brygg installasjonsnoden
Når du er ferdig, vil du ha kommandoer med node og npm installert på din Mac. For alle andre plattformer, følg instruksjonene på Node.js nettsted.
Vær forsiktig: Mange pakkeledere installerer for øyeblikket Node.js versjon 0.10. Denne opplæringen antas at du har versjon 5.3 eller nyere. Du kan sjekke din versjon ved å skrive:
node - versjon
De node
kommandoen kjører JavaScript-tolken. De NPM
kommandoen er en pakkebehandling for Node.js for å installere nye biblioteker, lage nye prosjekter, og kjøre skript for et prosjekt. Det er mange gode opplæringsprogrammer og kurs på Node.js og NPM på Envato Tuts+.
For å installere bibliotekene for webserveren må du kjøre disse kommandoene i Terminal.app eller iTerm.app-programmet:
npm installere express - save npm installere styrer - save npm installerings øyeblikk - lagre npm installert merket - lagre npm installere jade - lagre npm installere morgan - save
Express er en webapplikasjonsutviklingsplattform. Det ligner GoWeb-biblioteket i Go. Håndtak er den templerende motoren for å lage sidene. Moment er et bibliotek for å jobbe med datoer. Merket er en flott Markdown til HTML-omformer i JavaScript. Jade er et HTML shorthand språk for enkelt å lage HTML. Morgan er et mellomvare bibliotek for Express som genererer Apache Standard Log Files.
En alternativ måte å installere bibliotekene på er å laste ned kildefilene for denne opplæringen. Når du har lastet ned og pakket ut, skriv dette inn i hovedkatalogen:
npm - installer
Det vil installere alt som trengs for å skape dette prosjektet.
Nå kan du begynne å lage serveren. I den øverste katalogen av prosjektet oppretter du en fil som heter nodePress.js, åpner den i valgfri redigeringsprogram, og begynner å legge til følgende kode. Jeg skal forklare koden som den er plassert i filen.
// // Last inn biblioteker som brukes. // var fs = krever ('fs'); var sti = krever ("sti"); var child_process = krever ('child_process'); var prosess = krever ('prosess'); var express = kreve ('express'); // http://expressjs.com/no/ var morgan = krever ('morgan'); // https://github.com/expressjs/morgan var Handlebars = krever ("styrer"); // http://handlebarsjs.com/ var moment = krever ("øyeblikk"); // http://momentjs.com/ var markert = krever ('merket'); // https://github.com/chjj/marked var jade = krever ('jade'); // http://jade-lang.com/
Serverkoden starter med initialiseringen av alle bibliotekene som brukes til å lage serveren. Biblioteker som ikke har en kommentar med en webadresse, er interne Node.js-biblioteker.
// // Oppsett Globale Variabler. // var deler = JSON.parse (fs.readFileSync ('./ server.json', 'utf8')); var styleDir = process.cwd () + '/ themes / styling /' + parts ['CurrentStyling']; var layoutDir = process.cwd () + '/ temaer / layouts /' + parts ['CurrentLayout']; var siteCSS = null; var siteScripts = null; var mainPage = null;
Deretter konfigurerer jeg alle de globale variablene og bibliotekskonfigurasjonene. Bruken av globale variabler er ikke den beste programvareutformingspraksis, men det fungerer og gir rask utvikling.
De deler
variabel er et hash-array som inneholder alle deler av en nettside. Hver side refererer til innholdet i denne variabelen. Den begynner med innholdet i server.json-filen som finnes øverst i serverkatalogen.
Jeg bruker deretter informasjonen fra server.json-filen for å lage de komplette stiene til stiler
og oppsett
kataloger som brukes til dette nettstedet.
Tre variabler blir deretter satt til nullverdier: siteCSS
, siteScripts
, og hovedside
. Disse globale variablene vil inneholde alt innholdet i CSS, JavaScripts og hovedindeksens innhold. Disse tre elementene er de mest etterspurte elementene på en hvilken som helst webserver. Derfor, å holde dem i minnet sparer tid. Hvis cache
variabelen i server.json-filen er feil, disse elementene blir re-read med hver forespørsel.
markert.settOptjoner (renderer: nytt merket.Renderer (), gfm: sant, tabeller: true, pauser: false, pedantic: false, sanitize: false, smartLists: true, smartypants: false);
Denne koden er for å konfigurere Marked-biblioteket for å generere HTML fra Markdown. For det meste går jeg på bord og smartLists støtte.
deler ["layout"] = fs.readFileSync (layoutDir + '/template.html', 'utf8'); deler ["404"] = fs.readFileSync (styleDir + '/404.html', 'utf8'); deler ["footer"] = fs.readFileSync (styleDir + '/footer.html', 'utf8'); deler ["header"] = fs.readFileSync (styleDir + '/header.html', 'utf8'); deler ["sidebar"] = fs.readFileSync (styleDir + '/sidebar.html', 'utf8'); // // Les i sidedelene. // var delFiles = fs.readdirSync (deler ['Sitebase'] + "parts /"); delFiles.forEach (funksjon (ele, indeks, array) deler [path.basename (ele, path.extname (ele))] = figurePage (deler ['Sitebase'] + "parts /" + path.basename path.extname (ele))););
De deler
variabel er ytterligere lastet med delene fra stiler
og oppsett
kataloger. Hver fil i deler
katalog inni nettstedet
katalogen er også lastet inn i deler
global variabel. Navnet på filen uten utvidelsen er navnet som brukes til å lagre innholdet i filen. Disse navnene blir utvidet i Handlebars-makroen.
// // Setup Handlebar's Helpers. // // // HandleBars Helper: save // // Beskrivelse: Denne hjelperen forventer en // """ "der navnet // er lagret med verdien for fremtidige // utvidelser. Den returnerer også // verdien direkte. // Handlebars.registerHelper (" lagre ", funksjon (navn, tekst) // // Lokale variabler. // var newName = "", newText = ""; // // Se om navnet og teksten er i det første argumentet // med en |. Hvis det er tilfelle, trekk dem ut ordentlig. Ellers, // bruk navnet og teksten argumenter som gitt. // hvis (name.indexOf ("|")> 0) var deler = name.split ("|"); newName = deler [0]; newText = parts [1]; else newName = navn; newText = text; // // Registrer den nye hjelperen. // Handlebars.registerHelper (newName, function () return newText;; // // Retur teksten. / returner newText;) ; // // HandleBars Helper: date // // Beskrivelse: Denne hjelperen returnerer datoen // basert på formatet gitt. // Handlebars.registerHelper ("date", funksjon (dFormat) return moment (). Format dFormat);); // // HandleBars Helper: cdate // // Beskrivelse: Denne hjelperen returnerer datoen gitt // i et format basert på formatet // gitt. // Handlebars.registerHelper ("cdate", funksjon (cTime, dFormat) retur øyeblikk (cTime) .format (dFormat););
Den neste delen av koden definerer håndteringshjelpene som jeg definerte for bruk på webserveren: lagre
, Dato
, og cdate
. Lagrehjelpen gjør det mulig å opprette variabler på en side. Denne versjonen støtter goPress-versjonen der parameteren har navnet og verdien sammen adskilt av en "|". Du kan også angi en lagring med to parametere. For eksempel:
save "navn | Richard Guay" lagre "newName" "Richard Guay" Navn er: name newName er: newName
Dette vil gi de samme resultatene. Jeg foretrekker den andre tilnærmingen, men Handlebars-biblioteket i Go tillater ikke mer enn én parameter.
De Dato
og cdate
hjelpere formaterer gjeldende dato (Dato
) eller en bestemt dato (cdate
) ifølge moment.js bibliotek formatering regler. De cdate
Helper forventer at datoen skal gjengis som den første parameteren og ha ISO 8601-formatet.
// // Opprett og konfigurer serveren. // var nodePress = express (); // // Konfigurer mellomvare. // nodePress.use (morgan ('combined'))
Nå oppretter koden et Express-instans for å konfigurere den faktiske servermotoren. De nodePress.use ()
funksjonen setter opp middleware-programvaren. Middleware er noen kode som blir servert på hver samtale til serveren. Her satt jeg opp Morgan.js-biblioteket for å skape den riktige serverloggutgangen.
// // Definer ruter. // nodePress.get ('/', funksjon (forespørsel, svar) setBasicHeader (respons), hvis ((deler ["Cache"] == true) && (mainPage! = null)) response.send (mainPage) ; else mainPage = side ("main"); response.send (mainPage);); nodePress.get ('/ favicon.ico', funksjon (forespørsel, svar) var options = root: parts ['Sitebase'] + 'images /', dotfiles: 'deny', overskrifter: 'x-timestamp' : Date.now (), 'x-sendt': true; response.set ("Content-Type", "image / ico"); setBasicHeader (respons); response.sendFile ('favicon.ico' , funksjon (feil) hvis (err) console.log (err); response.status (err.status) .end (); else console.log ('Favicon ble sendt:', 'favicon.ico' ););); nodePress.get ('/ stylesheets.css', funksjon (forespørsel, svar) response.set ("Content-Type", "text / css"); setBasicHeader (respons); response.type ("css"); ((Cache)] == true) && (siteCSS! = null)) response.send (siteCSS); else siteCSS = fs.readFileSync (deler ['Sitebase'] + 'css / final / final .css '); response.send (siteCSS);); nodePress.get ('/ scripts.js', funksjon (forespørsel, svar) response.set ("Content-Type", "text / javascript"); setBasicHeader = true) && (siteScripts! = null)) response.send (siteScripts); else siteScripts = fs.readFileSync (deler ['Sitebase'] + 'js / final / final.js', 'utf8'); response.send (siteScripts);); nodePress.get ('/ images /: image', funksjon (forespørsel, svar) var options = root: parts ['Sitebase'] + 'images /', dotfiles: 'deny', overskrifter: 'x-timestamp ': Date.now (),' x-sendt ': true; response.set ("Content-Type", "image /" + path.extname (request.params.image) .substr (1)); setBasicHeader (response); response.sendFile (request.params.image, alternativer, funksjon (err) hvis (err) console.log (err); response.status (err.status) .end (); else console.log ('Bildet ble sendt:', request.params.image););); nodePress.get ('/ innlegg / blogger /: blogg', funksjon (forespørsel, svar) setBasicHeader (respons); response.send (post ("blogger", request.params.blog, "index"));) ; nodePress.get ('/ innlegg / blogger /: blogg /: post', funksjon (forespørsel, svar) setBasicHeader (response); response.send (post ("blogger", request.params.blog, request.params.post ));); nodePress.get ('/ innlegg / nyheter /: nyheter', funksjon (forespørsel, svar) setBasicHeader (respons); response.send (post ("nyheter", request.params.news, "index"));) ; nodePress.get ('/ posts / news /: news /: post', funksjon (forespørsel, svar) setBasicHeader (response); response.send (post ("nyheter", request.params.news, request.params.post ));); nodePress.get ('/: side', funksjon (forespørsel, svar) setBasicHeader (respons); response.send (side (request.params.page)););
Denne delen av koden definerer alle ruter som trengs for å implementere webserveren. Alle ruter kjører setBasicHeader ()
funksjon for å angi de riktige headerverdiene. Alle forespørsler om en sidetype vil fremkalle side()
funksjon, mens alle forespørsler for posttypeside vil fremkalde innlegg ()
funksjon.
Standard for Innholdstype
er HTML. Derfor, for CSS, JavaScript og bilder, den Innholdstype
er eksplisitt satt til sin passende verdi.
Du kan også definere ruter med sette
, slette
, og post
REST verb. Denne enkle serveren bruker bare få
verb.
// // Start serveren. // var addressItems = parts ['ServerAddress']. split (':'); var server = nodePress.listen (adresseItems [2], funksjon () var host = server.address () .adresse; var port = server.address (). port; console.log ('nodePress lytter på http: / /% s:% s ', vert, port););
Det siste du må gjøre før du definerer de forskjellige funksjonene som brukes, er å starte serveren. Server.json-filen inneholder DNS-navnet (her er det lokal vert
) og porten til serveren. En gang ble analysert, serverens lytte()
funksjonen bruker portnummeret til å starte serveren. Når serverporten er åpen, logger loggen adressen og porten for serveren.
// // Funksjon: setBasicHeader // // Beskrivelse: Denne funksjonen vil sette grunnleggende headerinformasjon // nødvendig. // // Inputs: // response Respons-objektet // funksjon setBasicHeader (respons) response.append ("Cache-Control", "max-age = 2592000, cache"); response.append ("Server", "nodePress - et CMS skrevet i node fra Custom Computer Tools: http://customct.com.");
Den første funksjonen er definert setBasicHeader ()
funksjon. Denne funksjonen setter svarhodet for å fortelle nettleseren å cache siden i en måned. Det forteller også nettleseren at serveren er en nodePress-server. Hvis det er noen andre standardheaderverdier du vil, vil du legge dem til her med response.append ()
funksjon.
// // Funksjon: side // // Beskrivelse: Denne funksjonen behandler en sideforespørsel // // Inputs: // side Den forespurte siden // funksjonsside (side) // // Behandle den oppgitte siden ved hjelp av standarden oppsett. // retur (prosessPage (deler ["layout"], deler ['Sitebase'] + "sider /" + side));
De side()
funksjonen sender layoutmalen for en side og plasseringen av siden på serveren til processPage ()
funksjon.
// // Funksjon: post // // Beskrivelse: Denne funksjonen behandler en postforespørsel // // Inputs: // type Type post. // cat Kategorien av innlegget. // post Den etterspurgte post // funksjonen post (type, katt, post) // // Behandle innlegget gitt typen og etternavnet. // retur (prosessPage (deler ["layout"], deler ['Sitebase'] + "innlegg /" + type + "/" + katt + "/" + innlegg));
De post()
funksjonen er akkurat som side()
funksjon, bortsett fra at innleggene har flere elementer for å definere hvert innlegg. I denne serien av servere inneholder et innlegg en type
, kategori og den faktiske post
. Typen er enten blogger
eller nyheter
. Kategorien er flatcms
. Siden disse representerer katalognavn, kan du lage dem uansett hva du vil. Bare samsvar med navngivelsen til det som finnes i filsystemet ditt.
// // Funksjon: processPage // // Beskrivelse: Denne funksjonen behandler en side for CMS. // // Inputs: // layout Oppsettet som skal brukes til siden. // side Sti til siden for å gjengi. // funksjonsprosessPage (layout, side) // // Få sidens innhold og legg til i oppsettet. // var context = ; context = MergeRecursive (kontekst, deler); kontekst ['content'] = figurePage (side); kontekst ['PageName'] = path.basename (side, path.extname (side)); // // Last side data. // if (fileExists (page + ".json")) // // Last sidenes datafil og legg den til datastrukturen. // context = MergeRecursive (kontekst, JSON.parse (fs.readFileSync (side + '.json', 'utf8'))); // // Prosesshåndtakskoder. // varmal = Handlebars.compile (layout); var html = mal (kontekst); // // Behandle alle kortkoder. // html = processShortCodes (html); // // Kjør gjennom håndtakene igjen. // template = Handlebars.compile (html); html = mal (kontekst); // // Retur resultater. // retur (html);
De processPage ()
funksjonen får oppsettet og banen til sidens innhold for å gjengis. Funksjonen starter ved å lage en lokal kopi av deler
global variabel og legge til "innholdet" hashtag med resultatene av å ringe figurePage ()
funksjon. Det setter da sidenavn
hash verdi til navnet på siden.
Denne funksjonen kompilerer siden innholdet til layoutmalen ved hjelp av håndtak. Etter det, processShortCodes ()
funksjonen vil utvide alle kortkodene som er definert på siden. Deretter går håndtaksmallmotoren over koden en gang til. Nettleseren mottar deretter resultatene.
// // Funksjon: processShortCodes // // Beskrivelse: Denne funksjonen tar en streng og // behandler alle snarveiene i // strengen. // // Inputs: // content String to process // funksjonsprosessShortCodes (innhold) // // Opprett resultatvariabelen. // varresultater = ""; // // Finn den første kampen. // var scregFind = / \ - \ [([^ \]] *) \] \ - / i; var match = scregFind.exec (innhold); hvis (match! = null) results + = content.substr (0, match.index); var scregNameArg = /(\w+)(.*)*/i; var parts = scregNameArg.exec (match [1]); hvis (deler! = null) // // Finn sluttkoden. // var scregClose = nytt RegExp ("\\ - \\ [\\ /" + deler [1] + "\\] \\ -"); var igjen = content.substr (match.index + 4 + deler [1] .length); var match2 = scregClose.exec (venstre); hvis (match2! = null) // // Behandle vedlagte kortteksttekst. // var vedlagt = processShortCodes (content.substr (match.index + 4 + parts [1] .length, match2.index)); // // Finn ut om det var noen argumenter. // var args = ""; hvis (parts.length == 2) args = deler [2]; // // Kjør kortnummeret. // resultater + = kortkoder [deler [1]] (args, vedlagt); // // Behandle resten av koden for kortkoder. // resultater + = processShortCodes (left.substr (match2.index + 5 + parts [1] .length)); ellers // // Ugyldig kortkode. Returner full streng. // resultater = innhold; annet // // Ugyldig kortkode. Returner full streng. // resultater = innhold; annet // // Ingen kortnumre funnet. Returner strengen. // resultater = innhold; retur (resultater);
De processShortCodes ()
funksjonen tar innholdet på nettsiden som en streng og søker etter alle kortnumre. En kodenavn er en blokk med kode som ligner på HTML-koder. Et eksempel ville være:
-[eske]-Dette er inne i en boks
-[/eske]-
Denne koden har en kortkode for eske
rundt et HTML-avsnitt. Hvor HTML bruker <
og >
, bruk av kortkoder -[
og ]-
. Etter navnet kan en streng som inneholder argumenter til kortkoden, være eller ikke være der.
De processShortCodes ()
funksjonen finner en kortkode, får navnet og argumentene, finner slutt for å få innholdet, behandler innholdet for kortkoder, kjører kortnummeret med argumentene og innholdet, legger resultatene til den ferdige siden, og søker etter neste kortnummer i resten av siden. Sløyfen utføres ved å rekursivt kalle funksjonen.
// // Definer kortnummerfunksjonen. // var kortkoder = 'boks': funksjon (args, inne) return (""+ inni +"");, 'Kolonne1': funksjon (args, inne) return (""+ inni +"");, 'Kolonne2': funksjon (args, inne) return (""+ inni +"");, 'Kolonne1of3': funksjon (args, inne) return (""+ inni +"");, 'Kolonne2of3': funksjon (args, inne) return (""+ inni +"");, 'Kolonne3of3': funksjon (args, inne) return (""+ inni +"");, 'php': funksjon (args, inne) return ("");, 'js': funksjon (args, inne) return (""+ inni +"");, 'html': funksjon (args, inne) return (""+ inni +"");, 'css': funksjon (args, inne) return (""+ inni +"");;"+ inni +"
Denne neste delen definerer kortkoder
json struktur som definerer navnet på en kortkode knyttet til funksjonen. Alle kortnummerfunksjoner godtar to parametere: args
og innsiden
. De args
er alt etter navn og plass og før lukking av taggen. De innsiden
er alt som finnes i åpnings- og lukkekodekoden. Disse funksjonene er enkle, men du kan lage en shortcode for å utføre alt du kan tenke på i JavaScript.
// // Funksjon: figurePage // // Beskrivelse: Denne funksjonen viser sidetypen // og laster innholdet riktig // retur HTML-innholdet til siden. // // Inputs: // side Siden for å laste innholdet. // funksjonsfigurSide (side) var result = ""; hvis (fileExists (side + ".html")) // // Det er en HTML-fil. Les den inn og send den videre. // result = fs.readFileSync (side + ".html"); annet hvis (fileExists (side + ".amber")) // // Det er en jadefil. Konverter til HTML og send den på. Jeg // bruker fortsatt den gule forlengelsen for kompatibilitet // for å gå. // var jadeFun = jade.compileFile (side + ".amber", ); // Gjenopprett funksjonen var resultatet = jadeFun (); annet hvis (fileExists (side + ".md")) // // Det er en markdown-fil. Konverter til HTML og send // den på. // resultat = merket (fs.readFileSync (side + ".md"). toString ()); // // Dette angir merket URI-koding av sitatkarakterer. // resultat = result.replace (/ \ & quot;; / g, "\" "; returnere (resultat);
De figurePage ()
funksjonen mottar hele banen til en side på serveren. Denne funksjonen tester for at den skal være en HTML-, Markdown- eller Jade-side basert på utvidelsen. Jeg bruker fortsatt .amber for Jade siden det var biblioteket jeg brukte med goPress-serveren. Alt Markdown og Jade innhold blir oversatt til HTML før du overfører det til kallingsrutinen. Siden Markdown-prosessoren oversetter alle sitater til "
, Jeg oversetter dem tilbake før de går tilbake.
// // Funksjon: fileExists // // Beskrivelse: Denne funksjonen returnerer en boolsk sant hvis // filen eksisterer. Ellers falsk. // // Inputs: // filePath Sti til en fil i en streng. // funksjon fileExists (filePath) prøv return fs.statSync (filePath) .isFile (); fangst (feil) return false;
De Filen eksisterer()
funksjon er en erstatning for fs.exists ()
funksjon som pleide å være en del av fs
bibliotek av Node.js. Den bruker fs.statSync ()
funksjon for å prøve å få status for filen. Hvis det oppstår en feil, a falsk
returneres. Ellers returnerer den ekte
.
// // Funksjon: MergeRecursive // // Beskrivelse: Rekursivt flette egenskaper av to objekter // // Inputs: // obj1 Det første objektet til å flette // obj2 Det andre objektet til å flette // funksjon MergeRecursive (obj1, obj2) for (var p i obj2) prøv // Eiendom i destinasjonsobjekt sett; oppdater verdien. hvis (obj2 [p] .constructor == Objekt) obj1 [p] = MergeRecursive (obj1 [p], obj2 [p]); ellers obj1 [p] = obj2 [p]; fangst (e) // Eiendom i destinasjonsobjekt ikke satt; lag det og sett verdien. obj1 [p] = obj2 [p]; returner obj1;
Den siste funksjonen er MergeRecursive ()
funksjon. Den kopierer det andre passobjektet til det første passerte objektet. Jeg benytter dette for å kopiere hovedmenyen deler
global variabel til en lokal kopi før du legger til siderespesifikke deler.
Etter at du har lagret filen, kan du kjøre serveren med:
node nodePress.js
Alternativt kan du bruke NPM
Skript som er i Package.json-filen. Du kjører npm-skript som dette:
npm start
Dette vil kjøre start
skript som er inne i package.json filen.
Pek nettleseren din til http: // localhost: 8080
og du vil se siden ovenfor. Du har kanskje lagt merke til at jeg har lagt til mer testkode på hovedsiden. Alle endringene på sidene er i nedlastingen for denne opplæringen. De er for det meste bare noen små tilpasninger for å fullstendig teste funksjonaliteten og tilpasse eventuelle forskjeller fra bruk av forskjellige biblioteker. Den mest bemerkelsesverdige forskjellen er at Jade-biblioteket ikke bruker $
å navngi variabler mens Amber gjør.
Nå har du akkurat det samme flate filsystemet CMS i Go og Node.js. Dette riper bare overflaten på det du kan bygge med denne plattformen. Forsøk og prøv noe nytt. Det er den beste delen av å skape din egen webserver.