Файл: Модель клиент-сервер (Применение модели «клиент-сервер» в разработке web-приложения).pdf
Добавлен: 26.04.2023
Просмотров: 484
Скачиваний: 1
СОДЕРЖАНИЕ
1.1. Понятие клиента и сервера
1.1.3 Понятие архитектуры клиент-сервер
1.2 Двухуровневая архитектура клиент-сервер
1.3. Многоуровневая архитектура клиент-сервер
1.4. Преимущества и недостатки
2. Применение модели «клиент-сервер» в разработке web-приложения
2.2. Алгоритм реализации решения
2.3. Описание необходимых элементов проекта
2.4. Создание и настройка проекта
2.5. Реализация серверной части
2.6. Реализация клиентской части
- GET /widget/show – инициализирует виджет на странице, на которой установлен
- POST /widget/get – возвращает данные виджета в виде заполненного шаблона
- GET /widget/delete – удаляет виджет из базы даных
Для отображения контента страниц и виджета нам понадобятся pug html шаблоны, они содержат:
- layout.pug – общие для всех страниц элементы
- main.pug –элементы главной страницы
- addwidget.pug – форма добавления виджета
- editwidget.pug – форма редактирования виджета
- widgetlistitem.pug – элемент списка виджета на для главной страницы
- widgetview.pug – содержит заголовок контента виджета
- widgetviewday.pug – содержит контент виджета с погодой за один день
Для стилизации html тегов интерфейса будем использовать библиотеку bootstrap, также нам понадобится файл widget.css для реализации нужного отображения элементов виджета.
2.4. Создание и настройка проекта
Для начала работы на компьютере должны быть установлены nodejs и redis для веб-сервера и базы данных соответственно. Для начала создадим папку weatherapp. Запустим терминал и перейдем в эту папку. Затем инициализируем проект через npm и добавим в него зависимости:
mkdir weatherapp
cd weatherapp
npm init
…
npm install express body-parser request pug redis
В созданном package.json в поле scripts добавим строку, указывающую на основной скрипт сервера, а также создадим и сам этот файл:
// выдержка из package.json:
"scripts": {
"start": "node app" // необходимая строка
}
// команда для создания файла app.js
touch app.js
Наконец, создадим папку для шаблонов и публичных файлов веб-сервера (мы разместим в ней css стили):
mkdir views
mkdir public
Итак, мы закончили с настройкой проекта. У нас есть файл основного скрипта сервера, необходимые библиотеки для работы и нужная структура папок. Теперь можно запустить в новом окне терминала сервер redis с помощью команды redis-server и перейти к написанию кода серверной части нашего приложения.
2.5. Реализация серверной части
Откроем app.js (программный код вынесен в приложение А) и перейдем к написанию серверной части приложения. Первым делом сделаем импорт зависимостей. Затем объявим константы: учетные данные api погодного сервиса, города для которых будет возможность получать погоду в виджетах, временные диапазоны прогнозов, имена месяцев и порт, на котором будет запущено приложение.
Теперь создадим переменную app веб-сервера express и настроим его: добавим возможность парсинга body приходящих запросов, установим движок pug для работы шаблонов страниц, откроем доступ к директории public, откроем прослушивание на выбранном порту. Затем создадим в переменной client подключение к хранилищу Redis. Мы получили минимально настроенное express веб-приложение – переходим к написанию функциональной части.
Напишем функцию addUpdateWidget которая получает данные виджета на вход и сохраняет их (с заменой при совпадении id) в базе данных Redis при помощи client. Мы задействуем эту функцию для создания и редактирования виджетов далее.
Перейдём к написанию обработчиков запросов. Начнём с «GET /» запроса. В его обработчике установим тип контента, получим из базы данные виджетов. Если виджеты найдены, отрендерим их с помощью шаблона widgetlistitem.pug и отправим ответ, используя шаблон main.pug. Если виджеты не найдены, отправим ответ, используя шаблон main.pug с соответствующим сообщением.
Для «GET /widget/add» установим тип контента и отправим ответ, используя шаблон addwidget.pug, содержащий форму создания виджета.
Для «POST /widget/add» и «POST /widget/edit» выполним функцию addUpdateWidget и сделаем редирект на главную страницу.
Для «GET /widget/edit» установим тип контента, проверим есть ли в базе виджет с указанным в параметрах запроса id. Если виджет не найден, отправим в ответ соответствующее сообщение. Если найден ¬отправим ответ, используя шаблон editwidget.pug и данные виджета, полученные из базы.
Для «GET /widget/show» установим тип контента и в качестве ответа вернём js-код, который дожидается загрузки библиотеки jQuery после чего добавляет на страницу файл стилей виджетов widget.css, затем получает данные виджета и отображает их на странице в соответствующем блоке.
Для «POST /widget/get» установим тип контента, затем проверим есть ли в базе виджет с указанным в параметрах запроса id. Если виджет не найден, отправим в ответ соответствующее сообщение. Если найден, то пробуем получить ответ от api сервиса прогнозов погоды. В случае ошибки получения данных погоды отправим в ответ сообщение о том, что запросы на получение погоды отправляются слишком часто и порекомендуем повторить попытку позже. Если же данные погоды пришли, то вернем ответ, используя шаблоны widgetviewday.pug и widgetview.pug.
Для «GET /widget/delete» удалим виджет с указанным в параметрах запроса id и сделаем редирект на главную страницу. На этом запросе завершаем работу над серверной частью приложения и переходим к клиентской.
2.6. Реализация клиентской части
В данном разделе мы будем работать в директориях public и views, в которых будут размещены файл стилей widget.css и файлы html шаблонов соответственно. Листинг файлов приведён в приложении Б. Для начала добавим в widget.css отступы и стиль отображения для css-класса card, чтобы элементы виджета (день с данными о погоде) могли выстраиваться в горизонтальный ряд.
Перейдём к написанию html шаблонов. В коде будем по возможности использовать синтаксис шаблонизатора pug. Для стиллизации будем использовать классы библиотеки bootstrap.
В файле layout.pug объявим теги html, head и body, добавим link на файл стилей bootstrap, теги script со ссылками на скрипты jquery и bootstrap, а главное – тег h1 для вывода в него заголовка и блок content для содержимого страниц.
В файле main.pug унаследуем шаблон layout.pug, в блоке content разместим ссылку на страницу добавления виджета и тег div для вывода списка виджетов. Также можно разместить полученный в приложении код виджета для встраивания, чтобы проверить его работу.
В файле addwidget.pug унаследуем шаблон layout.pug, в блоке content разместим ссылку на главную страницу и форму добавления виджета с полями id (уникальный идентификатор), name (название), city (город), days (количество дней, за которые будет отображаться прогноз), type (тип отображения – вертикальный или горизонтальный).
В файле editwidget.pug унаследуем шаблон layout.pug, в блоке content разместим ссылку на главную страницу и форму редактирования виджета с полями, аналогичными заданным в форме добавления.
В файле widgetlistitem.pug разместим тег h5 состоящий из названия виджета, его типа и количества дней за которые будет отображаться прогноз. Ниже разместим код для встраивания, ссылки на редактирование и удаление.
В файле widgetview.pug разместим тег h4 состоящий из слов «Weather in» и названия города для которого будет показан прогноз погоды. Ниже разместим тег div с классом, который зависит от type виджета и кодом прогноза погоды по дням.
В файле widgetviewday.pug разместим данные о погоде за один день: день недели, описание состояния погоды, температура ночью и днём. На этом шаблоне мы заканчиваем разработку клиентской части приложения и можем приступить к проверке и запуску.
2.7. Проверка и запуск решения
Для проверки решения на соответствие задаче необходимо произвести запуск приложения. Для этого воспользуемся приведенной ниже инструкцией (система macOS с установленным пакетным менеджером brew).
1. Установить сервер nodejs: `brew install node`
2. Устновить и запустить redis: `brew install redis` для установки, `redis-server` для запуска
3. Установить зависимости проекта через npm: `npm install`
4. Запустить проект и открыть в браузере: `npm start` для запуска, `http://localhost:3033` страница приложения в браузере
Рис. 2.7.1. Главная страница, база виджетов пуста
Откроем главную страницу, убедимся, что у нас есть кнопка «Add widget» для добавления виджетов. Нажимаем на неё.
Рис. 2.7.2. Страница добавления виджета
Должна открыться страница с формой добавления виджета. Вводим в ней данные виджета: «Widget ID» – 1, «Name» – MSC, «City» – Moscow, «Days count» – 3 days, «View type» – Horizontal. Нажимаем кнопку «Save».
Рис. 2.7.3. Главная страница, в базе есть виджет
Мы видим главную страницу, но теперь на ней виден список созданных виджетов, а также установленный виджет с id=1. Нажмём на кнопку «EDIT».
Рис. 2.7.4. Страница редактирования виджета
На экране отображена страница редактирования виджета, данные введены в поля и доступны для изменения.
Проверив все описанные в задаче механики работы приложения, мы убедились что получившееся решение в большинстве требований соответствует поставленной задаче и функционирует как и было задумано. Выбранные технологии позволили выполнить задачу быстро и просто. В дальнейшем можно улучшить приложение: исправить недочёты в коде программы, оптимизировать запросы и кешировать в базе данные полученные от api сервиса прогноза погоды.
ЗАКЛЮЧЕНИЕ
Подробное рассмотрение модели «клиент-сервер» в теоретическом и практическом ключе в этой работе даёт уверенное понимание работы приложений, реализующих такую архитектуру.
Получившееся в результате разработки приложение успешно использует модель «клиент-сервер» и позволяет создавать виджеты с прогнозом погоды, которые можно встраивать в любой сайт. Разработка подобных решений даёт уверенное понимание веб-разработки с использованием браузеров-клиентов и приложения-сервера.
Клиенты в виде веб-браузеров, мобильных приложений и серверы в виде компьютеров установленных в дата-центрах в настоящее время составляют большую часть сети Интернет также обширно используют многоуровневую модель клиент-серверной архитектуры. Работа различных процессов, таких как обработка изображений и видео, хранение и передача текстовых сообщений, аудио и видео трансляции и многих других, обеспечивается группами специализированных серверов: файловые хранилища, кеш-хранилища, базы данных, поисковые движки и другие виды. Данная модель несомненно будет применяться в будущем, развиваться и решать сложные проблемы работы с данными, передаваемыми между ноутбуками, пк, планшетами, смартфонами, устройствами «Умного дома», самолетами, самоуправляемыми автомобилями и многими другими, в том числе совершенно новыми.
Список использованных источников
- Д.Васкевич Стратегии клиент/сервер. Диалектика, Киев, 1997.
- Валерий Коржов. Многоуровневые системы клиент-сервер. (https://www.osp.ru/nets/1997/06/142618/) Издательство Открытые системы (17 июня 1997)
- http://www.glossary.ru
- http://www.nadprof.ru/school/client-server.shtml
- http://mrivkin.narod.ru/Publ/CLSERV1.htm
- http://www.intuit.ru/department/se/crosspl/1/2.html
Приложение А
app.js
const express = require('express');
const bodyParser = require('body-parser');
const pug = require('pug');
const path = require('path');
const redis = require('redis');
const request = require("request");
// Config OpenWeatherMap
const openWeatherMapApiKey = 'bbf89a2ce7b2d36edaac82d86df3ca8e';
const openWeatherMapApiUri = 'http://api.openweathermap.org/data/2.5/forecast/daily?';
// Setup widget options values
const cityNames = {
"524901": "Moscow",
"498817": "Saint Petersburg",
"520555": "Nizhniy Novgorod"
}
const forecastDurations = {
1: "Today",
3: "3 days",
7: "Week"
}
const widgetTypes = {
"vertical": "Vertical",
"horizontal": "Horizontal"
}
var monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
// Create redis client
let client = redis.createClient();
client.on('connect', function () {
console.log('Connected to Redis ...');
})
// Set port
const port = 3033;
// Init app
const app = express();
// body parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// View engine
app.set('view engine', 'pug')
// Add public static files access
app.use(express.static(__dirname + '/public'));
app.listen(port, function () {
console.log('Server started on port ' + port);
})
// add/update widget function
function addUpdateWidget(req,callback) {
let id = 'widget_'+req.body.id;
let name = req.body.name;
let city = req.body.city;
let days = req.body.days;
let type = req.body.type;
client.hmset(id, [
'name', name,
'city', city,
'city_name', cityNames[city],
'days', days,
'type', type
], function (err, reply) {
if (err) {
console.log(err);
}
callback(reply);
})
}
app.get('/', function (req, res) {
res.set('Content-Type', 'text/html');
var widgets = [];
var widgets_code = "";
// get widgets ids SCAN 0 MATCH widget* COUNT 100
client.scan(0, 'MATCH', 'widget*', 'COUNT', 100, function (err, reply) {
if (err) {
res.render('main', { title: 'Widget app', message: 'Manage widgets', error: err })
}
if(reply[1].length>0) {
// loop to render them
let promise = new Promise(function (resolve) {
reply[1].map(function (val, index, arr) {
client.hgetall(val, function (err, obj) {
obj.id = val;
pug.renderFile('views/widgetlistitem.pug', {
id: obj.id,
name: obj.name,
city: cityNames[obj.city],
type: obj.type,
days: obj.days,
code: '<div id="weatherwidget_'+obj.id+'"></div><script type="text/javascript" src="http://localhost:3033/widget/show?id='+obj.id+'"></script>'
}, function (empty, res) {
widgets += obj;
widgets_code += res;
if (index == arr.length - 1) resolve(true);
});
});
});
});
promise
.then(function (value) {
res.render('main', { title: 'Widget app', message: 'Manage widgets', widgets_code: widgets_code });
return "widgets load success"