Паттерн MVC является одним из распространенных паттернов, применяемых в веб-приложениях. В том числе он применяется и в приложениях на Node.js.
Паттерн MVC включает ряд компонентов:
Модели определяют структуру и логику используемых данных
Представления (views) определяет визуальную часть, как данные будут отображаться
Контроллеры обрабатывают входящие http-запросы, используя для обработки модели и представления, и отправляет в ответ клиенту некоторый результат обработки, нередко в виде html-кода.
Система маршрутизация как дополнительный компонент сопоставляет запросы с маршрутами и выбирает для обработки запросов определенный контроллер.
В общем случае, когда к приложению приходит запрос, система маршрутизации выбирает нужный контроллер для обработки запроса. Контроллер обрабатывает запрос. В процессе обработки он может обращаться к данным через модели и для рендеринга ответа использовать представления. Результат обработки контроллера отправляется в ответ клиенту. Нередко ответ представляет html-страницу, которую пользователь видит в своем браузере.
Вкратце рассмотрим, как мы можем применять паттерн MVC в приложениях Node.js.
Контроллеры в паттерне MVC позволяют связать представления и модели и выполняют некоторую логику по обработке запроса.
Поскольку мы будем использовать фреймворк Express, то вначале добавим его пакеты в проект:
npm install express --save
В главном файле приложения определим следующий код:
const express = require("express"); const app = express(); // определяем роутеры const userRouter = express.Router(); // для адресов с "/users" userRouter.use("/create", function (request, response){ response.send("Добавление пользователя"); }); userRouter.use("/", function(request, response){ response.send("Список пользователей"); }); // сопоcтавляем роутер с конечной точкой "/users" app.use("/users", userRouter); // общие обработчики app.get("/about", function (request, response) { response.send("О сайте"); }); app.get("/", function (request, response) { response.send("Главная страница"); }); // обработка ошибки 404 app.use(function (req, res, next) { res.status(404).send("Not Found") }); app.listen(3000);
В приложении определен роутер , который сопоставляется с адресами на "/users", например, "localhost:3000/users/create", а также два общих адреса: главная страница и адресом "/about".
В данном случае обработчики маршрутов в роутерах для простоты отправляют одну строку. Но логика каждого обработчика может быть гораздо больше, занимать множество строк. Кроме того, маршрутов и соответственно их обработчиков может быть определено гораздо больше. Третий момент - в данном случае мы видим, что обработчики группируются: часть маршрутов увязываются с действиями вокруг условных товаров (просмотр списка пользователей или добавления пользователей), а часть маршрутов представляют функции общего характера - главная страница и информация о сайте.
Теперь изменим приложение, вынеся всю логику обработки в контроллеры. Для этого определим в каталоге приложения новую папку, которую назовем controllers. Создадим в этой папке новый файл userController.js со следующим кодом:
exports.addUser = function (request, response){ response.send("Добавление пользователя"); }; exports.getUsers = function(request, response){ response.send("Список пользователей"); };
Далее добавим в папку controllers второй файл homeController.js со следующим кодом:
exports.index = function (request, response) { response.send("Главная страница"); }; exports.about = function (request, response) { response.send("О сайте"); };
Фактически каждый файл представляет отдельный контроллер, который содержит набор функций-обработчиков маршрутов.
Теперь используем эти контроллеры в файле приложения:
const express = require("express"); const app = express(); const userController = require("./controllers/userController.js"); const homeController = require("./controllers/homeController.js"); // определяем Router const userRouter = express.Router(); const homeRouter = express.Router(); // определяем маршруты и их обработчики внутри роутера userRouter userRouter.use("/create", userController.addUser); userRouter.use("/", userController.getUsers); app.use("/users", userRouter); // определяем маршруты и их обработчики внутри роутера homeRouter homeRouter.get("/about", homeController.about); homeRouter.get("/", homeController.index); app.use("/", homeRouter); app.use(function (req, res, next) { res.status(404).send("Not Found") }); app.listen(3000, ()=>console.log("Сервер запущен и ожидает подключения..."));
Контроллеры подключаются как стандартные модули, и затем функции контроллеров используются для обработки маршрутов. Кроме того, для упрощения логической организации маршруты, которые обрабатываются контроллером homeController, организованы в отдельный роутер - homeRouter.
Вдесь следует отметить, что мы можем добавлять обработку маршрутов с помощью разных методов, так, в примере выше применяются методы use
и get
:
userRouter.use("/create", userController.addUser); homeRouter.get("/about", homeController.about);
В данном случае это не принципиально, можно и в первой строке использовать метод get()
:
userRouter.get("/create", userController.addUser); homeRouter.get("/about", homeController.about);
Просто надо учитывать тип запросов, который будут обрабатываться по этому маршруту. Так, метод get() ограничивается только get-запросами.
Однако в данном случае опять же стоит отметить, что внутри каждого роутера может быть определено множество маршрутов. В этом случае для упрощения управления маршрутами каждый роутер организуется и подключается в виде отдельного модуля. Для этого добавим в проект новую папку routes. Затем в этой папке создадим новый файл homeRouter.js и определим в нем следующий код:
const express = require("express"); const homeController = require("../controllers/homeController.js"); const homeRouter = express.Router(); homeRouter.get("/about", homeController.about); homeRouter.get("/", homeController.index); module.exports = homeRouter;
По сути здесь определен весь связанный с роутером homeRouter код. Также добавим в папку routes новый файл userRouter.js, в который добавим код для второго роутера:
const express = require("express"); const userController = require("../controllers/userController.js"); const userRouter = express.Router(); userRouter.use("/create", userController.addUser); userRouter.use("/", userController.getUsers); module.exports = userRouter;
После этого весь проект будет выглядеть следующим образом:
Теперь изменим файл приложения:
const express = require("express"); const app = express(); const userRouter = require("./routes/userRouter.js"); const homeRouter = require("./routes/homeRouter.js"); app.use("/users", userRouter);; app.use("/", homeRouter); app.use(function (req, res, next) { res.status(404).send("Not Found") }); app.listen(3000, ()=>console.log("Сервер запущен и ожидает подключения..."));
Таким образом, за счет выноса логики обработки маршрутов и организации маршрутов в роутеры в отдельные модули общий код приложения стал проще и яснее.