Heim >Web-Frontend >js-Tutorial >Erstellen Sie eine vollständige MVC -Website mit Express
Express ist eines der besten Frameworks für node.js. Es hat großartige Unterstützung und eine Reihe hilfreicher Funktionen. Es gibt viele großartige Artikel, die alle Grundlagen abdecken. Dieses Mal möchte ich jedoch etwas tiefer ausgraben und meinen Workflow zum Erstellen einer vollständigen Website weitergeben. Im Allgemeinen dient dieser Artikel nicht nur für Express, sondern auch für die Verwendung in Kombination mit einigen anderen großartigen Tools, die für Knotenentwickler verfügbar sind.
Um diesem Tutorial zu folgen, gehe ich davon aus, dass Sie mit dem Knoten etwas vertraut sind und es bereits in Ihrem System installieren lassen.
Im Zentrum von Express steht die Verbindung. Dies ist ein Middleware -Framework, das mit vielen nützlichen Dingen ausgestattet ist. Wenn Sie sich fragen, was genau Middleware ist, ist hier ein kurzes Beispiel:
const Connect = Request ('Connect'),<br> http = required ('http');<br><br> const App = Connect ()<br> .use (Funktion (req, res, next) {<br> console.log ("Das ist meine erste Middleware");<br> nächste();<br> })<br> .use (Funktion (req, res, next) {<br> console.log ("Das ist meine zweite Middleware");<br> nächste();<br> })<br> .use (Funktion (req, res, next) {<br> console.log ("Ende");<br> res.end ("Hallo Welt");<br> });<br><br> http.createServer (App) .Listen (3000);<br>
Middleware ist im Grunde eine Funktion, die Antwortobjekte und ein Antwortobjekt akzeptiert oder den Fluss an die nächste Funktion übergeben, indem der nächste () Methodenaufruf in der zweiten Middleware aufgerufen wird. Und req.cookies mit einem vom Namen des Cookies gekennzeichneten Objekts.
Express -Wraps verbinden sich tatsächlich und fügt einige neue Funktionen hinzu, wie die Routing -Logik, was den Prozess viel glatter macht. Hier ist ein Beispiel für den Umgang mit einer Get -Anfrage in Express:
app.get ('/hello.txt', function (req, res) {<br> var body = 'Hallo Welt';<br> res.setheader ('Inhaltstyp', 'text/plain');<br> res.setheader ('content-Length', body.length);<br> res.end (Körper);<br> });<br>
Der Quellcode für diese von uns erstellte Beispiel -Site ist auf GitHub verfügbar. Fühlen Sie sich frei, es zu geben und damit zu spielen. Hier sind die Schritte zum Ausführen der Website.
{<br> "Name": "MyWebsite",<br> "Beschreibung": "Meine Website",<br> "Version": "0.0.1",<br> "Abhängigkeiten": {{<br> "Express": "5.x"<br> }<br> }<br>
Der Code des Framework wird in node_modules platziert und Sie können eine Instanz davon erstellen. Ich bevorzuge jedoch eine alternative Option, indem ich das Befehlszeilen-Tool verwende. Mit dem Befehl npx express generator:
Verwendung: Express [Optionen] [Dir]<br><br> Optionen:<br><br> -Version Ausgabe der Versionsnummer<br> -e, --ejs fügen EJS -Motorunterstützung hinzu<br> -Mop-Mops-Mops-Motorunterstützung hinzufügen<br> -HBS Fügen Sie Lenkermotorenstütze hinzu<br> -H, - -Hogan Fügen Sie Hogan.js Motorunterstützung hinzu<br> -v, --view <cotor> Ansicht add <engine> Support (Staub | ejs | hbs | hjs | jade | mops | twig | vash) (Standard an Jade)<br> -NO-View verwenden statische HTML anstelle von View Engine<br> -c, -css <engine> Stylesheet <onge> Support (Less | Stylus | Kompass | Sass) (Standardeinstellungen an einfache CSS)<br> -Git add .gitignore<br> -F,-Kraftkraft auf nicht leeres Verzeichnis<br> -h, -HELP -Ausgangsnutzungsinformationen<br></onge></engine></engine></cotor>
Wie Sie sehen können, gibt es nur einige Optionen, aber für mich sind sie genug. Normalerweise benutze ich weniger als CSS -Präprozessor und Lenker als Templating -Motor. In diesem Beispiel benötigen wir auch Sitzungsunterstützung, sodass die NPM -Installation und ein Ordner node_modules angezeigt werden.
Mir ist klar, dass der obige Ansatz nicht immer angemessen ist. Möglicherweise möchten Sie Ihre Route -Handler in ein anderes Verzeichnis oder ähnliches aufnehmen. Aber wie Sie in den nächsten Abschnitten sehen werden, werde ich Änderungen an der bereits erzeugten Struktur vornehmen, und es ist ziemlich einfach. Sie sollten also nur an die App.js denken, um mit unserer neuen Dateistruktur zu arbeiten. Wir müssen diese beiden Zeilen entfernen:
const userRouter = required ("./ Routen/Benutzer");<br> ...<br> app.use ("/user", userrouter);<br>
Jetzt müssen wir die Konfiguration einrichten. Stellen wir uns vor, unsere kleine Website sollte an drei verschiedenen Orten bereitgestellt werden: ein lokaler Server, ein Staging -Server und ein Produktionsserver. Natürlich sind die Einstellungen für jede Umgebung unterschiedlich, und wir sollten einen Mechanismus implementieren, der flexibel genug ist. Wie Sie wissen, wird jedes Knotenskript als Konsolenprogramm ausgeführt. So können wir leicht Befehlszeilenargumente senden, die die aktuelle Umgebung definieren. Ich habe diesen Teil in ein separates Modul gewickelt, um später einen Test dafür zu schreiben. Hier ist die Datei /config/index.js :
const config = {<br> Lokal: {<br> Modus: 'Lokal',<br> Port: 3000<br> },<br> Inszenierung: {<br> Modus: 'Staging',<br> Port: 4000<br> },<br> Produktion: {<br> Modus: 'Produktion',<br> Port: 5000<br> }<br> }<br> module.exports = function (modus) {<br> return config [modus || process.argv [2] || 'Lokal'] || config.Local;<br> }<br>
Es gibt nur zwei Einstellungen (vorerst): Port. Wie Sie vielleicht vermutet haben, verwendet die Anwendung verschiedene Ports für die verschiedenen Server. Aus diesem Grund müssen wir den Einstiegspunkt der Website in app.js aktualisieren.
const config = required ('./ config') ();<br> process.env.port = config.port;<br>
Um zwischen den Konfigurationen zu wechseln, fügen Sie einfach die Umgebung am Ende hinzu. Zum Beispiel:
NPM Start Inszenierung<br>
Führt den Server bei Port 4000 aus.
Jetzt haben wir alle unsere Einstellungen an einem Ort und sie sind leicht überschaubar.
Ich bin ein großer Fan von testgetriebenen Entwicklung (TDD). Ich werde versuchen, alle in diesem Artikel verwendeten Basisklassen abzudecken. Wenn Sie Tests für absolut alles haben, würde dieses Schreiben natürlich zu lange machen, aber im Allgemeinen sollten Sie beim Erstellen Ihrer eigenen Apps fortfahren. Eines meiner Lieblingsrahmen für das Testen ist UVU, da es sehr einfach zu bedienen und schnell zu verwenden ist. Natürlich ist es in der NPM -Registrierung erhältlich:
NPM Install-Save-dev UVU<br>
Erstellen Sie dann ein neues Skript im NPM -Test und Sie sollten Folgendes sehen:
config.js<br> • • • (3/3)<br><br> Gesamt: 3<br> Bestanden: 3<br> Übersprungen: 0<br> Dauer: 0,81 ms<br>
Diesmal schrieb ich zuerst die Implementierung und den Test zweitens. Das ist nicht genau die TDD -Art, Dinge zu tun, aber in den nächsten Abschnitten werde ich das Gegenteil tun.
Ich empfehle dringend, viel Zeit mit dem Schreiben von Tests zu verbringen. Es gibt nichts Besseres als eine vollständig getestete Anwendung.
Vor ein paar Jahren erkannte ich etwas sehr Wichtiges, was Ihnen helfen kann, bessere Programme zu produzieren. Fragen Sie sich jedes Mal, wenn Sie eine neue Klasse, ein neues Modul oder nur ein neues Stück Logik schreiben, fragen Sie sich:
Wie kann ich das testen?
Die Antwort auf diese Frage hilft Ihnen, viel effizienter zu codieren, bessere APIs zu erstellen und alles in gut getrennte Blöcke zu stecken. Sie können keine Tests für Spaghetti -Code schreiben. In der obigen Konfigurationsdatei ( /config/index.js ) habe ich beispielsweise die Möglichkeit hinzugefügt, die Produktionskonfiguration zu senden, das Knotenskript wird jedoch mit einem NPM -Installation von MongoDB ausgeführt.
Als nächstes schreiben wir einen Test, der überprüft, ob ein MongoDB -Server ausgeführt wird. Hier ist die Datei /tests/mongodb.js :
const {test} = required ("uvu");<br> const {mongoclient} = required ("mongoDB");<br><br> test ("MongoDB Server Active", Async Function () {<br> const client = neuer mongoclient ("mongoDB: //127.0.0.1: 27017/fastdelivery");<br> erwarten client.connect ();<br> });<br><br> test.run ();<br><br>
Wir müssen keine .connect -Methode des MongoDB -Clients hinzufügen, das jedes Mal, wenn wir eine Anfrage an die Datenbank stellen müssen, ein Mongoklient -Objekt erhält. Aus diesem Grund sollten wir in der ersten Servererstellung eine Verbindung zur Datenbank herstellen. Zu diesem Zweck in der verfügbaren Eigenschaft in req.db verfügbar, da die Middleware automatisch vor jeder Anfrage ausgeführt wird.
Wir alle kennen das MVC -Muster. Die Frage ist, wie dies zum Ausdruck gilt. Mehr oder weniger ist es eine Frage der Interpretation. In den nächsten Schritten erstelle ich Module, die als Modell, Ansicht und Controller fungieren.
Das Modell wird die Daten in unserer Anwendung behandeln. Es sollte Zugang zu einem Mongoklienten haben. Unser Modell sollte auch eine Methode zur Erweiterung haben, da wir möglicherweise verschiedene Arten von Modellen erstellen möchten. Zum Beispiel möchten wir vielleicht ein ContactSmodel. Daher müssen wir eine neue Spezifikation schreiben, /tests/base.model.js , um diese beiden Modellfunktionen zu testen. Und denken Sie daran, dass wir durch die Definition dieser Funktionen vor dem Codieren der Implementierung garantieren können, dass unser Modul nur das tut, was wir wollen.
const {test} = required ("uvu");<br> const assert = fordert ("uvu/assert");<br> const modelClass = required ("../ models/base");<br> const dbmockUp = {};<br> test ("Modulerstellung", asynchrische Funktion () {<br> const model = neue Modellklasse (DBMOCKUP);<br> Assert.OK (modell.db);<br> Assert.OK (model.setDB);<br> Assert.OK (Modell.Collection);<br> });<br> test.run ();<br>
Anstelle eines echten DB -Objekts und eines Getters für unser Datenbankansichtsverzeichnis werden in die Basis -Ansichtsklasse geändert. Diese kleine Veränderung erfordert jetzt eine weitere Änderung. Wir sollten ausdrücken, dass unsere Vorlagendateien jetzt in einem anderen Verzeichnis platziert sind:
app.set ("Ansichten", Path.Join (__ DirName, "Vorlagen");<br>
Zuerst werde ich definieren, was ich brauche, den Test schreiben und dann die Implementierung schreiben. Wir brauchen ein Modul, das den folgenden Regeln entspricht:
const data = {Entwickler: "Krasimir tSonev"};<br> response.contentType ("application/json");<br> Antwort.Send (json.Stringify (Daten));<br>
Instead of doing this every time, it would be nice to have an JSONView class, or even an tests directory, and if you run '/' after the route—which, in the example above, is actually the controller—is just a middleware function which accepts response , and express(1) command-line tool creates a directory named run method, which is the old middleware function
OK, wir haben eine gute Reihe von Kursen für unsere MVC -Architektur und haben unsere neu erstellten Module mit Tests behandelt. Jetzt sind wir bereit, mit der Website unserer gefälschten Firma FastDelivery fortzufahren.
Stellen wir uns vor, die Website hat zwei Teile: ein Front-End- und ein Verwaltungsgremium. Mit dem Front-End wird die in der Datenbank geschriebenen Informationen an unsere Endbenutzer angezeigt. Das Administratorbereich wird verwendet, um diese Daten zu verwalten. Beginnen wir mit unserem Administrator (Control) -Panel.
Lassen Sie uns zunächst einen einfachen Controller erstellen, der als Administrationsseite dient. Hier ist die Datei /routes/admin.js :
const Basecontroller = fordert ("./ Basis"),<br> View = Request ("../ Ansichten/Basis");<br> module.exports = new (Klasse admincontroller erweitert Basecontroller {<br> constructor () {<br> Super ("admin");<br> }<br> run (req, res, next) {<br> if (this.authorize (req)) {<br> req.Session.fastDelivery = true;<br> req.session.save (Funktion (err) {<br> var v = neue Ansicht (res, "admin");<br> v.render ({{<br> Titel: "Administration",<br> Inhalt: "Willkommen im Bedienfeld",<br> });<br> });<br> } anders {<br> const v = neue Ansicht (res, "admin-login");<br> v.render ({{<br> Titel: "Bitte login",<br> });<br> }<br> }<br> autorisieren (req) {<br> zurückkehren (<br> (req.session &&<br> req.session.fastdelivery &&<br> req.session.fastDelivery === true) ||<br> (Req.body &&<br> req.body.username === this.username &&<br> req.body.password === this.password)<br> );<br> }<br> }) ();<br>
Durch die Verwendung der vorgeschriebenen Basisklassen für unsere Controller und Ansichten können wir den Einstiegspunkt für das Bedienfeld problemlos erstellen. Die admin.Run -Methode direkt als Middleware. Das liegt daran, dass wir den Kontext behalten wollen. Wenn wir das tun:
app.all ('/admin*', admin.run);<br>
Das Wort admin wird auf etwas anderes verweist.
Jede Seite, die mit /admin beginnt, sollte geschützt werden. Um dies zu erreichen, werden wir die Middleware von Express: Sitzung verwenden. Es wird einfach ein Objekt an die Anforderung mit dem Namen Admin Controller angeschlossen, um zwei zusätzliche Dinge zu tun:
Hier ist eine kleine Helferfunktion, mit der wir dies erreichen können:
autorisieren (req) {<br> zurückkehren (<br> (req.session &&<br> req.session.fastdelivery &&<br> req.session.fastDelivery === true) ||<br> (Req.body &&<br> req.body.username === this.username &&<br> req.body.password === this.password)<br> );<br> }<br>
Zunächst haben wir eine Erklärung, die versucht, den Benutzer über das Sitzungsobjekt zu erkennen. Zweitens überprüfen wir, ob ein Formular eingereicht wurde. In diesem Fall sind die Daten aus dem Formular in der Bodyparser Middleware verfügbar. Dann prüfen wir nur, ob der Benutzername und das Passwort übereinstimmen.
Und jetzt ist hier der Titel, das Bild und die Typ -Eigenschaft den Eigentümer des Datensatzes. Zum Beispiel muss die Kontaktseite nur einen Datensatz mit Admin -Controller benötigt. Um die Aufgabe zu vereinfachen, habe ich beschlossen, die Liste der hinzugefügten Datensätze und das Formular zum Hinzufügen/Bearbeiten zu kombinieren. Wie Sie im folgenden Screenshot sehen können, ist der linke Teil der Seite für die Liste und den richtigen Teil für das Formular reserviert.
Alles auf einer Seite zu haben, bedeutet, dass wir uns auf den Teil konzentrieren müssen, der die Seite oder, um genauer zu sein, auf die Daten, die wir an die Vorlage senden, zu vermitteln. Deshalb habe ich mehrere Helferfunktionen erstellt, die kombiniert werden, wie SO:
this.del (req, function () {<br> this.form (req, res, function (formmarkup) {<br> this.list (Funktion (Listmarkup) {<br> v.render ({{<br> Titel: "Administration",<br> Inhalt: "Willkommen im Bedienfeld",<br> Liste: Listmarkup,<br> Form: Formarkup,<br> });<br> });<br> });<br> });<br> const v = neue Ansicht (res, "admin");<br>
Es sieht ein bisschen hässlich aus, aber es funktioniert, wie ich wollte. Der erste Helfer ist eine Aktion = delete & id = [ID des Datensatzes], es wird Daten aus der Sammlung entfernt. Die zweite Funktion wird als List -Methode bezeichnet, holt die Informationen ab und bereitet eine HTML -Tabelle vor, die später an die Vorlage gesendet wird. Die Implementierung dieser drei Helfer finden Sie im Quellcode für dieses Tutorial.
Hier habe ich beschlossen, Ihnen die Funktion zu zeigen, die das Datei -Upload in admin.js übernimmt:
talefileUpload (req) {<br> if (! req.files ||! req.files.picture ||! req.files.picture.name) {<br> return req.body.currentpicture || "";<br> }<br> const data = fs.readFilesync (req.files.picture.Path);<br> const Dateiname = req.files.picture.name;<br> const uid = crypto.randombytes (10) .ToString ("hex");<br> const Dir = __dirname "/../public/uploads/" uid;<br> fs.mkdirsync (Dir, "0777");<br> fs.WriteFilesync (Dir "/" Dateiname, Daten);<br> Rückgabe "/Uploads/" UID "/" Dateiname;<br> }<br>
Wenn eine Datei übermittelt wird, wird das Knotenskript req.files.picture. Im obigen Code -Snippet, ReadFileSync, WriteFilesync.
Die harte Arbeit ist jetzt abgeschlossen. Das Verwaltungsgremium arbeitet und wir haben ein Zuhause und vier Datensätze mit einer Art von DB -Objekt im Blog, rufen jedoch anders /blog /: id String. Diese Route entspricht URLs wie req.params.id. Mit anderen Worten, wir können dynamische Parameter definieren. In unserem Fall ist das die ID des Datensatzes. Sobald wir diese Informationen haben, können wir für jeden Artikel eine eindeutige Seite erstellen.
Der zweite interessante Teil ist, wie ich die Dienstleistungen, Karrieren und Kontaktseiten aufgebaut habe. Es ist klar, dass sie nur einen Datensatz aus der Datenbank verwenden. Wenn wir für jede Seite einen anderen Controller erstellen müssten, müssten wir denselben Code kopieren/einfügen und den Befehl type in seinem npm -Installationsbefehl ändern, um die neuen Abhängigkeiten (falls vorhanden) zu installieren.
Denken Sie daran, dass der Knoten immer noch ziemlich jung ist, so dass nicht alles wie Sie erwartet haben können, aber es werden ständig Verbesserungen vorgenommen. Zum Beispiel garantiert sich für immer, dass Ihr Node.js -Programm kontinuierlich ausgeführt wird. Sie können dies tun, indem Sie den folgenden Befehl ausgeben:
Starten Sie für immer Ihre Anapp.js<br>
Das benutze ich auch auf meinen Servern. Es ist ein schönes kleines Werkzeug, aber es löst ein großes Problem. Wenn Sie Ihre App mit nur für immer ausführen, starten Sie die Anwendung einfach neu.
Jetzt bin ich kein Systemadministrator, aber ich wollte meine Erfahrung mit der Integration von Knoten -Apps mit Apache oder Nginx teilen, da ich denke, dass dies irgendwie ein Teil des Entwicklungsworkflows ist.
Wie Sie wissen, wird Apache normalerweise auf Port 80 ausgeführt. Wenn Sie http: // localhost: 80 öffnen, sehen Sie eine Seite, die von Ihrem Apache -Server bedient wird, und wahrscheinlich hört Ihr Knotenskript einen anderen Port an. Sie müssen also einen virtuellen Host hinzufügen, der die Anfragen akzeptiert und an den richtigen Port sendet. Nehmen wir beispielsweise an, ich möchte die Site hosten, die wir gerade auf meinem lokalen Apache -Server unter der Hosts -Datei aufgebaut haben.
127.0.0.1 ExpressCompleteWebsite.dev<br>
Danach müssen wir die HTTPD-Vhosts.conf -Datei im APache-Konfigurationsverzeichnis bearbeiten und hinzufügen:
# ExpressCompletewebsite.dev<br> <virtualhost><br> ServerName ExpressCompleteWebsite.dev<br> Serveralias www.expressCompleteWebSite.dev<br> ProxyRequests ab<br> <proxy><br> Bestellen leugnen, erlauben<br> Von allen erlauben<br> </proxy><br> <position></position><br> Proxypass http: // localhost: 3000/<br> Proxypassreverse http: // localhost: 3000/<br> <br> </virtualhost><br>
Der Server akzeptiert immer noch Anfragen auf Port 80, leitet sie jedoch an Port 3000 weiter, wobei der Knoten zuhört.
Das Nginx-Setup ist viel einfacher und um ehrlich zu sein, ist es eine bessere Wahl für die Hosting von node.js-basierten Apps. Sie müssen den Domänennamen in Ihrer Hosts -Datei weiterhin hinzufügen. Erstellen Sie danach einfach eine neue Datei im Verzeichnis /Sites-fähigen Verzeichnis unter der NGINX-Installation. Der Inhalt der Datei würde ungefähr so aussehen:
Server {<br> Hören Sie 80;<br> server_name expressCompletewebsite.dev<br> Standort / {<br> proxy_pass http://127.0.0.1:3000;<br> proxy_set_header host $ http_host;<br> }<br> }<br>
Denken Sie daran, dass Sie mit den oben genannten Hosts -Setups nicht sowohl Apache als auch Nginx ausführen können. Dies liegt daran, dass beide Port 80 benötigen. Außerdem möchten Sie möglicherweise ein wenig zusätzliche Nachforschungen über eine bessere Serverkonfiguration durchführen, wenn Sie vorhaben, die oben genannten Code -Snippets in einer Produktionsumgebung zu verwenden. Wie gesagt, ich bin kein Experte in diesem Bereich.
Express ist ein großartiger Rahmen, der Ihnen einen guten Ausgangspunkt für den Aufbau Ihrer Anwendungen bietet. Wie Sie sehen können, ist es eine Frage der Wahl darüber, wie Sie es erweitern und was Sie verwenden werden, um damit aufzubauen. Es vereinfacht die langweiligen Aufgaben, indem es einige großartige Middleware verwendet und dem Entwickler die lustigen Teile überlässt.
Dieser Beitrag wurde mit Beiträgen von Jacob Jackson aktualisiert. Jacob ist Webentwickler, technischer Schriftsteller, Freiberufler und Open-Source-Mitwirkende.
Das obige ist der detaillierte Inhalt vonErstellen Sie eine vollständige MVC -Website mit Express. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!