Heim > Web-Frontend > js-Tutorial > Hauptteil

Eine einfache Beispielanalyse zur Verwendung von Node.js zur Implementierung des MVC-Frameworks

黄舟
Freigeben: 2017-08-07 11:51:08
Original
1569 Leute haben es durchsucht

Der folgende Editor bringt Ihnen einen Artikel darüber, wie Sie ein einfaches MVC-Framework mit Node.js implementieren. Der Herausgeber findet es ziemlich gut, deshalb werde ich es jetzt mit Ihnen teilen und es allen als Referenz geben. Folgen wir dem Editor und werfen wir einen Blick darauf.

Im Artikel Erstellen eines statischen Ressourcenservers mit Node.js haben wir die Verarbeitung statischer Ressourcenanforderungen durch den Server abgeschlossen, jedoch keine dynamischen Anforderungen. Derzeit ist dies nicht möglich basierend auf dem Kunden ausgegeben und personalisierte Inhalte für verschiedene Anfragen zurückgeben. Statische Ressourcen allein können diese komplexen Website-Anwendungen nicht unterstützen. In diesem Artikel wird erläutert, wie Sie mit <span style="font-family:NSimsun">Node</span> dynamische Anforderungen verarbeiten und ein einfaches MVC-Framework erstellen. Da im vorherigen Artikel ausführlich beschrieben wurde, wie auf statische Ressourcenanforderungen reagiert werden soll, werden in diesem Artikel alle statischen Teile übersprungen.

Ein einfaches Beispiel

Beginnen Sie mit einem einfachen Beispiel, um zu verstehen, wie dynamische Inhalte an den Client in Node zurückgegeben werden.

Angenommen, wir haben eine solche Anforderung:

Gibt Schauspieler zurück, wenn der Benutzer auf die Liste <span style="font-family:NSimsun">/actors<code><span style="font-family:NSimsun">/actors</span> zugreift Seite

Wenn der Benutzer <span style="font-family:NSimsun">/actresses<code><span style="font-family:NSimsun">/actresses</span> besucht, um zur Liste der Schauspielerinnen zurückzukehren

kann das verwenden Folgende Code-Vervollständigungsfunktion:


const http = require(&#39;http&#39;);
const url = require(&#39;url&#39;);

http.createServer((req, res) => {
  const pathName = url.parse(req.url).pathname;
  if ([&#39;/actors&#39;, &#39;/actresses&#39;].includes(pathName)) {
    res.writeHead(200, {
      &#39;Content-Type&#39;: &#39;text/html&#39;
    });
    const actors = [&#39;Leonardo DiCaprio&#39;, &#39;Brad Pitt&#39;, &#39;Johnny Depp&#39;];
    const actresses = [&#39;Jennifer Aniston&#39;, &#39;Scarlett Johansson&#39;, &#39;Kate Winslet&#39;];
    let lists = [];
    if (pathName === &#39;/actors&#39;) {
      lists = actors;
    } else {
      lists = actresses;
    }

    const content = lists.reduce((template, item, index) => {
      return template + `<p>No.${index+1} ${item}</p>`;
    }, `<h1>${pathName.slice(1)}</h1>`);
    res.end(content);
  } else {
    res.writeHead(Eine einfache Beispielanalyse zur Verwendung von Node.js zur Implementierung des MVC-Frameworks);
    res.end(&#39;<h1>Requested page not found.</h1>&#39;)
  }
}).listen(9527);
Nach dem Login kopieren

Der Kern des obigen Codes ist der Routenabgleich. Überprüfen Sie, ob eine logische Verarbeitung vorhanden ist, die ihrem Pfad entspricht Die Anfrage stimmt nicht überein. Für jede Route wird Eine einfache Beispielanalyse zur Verwendung von Node.js zur Implementierung des MVC-Frameworks zurückgegeben. Bei erfolgreichem Match wird die entsprechende Logik abgearbeitet.

simple request

Der obige Code ist offensichtlich nicht universell und es gibt nur zwei Routenanpassungskandidaten (und die Anforderungsmethode wurde nicht unterschieden), die Datenbank und die Vorlagendateien jedoch nicht Unter der Prämisse ist der Code bereits etwas verworren. Als nächstes erstellen wir ein einfaches MVC-Framework, um Daten, Modelle und Leistung zu trennen, und jedes kann seine eigenen Aufgaben ausführen.

Erstellen Sie ein einfaches MVC-Framework

MVC bezieht sich jeweils auf:

M: Modell (Daten)

V: Ansicht (Leistung)

C: Controller (Logik)

In Node ist der Prozess der Verarbeitung von Anforderungen unter der MVC-Architektur wie folgt:

Anfrageeingang Der Server

Der Server übergibt die Anfrage an das Routing

Das Routing passt den Pfad an und leitet die Anfrage an den entsprechenden Controller weiter

Der Controller empfängt die Anfrage und bittet das Modell um die Daten

Modell gibt die erforderlichen Daten an den Controller zurück

Controller muss möglicherweise eine Neuverarbeitung der empfangenen Daten durchführen

Controller übergibt die verarbeiteten Daten Daten zum Anzeigen

Ansicht generiert Antwortinhalte basierend auf Daten und Vorlagen

Der Server gibt diesen Inhalt an den Client zurück

Auf dieser Grundlage müssen wir die folgenden Module vorbereiten:

Server: Anfragen überwachen und beantworten

Router: Weiterleiten der Anfrage an den richtigen Controller zur Verarbeitung

Controller: Geschäftslogik ausführen, Daten aus dem Modell abrufen und übergeben es der Ansicht hinzufügen

Modell: Daten bereitstellen

Ansicht: HTML bereitstellen

Erstellen Sie das folgende Verzeichnis:


-- server.js
-- lib
  -- router.js
-- views
-- controllers
-- models
Nach dem Login kopieren

Server

Server.js-Datei erstellen:


const http = require(&#39;http&#39;);
const router = require(&#39;./lib/router&#39;)();

router.get(&#39;/actors&#39;, (req, res) => {
  res.end(&#39;Leonardo DiCaprio, Brad Pitt, Johnny Depp&#39;);
});

http.createServer(router).listen(9527, err => {
  if (err) {
    console.error(err);
    console.info(&#39;Failed to start server&#39;);
  } else {
    console.info(`Server started`);
  }
});
Nach dem Login kopieren

Unabhängig von den Details in dieser Datei ist der Router Das Modul, das unten vervollständigt wird, wird hier zuerst vorgestellt und nach Eintreffen der Anfrage bearbeitet.

Router-Modul

Das Router-Modul muss eigentlich nur eines erledigen, nämlich die Anfrage zur Verarbeitung an den richtigen Controller weiterleiten.


const router = require(&#39;./lib/router&#39;)();
const actorsController = require(&#39;./controllers/actors&#39;);

router.use((req, res, next) => {
  console.info(&#39;New request arrived&#39;);
  next()
});

router.get(&#39;/actors&#39;, (req, res) => {
  actorsController.fetchList();
});

router.post(&#39;/actors/:name&#39;, (req, res) => {
  actorsController.createNewActor();
});
Nach dem Login kopieren

Im Allgemeinen hoffen wir, dass es sowohl Routing-Middleware als auch Nicht-Middleware unterstützt. Nachdem die Anfrage eingegangen ist, übergibt der Router sie an die entsprechende Middleware Verarbeitung. Middleware ist eine Funktion, die auf Anforderungsobjekte und Antwortobjekte zugreifen kann:

Beliebigen Code ausführen, z. B. Protokolle hinzufügen und Fehler behandeln usw.

Ändern Anfrage ( req) und Antwortobjekt (res) rufen beispielsweise die Abfrageparameter von req.url ab und weisen sie req.query zu

Antwort beenden

Aufruf die nächste Middleware (next )

Hinweis:

Es ist zu beachten, dass die Kontrolle übergeben wird, wenn die Antwort weder beendet wird noch die nächste Methode in einer Middleware aufgerufen wird Weiter zur nächsten Middleware, die Anfrage bleibt hängen

__Nicht-Routing-Middleware__ wird auf folgende Weise hinzugefügt und passt zu allen Anfragen:


router.use(fn);
Nach dem Login kopieren

Zum Beispiel im obigen Beispiel:


router.use((req, res, next) => {
  console.info(&#39;New request arrived&#39;);
  next()
});
Nach dem Login kopieren

__routing middleware__ wird auf folgende Weise hinzugefügt, um der Anforderungsmethode und dem Pfad genau zu entsprechen:


router.HTTP_METHOD(path, fn)
Nach dem Login kopieren

Nachdem Sie es geklärt haben, schreiben Sie zuerst das Framework:

/lib/router.js


const METHODS = [&#39;GET&#39;, &#39;POST&#39;, &#39;PUT&#39;, &#39;DELETE&#39;, &#39;HEAD&#39;, &#39;OPTIONS&#39;];

module.exports = () => {
  const routes = [];

  const router = (req, res) => {
    
  };

  router.use = (fn) => {
    routes.push({
      method: null,
      path: null,
      handler: fn
    });
  };

  METHODS.forEach(item => {
    const method = item.toLowerCase();
    router[method] = (path, fn) => {
      routes.push({
        method,
        path,
        handler: fn
      });
    };
  });
};
Nach dem Login kopieren

Das Obige fügt dem Router hauptsächlich Use-, Get-, Post- und andere Methoden hinzu. Immer wenn diese Methoden aufgerufen werden, wird den Routen eine Routenregel hinzugefügt.

Hinweis:

Eine Funktion in Javascript ist ein spezielles Objekt, das aufgerufen werden kann und auch Eigenschaften und Methoden haben kann.

Der nächste Fokus liegt auf der Router-Funktion. Was sie tun muss, ist:

Methode und Pfadnamen abrufen

aus dem req-Objekt

依据 method、pathname 将请求与routes数组内各个 route 按它们被添加的顺序依次匹配

如果与某个route匹配成功,执行 route.handler,执行完后与下一个 route 匹配或结束流程 (后面详述)

如果匹配不成功,继续与下一个 route 匹配,重复3、4步骤


 const router = (req, res) => {
    const pathname = decodeURI(url.parse(req.url).pathname);
    const method = req.method.toLowerCase();
    let i = 0;

    const next = () => {
      route = routes[i++];
      if (!route) return;
      const routeForAllRequest = !route.method && !route.path;
      if (routeForAllRequest || (route.method === method && pathname === route.path)) {
        route.handler(req, res, next);
      } else {
        next();
      }
    }

    next();
  };
Nach dem Login kopieren

对于非路由中间件,直接调用其 handler。对于路由中间件,只有请求方法和路径都匹配成功时,才调用其 handler。当没有匹配上的 route 时,直接与下一个route继续匹配。

需要注意的是,在某条 route 匹配成功的情况下,执行完其 handler 之后,还会不会再接着与下个 route 匹配,就要看开发者在其 handler 内有没有主动调用 next() 交出控制权了。

在__server.js__中添加一些route:


router.use((req, res, next) => {
  console.info(&#39;New request arrived&#39;);
  next()
});

router.get('/actors', (req, res) => {
  res.end('Leonardo DiCaprio, Brad Pitt, Johnny Depp');
});

router.get('/actresses', (req, res) => {
  res.end('Jennifer Aniston, Scarlett Johansson, Kate Winslet');
});

router.use((req, res, next) => {
  res.statusCode = Eine einfache Beispielanalyse zur Verwendung von Node.js zur Implementierung des MVC-Frameworks;
  res.end();
});
Nach dem Login kopieren

每个请求抵达时,首先打印出一条 log,接着匹配其他route。当匹配上 actors 或 actresses 的 get 请求时,直接发回演员名字,并不需要继续匹配其他 route。如果都没匹配上,返回 Eine einfache Beispielanalyse zur Verwendung von Node.js zur Implementierung des MVC-Frameworks。

在浏览器中依次访问 http://localhost:9527/erwe、http://localhost:9527/actors、http://localhost:9527/actresses 测试一下:

Eine einfache Beispielanalyse zur Verwendung von Node.js zur Implementierung des MVC-Frameworks

<span style="font-family:NSimsun">network</span> 中观察到的结果符合预期,同时后台命令行中也打印出了三条 <span style="font-family:NSimsun">New request arrived</span>语句。

接下来继续改进 router 模块。

首先添加一个 router.all 方法,调用它即意味着为所有请求方法都添加了一条 route:


router.all = (path, fn) => {
    METHODS.forEach(item => {
      const method = item.toLowerCase();
      router[method](path, fn);
    })
  };
Nach dem Login kopieren

接着,添加错误处理。

/lib/router.js


const defaultErrorHander = (err, req, res) => {
  res.statusCode = 500;
  res.end();
};

module.exports = (errorHander) => {
  const routes = [];

  const router = (req, res) => {
      ...
    errorHander = errorHander || defaultErrorHander;

    const next = (err) => {
      if (err) return errorHander(err, req, res);
      ...
    }

    next();
  };
Nach dem Login kopieren

server.js


...
const router = require(&#39;./lib/router&#39;)((err, req, res) => {
  console.error(err);
  res.statusCode = 500;
  res.end(err.stack);
});
...
Nach dem Login kopieren

默认情况下,遇到错误时会返回 500,但开发者使用 router 模块时可以传入自己的错误处理函数将其替代。

修改一下代码,测试是否能正确执行错误处理:


router.use((req, res, next) => {
  console.info(&#39;New request arrived&#39;);
  next(new Error(&#39;an error&#39;));
});
Nach dem Login kopieren

这样任何请求都应该返回 500:

error stack

继续,修改 route.path 与 pathname 的匹配规则。现在我们认为只有当两字符串相等时才让匹配通过,这没有考虑到 url 中包含路径参数的情况,比如:

localhost:9527/actors/Leonardo
Nach dem Login kopieren

router.get(&#39;/actors/:name&#39;, someRouteHandler);
Nach dem Login kopieren

这条route应该匹配成功才是。

新增一个函数用来将字符串类型的 route.path 转换成正则对象,并存入 route.pattern:


const getRoutePattern = pathname => {
 pathname = &#39;^&#39; + pathname.replace(/(\:\w+)/g, &#39;\(\[a-zA-Z0-9-\]\+\\s\)&#39;) + &#39;$&#39;;
 return new RegExp(pathname);
};
Nach dem Login kopieren

这样就可以匹配上带有路径参数的url了,并将这些路径参数存入 req.params 对象:


    const matchedResults = pathname.match(route.pattern);
    if (route.method === method && matchedResults) {
      addParamsToRequest(req, route.path, matchedResults);
      route.handler(req, res, next);
    } else {
      next();
    }
Nach dem Login kopieren


const addParamsToRequest = (req, routePath, matchedResults) => {
  req.params = {};
  let urlParameterNames = routePath.match(/:(\w+)/g);
  if (urlParameterNames) {
    for (let i=0; i < urlParameterNames.length; i++) {
      req.params[urlParameterNames[i].slice(1)] = matchedResults[i + 1];
    }
  }
}
Nach dem Login kopieren

添加个 route 测试一下:


router.get(&#39;/actors/:year/:country&#39;, (req, res) => {
  res.end(`year: ${req.params.year} country: ${req.params.country}`);
});
Nach dem Login kopieren

访问<span style="font-family:NSimsun">http://localhost:9527/actors/1990/China</span>试试:

url parameters

router 模块就写到此,至于查询参数的格式化以及获取请求主体,比较琐碎就不试验了,需要可以直接使用 bordy-parser 等模块。

现在我们已经创建好了router模块,接下来将 route handler 内的业务逻辑都转移到 controller 中去。

修改__server.js__,引入 controller:


...
const actorsController = require(&#39;./controllers/actors&#39;);
...
router.get(&#39;/actors&#39;, (req, res) => {
  actorsController.getList(req, res);
});

router.get(&#39;/actors/:name&#39;, (req, res) => {
  actorsController.getActorByName(req, res);
});

router.get(&#39;/actors/:year/:country&#39;, (req, res) => {
  actorsController.getActorsByYearAndCountry(req, res);
});
...
Nach dem Login kopieren

新建__controllers/actors.js__:


const actorsTemplate = require(&#39;../views/actors-list&#39;);
const actorsModel = require(&#39;../models/actors&#39;);

exports.getList = (req, res) => {
  const data = actorsModel.getList();
  const htmlStr = actorsTemplate.build(data);
  res.writeHead(200, {
    &#39;Content-Type&#39;: &#39;text/html&#39;
  });
  res.end(htmlStr);
};

exports.getActorByName = (req, res) => {
  const data = actorsModel.getActorByName(req.params.name);
  const htmlStr = actorsTemplate.build(data);
  res.writeHead(200, {
    &#39;Content-Type&#39;: &#39;text/html&#39;
  });
  res.end(htmlStr);
};

exports.getActorsByYearAndCountry = (req, res) => {
  const data = actorsModel.getActorsByYearAndCountry(req.params.year, req.params.country);
  const htmlStr = actorsTemplate.build(data);
  res.writeHead(200, {
    &#39;Content-Type&#39;: &#39;text/html&#39;
  });
  res.end(htmlStr);
};
Nach dem Login kopieren

在 controller 中同时引入了 view 和 model, 其充当了这二者间的粘合剂。回顾下 controller 的任务:

controller 收到请求,向 model 索要数据
model 给 controller 返回其所需数据
controller 可能需要对收到的数据做一些再加工
controller 将处理好的数据交给 view

在此 controller 中,我们将调用 model 模块的方法获取演员列表,接着将数据交给 view,交由 view 生成呈现出演员列表页的 html 字符串。最后将此字符串返回给客户端,在浏览器中呈现列表。

从 model 中获取数据

通常 model 是需要跟数据库交互来获取数据的,这里我们就简化一下,将数据存放在一个 json 文件中。

/models/test-data.json


[
  {
    "name": "Leonardo DiCaprio",
    "birth year": 1974,
    "country": "US",
    "movies": ["Titanic", "The Revenant", "Inception"]
  },
  {
    "name": "Brad Pitt",
    "birth year": 1963,
    "country": "US",
    "movies": ["Fight Club", "Inglourious Basterd", "Mr. & Mrs. Smith"]
  },
  {
    "name": "Johnny Depp",
    "birth year": 1963,
    "country": "US",
    "movies": ["Edward Scissorhands", "Black Mass", "The Lone Ranger"]
  }
]
Nach dem Login kopieren

接着就可以在 model 中定义一些方法来访问这些数据。

models/actors.js


const actors = require(&#39;./test-data&#39;);

exports.getList = () => actors;

exports.getActorByName = (name) => actors.filter(actor => {
  return actor.name == name;
});

exports.getActorsByYearAndCountry = (year, country) => actors.filter(actor => {
  return actor["birth year"] == year && actor.country == country;
});
Nach dem Login kopieren

当 controller 从 model 中取得想要的数据后,下一步就轮到 view 发光发热了。view 层通常都会用到模板引擎,如 dust 等。同样为了简化,这里采用简单替换模板中占位符的方式获取 html,渲染得非常有限,粗略理解过程即可。

创建 /views/actors-list.js:


const actorTemplate = `
<h1>{name}</h1>
<p><em>Born: </em>{contry}, {year}</p>
<ul>{movies}</ul>
`;

exports.build = list => {
  let content = &#39;&#39;;
  list.forEach(actor => {
    content += actorTemplate.replace(&#39;{name}&#39;, actor.name)
          .replace(&#39;{contry}&#39;, actor.country)
          .replace(&#39;{year}&#39;, actor["birth year"])
          .replace(&#39;{movies}&#39;, actor.movies.reduce((moviesHTML, movieName) => {
            return moviesHTML + `<li>${movieName}</li>`
          }, &#39;&#39;));
  });
  return content;
};
Nach dem Login kopieren

在浏览器中测试一下:

test mvc

至此,就大功告成啦!

Das obige ist der detaillierte Inhalt vonEine einfache Beispielanalyse zur Verwendung von Node.js zur Implementierung des MVC-Frameworks. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage