Розширюємо можливості маршрутизації в ExpressJS
Зазвичай такі функції як маршрутизація запитів, контроль доступу та журналювання дій користувачів використовуються як окремі незалежні модулі. Але простота архітектури API та мінімалізм фреймворку ExpressJS дають нам можливість легко об’єднати ці модулі та навіть автоматизувати їх.
Для початку створимо шаблон контролера з основними діями в файлі user.js
та збережемо його у теку controllers
. Зараз він потрібен лише для налагодження маршрутизації. Але пізніше його необхідно буде заповнити відповідним реальним кодом.
export default { list: async (request, response) => { // ... response.send('user.list'); }, read: async (request, response) => { // ... response.send('user.read'); }, create: async (request, response) => { // ... response.send('user.create'); }, update: async (request, response) => { // ... response.send('user.update'); }, delete: async (request, response) => { // ... response.send('user.delete'); } }
Наступним створимо файл routes.js
, де перелічимо наявні в нас контролери у вигляді масиву об’єктів з певними властивостями, їх можливі дії та додаткові налаштування. Я наведу для прикладу лише фрагмент цього файлу з описом одного контролера user
. Для пояснення цього цілком достатньо, адже в API контролери дуже подібні між собою.
export default [ // ... { name: 'user', path: '/users', actions: [ { name: 'list', path: '', method: 'get', level: 5 }, { name: 'read', path: '/:id', method: 'get', level: 4 }, { name: 'create', path: '', method: 'post', level: 2, log: true }, { name: 'update', path: '/:id', method: 'put', level: 2, log: true }, { name: 'delete', path: '/:id', method: 'delete', level: 2, log: true } ] }, // ... ]
Властивості name
, path
, method
містять всі необхідні дані для здійснення маршрутизації запитів. Властивість level
містить число — мінімальний рівень доступу, необхідний для виконання конкретної дії контролера. В цьому випадку використовуються рівні від 1 (найвищий, «Адміністратор») до 5 (найнижчий, «Читач»). Такий простий підхід дає можливість легко створити ієрархію ролей користувачів та суттєво автоматизувати контроль доступу на їх основі. Наявність властивості log
вказує на необхідність журналювання даної дії користувача.
Для перевірки доступу користувача на проєкті вже повинна використовуватись своя система на кшталт jwt
або щось подібне. Опис її використання не входить в мету даної статті. Ми лише автоматизуємо перевірку доступу на її основі та об’єднаємо разом з маршрутизацією для зручності.
const router = express.Router(); for (const route of routes) { const controller = (await import( `./controllers/${route.name}.js` )).default; for (const action of route.actions) { const middleware = []; if (action?.level) { // verify user authentication by token (middleware) // check user authorization by access level (middleware) } if (action?.log) { // logging controller action data (middleware) } middleware.push(controller[action.name]); router[action.method](route.path + action.path, middleware); } } app.use('/api', router);
Сам алгоритм маршрутизації надзвичайно простий: він перебирає елементи масиву налаштувань контролерів та їх дій з описаного раніше файлу routes.js
, створює на їх основі маршрути для запитів, записуючи у відповідний об’єкт Router фреймворку ExpressJS. Також, залежно від інших властивостей дій контролера, можуть додаватись відповідні проміжні функції (middleware
).
Якщо виконання дії контролера дозволено тільки для автентифікованого користувача, тоді додається відповідна верифікація. Під час неї отримуються дані користувача, включно з його рівнем доступу, які потрібно тимчасово зберегти на час виконання запиту для подальшого використання. Додатково, при необхідності, здійснюється перевірка рівня доступу ролі користувача.
Для журналювання дій користувачів необхідно зберегти дані цього конкретного запиту: дата операції, назви контролера та його дії, ідентифікатор користувача тощо. Ну і звичайно не забуваємо інтегрувати виконання самої дії контролера. В кінці циклу додаємо створений та налаштований нами маршрут у маршрутизатор зі зазначенням шляху для поточної дії контролера.
А зрештою додаємо і сам маршрутизатор у фреймворк згідно з документацією з визначеним базовим шляхом для всього API.
Як ви переконались при створенні API на ExpressJS можливо спростити та автоматизувати доволі велику кількість рутинних операцій. Звісно, даний підхід не розв’язує всі проблеми з авторизацією чи логуванням, але переважну більшість з них. В інших поодиноких випадках все ж таки доведеться писати додатковий код вручну індивідуально для конкретної дії контролера.
Немає коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів