Микрофреймворки, такие как hapi.js, являются одним из многих предпочтительных вариантов для потребностей серверной части корпоративного уровня. Первоначально он был разработан Walmart для масштабирования своего приложения во время распродажи Черная пятница.

Микрофреймворки предпочтительны в микросервисной архитектуре. По очевидным причинам, то есть они легкие и могут быть легко масштабируемы.

Чтобы узнать больше о hapi.js нажмите здесь.

Чтобы понять, как работает hapi.js. Мы создадим простое приложение для сокращения URL-адресов и узнаем о нем больше в процессе.

Итак, давайте начнем.

Если у вас есть навыки работы с любым микрофреймворком, таким как flask или express, это будет плюсом. Даже если вы никогда не работали ни с одним микрофреймворком, не волнуйтесь. Я прикрыл тебя. 😉

Важно. Убедитесь, что в вашей системе установлена ​​версия nodejs 12.*+, npm, MongoDB.

Создать каталог

mkdir url-shortener
cd url-shortener

Инициализируйте каталог с помощью package.json, запустив

npm init

Установить зависимости

npm i @hapi/hapi @hapi/vision ejs mongoose shortid --save

После установки зависимостей ваш package.json dependencies должен выглядеть примерно так.

package.json

"dependencies": {
"@hapi/hapi": "^19.1.1",
"@hapi/vision": "^6.0.0",
"ejs": "^3.0.1",
"mongoose": "^5.9.2",
"shortid": "^2.2.15"
}

**Если у вас возникли проблемы с запуском приложения, установите версию, указанную выше**

Пройдемся по зависимостям.

«@hapi/hapi» устанавливает hapi.js.

«@hapi/vision» будет использоваться для запуска механизмов рендеринга шаблонов. Он может обрабатывать практически все механизмы рендеринга, такие как ejs, pug.js, handlebar.js.

«ejs» — это наш движок рендеринга шаблонов. Как механизм рендеринга, он помогает нам писать условия и циклы в нашем HTML. Он передает данные со стороны сервера на сторону клиента.

«mongoose», чтобы использовать базу данных MongoDB с нашим приложением.

«shortid» — это небольшая библиотека, которая поможет нам сгенерировать короткую уникальную строку небольшой длины (это будет наш короткий URL-адрес).

Для оформления HTML я использую Semantic-UI. Ничего против бутстрапа, бутстрап проще в использовании. Но я лично нахожу семантический интерфейс более чистым и отзывчивым, чем бутстрап. (таблицы semantic-ui адаптивны, bootstrap не предоставляет адаптивные таблицы)

Давайте просмотрим код и поймем его.

Структура каталогов.

| — app.js
| — models
| — | — shortUrl.js(models/shortUrl.js)
| — views
| — | — index.ejs(views/index.ejs)
| - package.json
| - package-lock.json
| - node_modules

app.js

модели/shortUrl.js

представления/index.ejs

Если вы знакомы с архитектурой Model View Controller (MVC). Структура каталогов будет иметь смысл для вас. Здесь app.js — наш контроллер. «shortUrl.js» — это наша модель для хранения полных и коротких URL-адресов. index.ejs — это наши представления для отображения всех URL-адресов.

Давайте сначала разберемся с приложением, пройдясь по коду. Давайте начнем с понимания модели.

модели/shortUrl.js

const mongoose = require('mongoose');
const shortId = require('shortid')

Требование необходимых зависимостей. Как отмечалось ранее, «мангуст» будет нашим ORM (объектно-реляционным картографом) для связи с базой данных MongoDB, а «shortId» поможет нам создать короткий URL-адрес для длинных URL-адресов.

модели/shortUrl.js

let shortUrlSchema = new mongoose.Schema({
fullUrl: {
type: String,
required: true,
},
shortUrl: {
type: String,
default: shortId.generate
},
count: {
type: Number,
default: 0,
}
});
let ShortUrl = mongoose.model('ShortUrl', shortUrlSchema);
module.exports = ShortUrl

Схема для нашей модели приведена выше. Мы храним fullUrl, shortUrl и count. Здесь count — это количество посещений короткого URL-адреса.

Если вы заметили, параметр по умолчанию для «shortUrl» — «shortId.generate». «shortId.generate», как следует из названия, — это функция, которая генерирует короткую строку для нашего длинного URL-адреса. Каждый раз, когда мы сохраняем длинный URL-адрес в БД, одновременно генерируется и сохраняется в базе данных короткий URL-адрес. Поскольку счетчик имеет значение по умолчанию 0. Всякий раз, когда сохраняется короткий URL-адрес, начальный счетчик устанавливается равным нулю.

app.js

'use strict';
const Hapi = require('@hapi/hapi');
//database connectivity
const mongoose = require('mongoose');
const mongodbURL = "mongodb://localhost/url-shortener";
mongoose.connect(mongodbURL, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => console.log("Connected"));
//model for our application
const ShortUrl = require('./models/shortUrl')
...

Требование необходимых зависимостей, подключение базы данных MongoDB и импорт модели в наше приложение. ShortUrl — это модель, которую мы определили в папке наших моделей. Сюда его импортируют.

app.js

...
const server = Hapi.server({
port: 8000,
});
await server.register(require('@hapi/vision'));
server.views({
engines: {
ejs: require('ejs')
},
relativeTo: __dirname,
path: 'views'
});
...

Здесь мы создаем сервер и запускаем его на порту 8000. Приложение будет видно на http://localhost:8000/

Мы регистрируем «видение» для запуска нашего механизма просмотра «ejs» в нашем приложении.

Мы объявляем «ejs» нашим движком и указываем папку «views» в качестве нашего каталога для хранения файла ejs. Папка относится к «__dirname», где «__dirname» — это путь, по которому запускается приложение.

Поскольку мы упоминаем здесь путь, нам не нужно писать абсолютный путь к нашему файлу представления в нашей функции-обработчике.

app.js

...
server.route({
method: 'GET',
path: '/',
handler: async (request, h) => {
return await h.view('index')
}
});
...

«server.route» обрабатывает маршрутизацию для нас. Когда клиент делает запрос на получение по пути «/» в нашем приложении, именно он называется индексным маршрутом. Это будет целевая страница нашего приложения.

Как только клиент посещает путь, он обрабатывается «обработчиком». Анонимная функция принимает два аргумента. Это помогает в том, как обрабатывать запрос и отвечать на него.

представления/index.ejs

...
<form class="ui form" action="/" method="POST">
<div class="field">
<input type="text" name="full-url" placeholder="Enter URL here">
</div>
<button class="ui primary button" type="submit">Submit</button>
</form>
...

**Игнорируйте классы, это CSS-классы семантического пользовательского интерфейса, используемые для стилизации.

Это создаст форму для нас. Эта форма выполнит POSTзапрос по пути ‘/’, когда мы заполним поле ввода длинным URL-адресом и отправим его.

Давайте посмотрим, как обрабатывать запросы POST в hapi.js.

app.js

...
server.route({
method: 'POST',
path: '/',
handler: async (request, h) => {
let fullUrl = request.payload['full-url'];
await ShortUrl.create({
fullUrl: fullUrl
}, (err) => {
if (err) {
console.log(err)
}
});
return h.redirect('/')
}
});
...

Для обработки запроса POST. Нам нужно указать наш метод как POST и указать его путь. Внутри обработчика мы извлекаем полный URL-адрес и сохраняем его в нашей БД. После сохранения нас перенаправляет на главную страницу.

Чтобы извлечь полный URL-адрес из запроса, мы используем встроенный API полезной нагрузки .

let fullUrl = request.payload['full-url']

Как мы указали, ‘shortId.generate’ используется по умолчанию в models/shortUrl.js . Он создаст для нас короткий URL-адрес, когда мы сохраним длинный URL-адрес в нашей БД.

модели/shortUrl.js

...
shortUrl: {
type: String,
default: shortId.generate
}
...

Далее давайте разберемся с логикой отображения всех URL-адресов вместе с коротким URL-адресом на главной странице.

app.js

...
server.route({
method: 'GET',
path: '/',
handler: async (request, h) => {
let allUrls = await ShortUrl.find()
return await h.view('index', { allUrls: allUrls })
}
});
...

Внесение изменений в обработчик index route ‘/’ приложения.

let allUrls = await ShortUrl.find()

Здесь allUrls возвращает все данные URL-адресов из БД.

return await h.view('index', { allUrls: allUrls })

h.view отображает шаблон, переменная allUrls передается шаблону index.ejs для рендеринга. { allUrls: allUrls} может сбивать с толку, но он передает allUrls как allUrls шаблону index.ejs. Чтобы им можно было манипулировать для рендеринга данных в index.ejs.

представления/index.ejs

<tbody>
<% for(let i = 0; i < allUrls.length; i++){%>
<tr>
<td>
<% if(allUrls[i].fullUrl.length > 50){ %>
<a href="<%= allUrls[i].fullUrl%>" target="_blank"><%= allUrls[i].fullUrl.slice(0,50)+"..."%></a>
<% }else{ %>
<a href="<%= allUrls[i].fullUrl%>" target="_blank"><%= allUrls[i].fullUrl%></a>
<%} %>
</td>
<td>
<a href="<%= allUrls[i].shortUrl%>" target="_blank"><%= allUrls[i].shortUrl%></a>
</td>
<td><%= allUrls[i].count %> </td>
</tr>
<%} %>
</tbody>

Чтобы отобразить все длинные URL-адреса, короткие URL-адреса и количество (количество раз, когда ссылка была нажата). Мы будем перебирать массив ‘allUrls’. Использование тегов шаблона ‘ejs’ упрощает получение необходимых данных из объекта итерируемого массива.

Внутри цикла for можно увидеть блок if else. Этот блок ограничивает длинную строку URL-адреса, чтобы она не отображала более 50 символов.

Теперь мы можем отправить длинный URL-адрес в нашу базу данных и одновременно сгенерировать короткий URL-адрес и сохранить его.

Как мы используем этот короткий URL-адрес для перенаправления?

представления/index.ejs

...
<a href="<%= allUrls[i].shortUrl%>" target="_blank"><%= allUrls[i].shortUrl%></a>
...

Как видите, приведенная выше строка сгенерирует для нас гиперссылку. При нажатии на него будет отправлен GETзапрос «domain.xyz/shortstring».

Чтобы дать ясную картину, давайте рассмотрим пример.

Приведенная выше строка HTML-кода может быть отображена, как показано ниже.

‹a href="ByZ0q6tm» target="_blank"›ByZ0q6tm‹/a›

Где ByZ0q6tm — короткая строка. Итак, когда вы нажимаете на ссылку. Он делает запрос GET «domain.xyz/ByZ0q6tm».

Чтобы обработать этот запрос GET, мы пишем в нашем контроллере следующую логику.

app.js

...
server.route({
method: 'GET',
path: '/{shortUrl}',
handler: async (request, h) => {
let params_url = request.params.shortUrl
const shortUrl = await ShortUrl.findOne({ shortUrl: params_url });
//short url is not found
if(!shortUrl){
return await h.redirect('/')
}
shortUrl.count++;
shortUrl.save()
return await h.redirect(shortUrl.fullUrl)
}
})
...

Здесь мы определяем наш путь как путь: ‘/{shortUrl}’. Это делается для обработки любого запроса GET с URL-адресом «domain.xyz/randomstring».

let params_url = request.params.shortUrl

Здесь короткий URL-адрес извлекается из параметра запроса с помощью встроенного API и сохраняется в переменной «params_url» .

const shortUrl = await ShortUrl.findOne({ shortUrl: params_url });

Здесь мы находим документ по переменной params_url.

Если мы не находим его, мы перенаправляем его на главную страницу. Если мы находим его, мы увеличиваем счетчик на 1, сохраняем его и перенаправляем на полный длинный URL-адрес.

Заключение.

На этом мы завершаем создание нашего приложения для сокращения URL-адресов. Это подводит итог объяснения кода. Надеюсь, мне удалось упростить ситуацию и дать вам обзор этого легкого микрофреймворка «hapi.js».

Загляните в репозиторий Github.

https://github.com/SaurabhPanja/url-shortener-hapi

Вот так выглядит моя версия приложения.

*Ссылка на приложение**



**На создание этого приложения меня вдохновил видеоурок одного из моих любимых инструкторов по JavaScript, Кайла из Web Dev Simplified. Загляните на его канал, если вы занимаетесь веб-разработкой**

Ссылка на упрощенный канал Youtube для веб-разработчиков: https://www.youtube.com/channel/UCFbNIlppjAuEX4znoulh0Cw

Если вы хотите создать такое же приложение в «express.js». Следуйте этому видеоруководству от Кайла.

Спасибо, что дочитали до конца.

Хорошего дня. 😊

Саурабх Панджа.

https://saurabhpanja.com/