×

Обробка POST запиту в окремому файлі Nodejs+express

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Доброго дня.

Допоможіть мені трішки, я справді перерив інтернет і не можу знайти відповідь. Пишу для себе пет-проект на Nodejs+express. Сайт з новинами, коментарями і все таке, все по стандарту. Вже зробив шаблон, формочки входу, додавання коментарів, вивід новин з БД і внесення новин в БД. Але всі форми у мене обробляються в app.js. Виглядає все якось так.

У мене є певна форма, вона міститься в файлі з розширенням ejs.

<form action="/form" method="POST">
    <input type="text" name="first_name"/>
    <input type="submit" value="Send"/>
</form>

Код котрий її обробляє, знаходиться в файлі app.js

app.post('/form', function(req, res, next){
    код обробки форми, котрий заносить дані в БД
});

Все чудово працює.

Якщо таких форм буде 20-30, код app.js буде просто великим. Як би винести код форми в окремий файл.
Спасибі велике за увагу.

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Ще хочу спитати, така проблема була і раніше. Результат обробки запиту і те що було занесену в БД, я можу побачити зупинивши мій сайт і знову запустивши. Просто оновити сторінку не допомагає. Як це вирішити? Що пошукати і що почитати? Буду дуже вдячний за допомогу.

БД у вас яка, MySQL? Без коду вставки даних в БД важко сказати «що не так».

Так, MySQL.
файл js сторінки з формою і виводом коментарів.


var express = require('express');
var router = express.Router();
var sql = require('mysql');
var con = sql.createConnection({
    тут дані про базу
});
var text = '';
con.query('SELECT * FROM Comments', function (err, rows, next) {
    if (err) throw err;
    for (i = 0; i < rows.length; i++) {
        text += '<div id=\"page_container\">'+
            '<div id="page_content">'+ rows[i].comment +
            '</div>'+
            '</div>'
    }
});
router.get('/', function(req, res, next) {
    res.render('comments', {
        title: 'site title', com: text
    });
});
module.exports = router;
ejs файл шаблону цієї сторінки, не весь звісно, а кусок з формою та виводом існуючих коментарів.

<form id="coments"  action="/formHandler" method="POST" >

                    <label><Br>
                        <textarea name="comment" cols="72" rows="10"></textarea></label><br>
                    <label><input type="submit" name="submit" value="Відправити"  id="send">
                        <input type="reset" value="Очистити" id="reset"></label>
            </form>
                <%- com %>
                </div>
            </div>
вміст файла обробника форми

var sql = require('mysql');
var con = sql.createConnection({
     тут дані про базу
});
con.connect();
module.exports.formHandler = function(req, res, next) {
    var message  = {id_Source: 1, id_User :'1', comment: req.body.comment};
    var query = con.query('INSERT INTO Comments SET ?', message, function(err, result) {
    });
    con.end();
    console.log(query.sql);
    res.redirect(301, 'http://localhost:3000/comments');
};
частинка app.js, підключення обробника форми

var handlers = require('formHandler');
app.post('/formHandler', handlers.formHandler);
Спасибі Вам, за те що приділяєте увагу.

Справа в тому, що Node.js не працює так як PHP — не перечитує за кожним запитом увесь ланцюжок директив. За кожним запитом обробляються лише так звані middleware рівня застосунку, або рівня роутинга, почитайте документацію з цього приводу.

В даному випадку краще зробити спеціальну функцію, яка буде спрацьовувати при кожному запиті роутера:

function getText()
{
  var text = '';

  con.query('SELECT * FROM Comments', function (err, rows, next)
  {
    if (err) throw err;

    for (i = 0; i < rows.length; i++)
    {
      text += `
      <div id="page_container">
        <div id="page_content">
          ${rows[i].comment}
        </div>
      </div>`
    }
  });
}

router.get('/', function(req, res, next)
{
  res.render
  (
    comments,
    {
      title: 'site title',
      com: getText()
    }
  );
});

P.S. І зверніть увагу на лапки, в які я взяв текст, — node.js вже розуміє `отакі лапки`, забудьте про багаторядкову конкатенацію, типу ’текст ’ + ’ще якийсь текст’.

У мене, тепер просто коментарі не виводяться. Спочатку писало comments undefined — матюкається на те, що знаходиться в рес.рендер. Я додав лапки, але таким чином взагалі не виводяться коментарі, але працює сторінка і не призводить до помилки. До речі, те що вноситься в title — спрацьовує, також коментар додається. Перевіряв відкатом змін і перзапуском.

Так, як і написав нижче Артем, я і сам не врахував асинхронність (теж покищо лише вчусь писати під Node.js).

По-ідеї, змінювати тут не потрібно багато чого. Тут я двічі використав middleware router.get(’/’). Результат виконання передав у змінну res, тобто у змінну відповіді, після чого ловлю цей результат в наступному middleware.

Спробуйте, тепер здається повинно працювати.

router.get('/', function (req, res, next)
{
  var text = '';

  con.query('SELECT * FROM Comments', function (err, rows, next)
  {
    if (err) throw err;

    for (i = 0; i < rows.length; i++)
    {
      text += `
      <div id="page_container">
        <div id="page_content">
          ${rows[i].comment}
        </div>
      </div>`
    }
    res.comments = text;
    next();
  });
});

router.get('/', function(req, res, next)
{
  res.render
  (
    comments,
    {
      title: 'site title',
      com: res.comments
    }
  );
});
if (err) throw err;
А что, в этом месте next вызывать не нужно? Запрос просто повиснет?
router.get(’/’, function(req, res, next)
{
res.render
(
comments,
{
title: ’site title’,
com: res.comments
}
);
});
тут next вызывать тоже не нужно?

Я покищо працював лише із Restify, його плагіни-middleware викликають next() самостійно, я подумав що і у Express так само. Тим більше — цей код я не змінював, автор каже що у нього виводиться інфа.

Стосовно

if (err) throw err;
автор модуля MySQL наводить приклад коду, де немає next();
github.com/...ysqljs/mysql#introduction

Ну ок, а кто вызывает next когда вы бросаете тут исключение?

if (err) throw err;
router.get(’/’, function(req, res, next)
{
res.render
(
comments,
{
title: ’site title’,
com: res.comments
}
);
});
Взагалі, якщо писати comments, а не ’comments’, видає помилку про те, що такої змінної немає. Потрібно як стрічку писати, щоб воно почало брати файл шаблону comments.ejs. В котрому стоять мітки <%- title %> <%- com %>.
Я починаю думати, що ця технологія для тривіальних задач типу сайт візитка чи щось в цьому роді — геть не підходить. Так наче просто не підходящий інструмент. Але розбираюсь далі, поки відклав цю задачу з перечитування ще раз файлу або запиту в БД на вибірку. Є ще ціла купа речей котрих потрібно осягнути.

А, так — може бути, то вже деталі які уточнюються «на місці». Я просто не працюю з в’юхами (видаю відповідь у вигляді JSON).

В попередньому коді я забув ще й закрити MySQL-з’єднання. Може вже це працюватиме:

router.get('/', function (req, res, next)
{
  var text = '';

  var con = sql.createConnection({
    // тут дані про базу
  });
  
  con.query('SELECT * FROM Comments', function (err, rows, fields)
  {
    if (err) throw err;

    for (i = 0; i < rows.length; i++)
    {
      text += `
      <div id="page_container">
        <div id="page_content">
          ${rows[i].comment}
        </div>
      </div>`
    }
    res.comments = text;
    next();
  });

  con.end();
});

router.get('/', function(req, res, next)
{
  res.render
  (
    'comments',
    {
      title: 'site title',
      com: res.comments
    }
  );
});
con.query(’SELECT * FROM Comments’, function (err, rows, next)
{
if (err) throw err;
Таки что, бросаем исклчюение, которое никто обрабатывать не будет, а с висящим запросом что делаем?

Артем, якщо автор модуля MySQL для Node.js не дописує next(), значить він передає таку err, яка сама прибиває завислі запити.

Я про HTTP-запрос говорю, который вы обрабатываете. Модуль mysql не знает и не должен знать о том, какие http запросы сейчас выполняются и что с ними делать в случае ошибки.

Ви маєте на увазі треба зробити так?

if (err)
{
  next();
  throw err;
}

Мені здається директива next() не повинна викликатись тут. В мене в цьому плані мало досвіду, думаю що правильним завершенням запиту повинен займатись все ж обробник помилок.

Запустите код и скажите, что происходит, если при выполнении SQL происходит ошибка.

У Restify, MySQL-помилка видається у відповіді клієнту (без додаткового next() у тілі обробки запиту ).

Я вам просто безмежно вдячний, ось, воно працює ;-) Порадьте, будь ласка, ресурси чи книги по котрим ви вивчаєте ноду.

А можна побачити, як би це написав Senior JavaScript Developer. Тільки не подумайте, що я пишу заради жарту. Я дійсно був би дуже радий, побачити як пишуть це JSсівці зі стажем. А то я, як і Костя Третяк, мислю по PHPшному

не, я тут код писать не буду, я его и так много пишу в других местах

Ви про такий код говорите (щоправда — це TypeScript, але думаю це не суттєво)?

import {MysqlConnectService} from '../services/mysql-connect-service';
import {Inject, annotate} from 'di';

interface IUser
{
  id:number
  ,userName:string
  ,userEmail:string
}

export class UsersModel
{
  constructor(private connect:MysqlConnectService){}

  getConnection()
  {
    return this.connect.getConnection();
  }

  getListUsers = ():Promise<IUser[]> =>
  {
    let connection = this.getConnection();
      
    return new Promise( (resolve, reject) =>
    {
      connection.query('select id, userName, userEmail from users', (err, rows, fields) =>
      {
        if(err)
          reject(err);
        else
          resolve(rows);
      });

      connection.end();
    })
  }
}

annotate(UsersModel, new Inject(MysqlConnectService));

Потому что это PHP, написанный на JS

Ну я догадувався, що ви очікували Promise в коді, але схоже що для автора теми вивчення промісів ще попереду.

Такий код схвалюєте? dou.ua/...orums/topic/17844/#949210

І ще одне — ви не правильно очікуєте next у запиті до БД

con.query('SELECT * FROM Comments', function (err, rows, next)
Правильно буде
con.query('SELECT * FROM Comments', function (err, rows, fields)
Справа в тому, що Node.js не працює так як PHP — не перечитує за кожним запитом увесь ланцюжок директив. За кожним запитом обробляються лише так звані middleware рівня застосунку, або рівня роутинга, почитайте документацію з цього приводу.
В даному випадку краще зробити спеціальну функцію, яка буде спрацьовувати при кожному запиті роутера:
      text += `
      <div id="page_container">
        <div id="page_content">
          ${rows[i].comment}
        </div>
      </div>`

Может лучше написать на PHP?

Я, особисто, радий писати на PHP, і з задоволенням продовжував писати на ньому. Але річ у тім, що керівництво вважає, що node.js, це гіпермегасуперкрута офігіти яка потужна мегаасинхронна штука, котра дозволяє робити все на світі. А нею потрібно клепати сайти, а PHP рівносильно Воланд де Морту, в тому сенсі, що ім’я згадувати не можна ;-) Ось і я переучуюсь і намагаюсь освоїти.

мегаасинхронна штука
Из-за того, что это мегаасинхронная штука, этот код ничего и не делает.

Вызывается getText, который асинхронно запускает SQL запрос и не возвращает никакого результата (undefined). Этот undefined кладется в объект, который отправляется на рендеринг.

После этого SQL запрос завершает выполнение, идет считывание результатов и собирание их в строку (вот тут возникает вопрос на чем все-таки пишет автор — на ноде или на PHP), но этот результат уже никуда не попадает.

Смысл, вынести роутер и контроллеры в отдельные файлы

Эээх... Новички уже превращают ДОУ в форум для себя :( Таких форумов есть полно. В частности по javascript, и около него
forum.jscourse.com

Можливо причина в тому, що новачки сприймають жителів цього форму, як мудрих і всезнаючих людей?))

Ви так написали, наче можете чітко означити тематику ДОУ. Яка вона? — Як ми створили Стартап? — Сири по 500 грн.? Чи може “куда бы свалить”? Або “как найти девушку Java-джуниору”?

У мене все видавало помилки(( Хоча інші просто файли в зв’язці ejs+js правильно роутив. В результаті рішення по виносу в окремий файл обробки форми, за межі app.js було зроблене створенням модуля. Навчився як їх робити.
файл модуля.
var sql = require(’mysql’);
var con = sql.createConnection({
host: «localhost»,
user: «user»,
password: «password»,
database: «a3b»
});
module.exports.formHandler = function(req, res, next) {
var message = {id_Source: 1, id_User :’1′, comment: req.body.comment};
var query = con.query(’INSERT INTO Comments SET ?’, message, function(err, result) {
});
console.log(query.sql);
res.redirect(301, ’http://localhost:4006′);
};

Ще хочу спитати, така проблема була і раніше. Результат обробки запиту і те що було занесену в БД, я можу побачити зупинивши мій сайт і знову запустивши. Просто оновити не допомагає. Як це вирішити? Що пошукати і що почитати?Буду дуже вдячний за допомогу.

почему он должен быть большим? Почему вы для 30 форм будете использовать 1 action. Это как минимум странно
Во-вторых, там не может быть много кода, потому что большая часть логики уходит в модели, layouts, blocks кароче размазывается ровным словом говна по всем паттернам

Для 30ти форм (число умовне) не один action, а різні. Просто подумав, що пхати всі обробники в головний файл не добре. Можу помилятись.

Цікаво чи використовували ви генератор коду. Там же показано як організувати і структуру роутінгу:

├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade

7 directories, 9 files

Чи вам таке не підходить?

Підходить, у мене правда не джейд, а єджиес. Але то в даному прикладі ролі не грає. Спасибі за наведення, з згенерованим кодом тримав відповіді на багато питань.

express.Router в помощь.
Вынеси функции обработки запросов в отдельные файлы, а в app.js используй их.
var form = require('./routes/form'); ... app.use('/form', form);

expressjs.com/en/guide/routing.html

Вы можете вынести функции обработки разных запросов в отдельный файл и require-ить их в app.js, тогда у вас app.js по сути будет роутингом, и будет что-то вроде:
const formHandler = require('...'); app.post('/form', formHandler);

можно попробовать еще import если es6 поддерживается в проекте

Node v6.2.2 пока import не поддерживает. import это вообще es7 по-моему, в es-6 он только представлен.

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

Спасибі за відповідь. Тобто вирізаю все що було раніше в app.post(’/form’, function(req, res, next){
код обробки форми, котрий заносить дані в БД
}); обрамляю в файлі все це в функцію і підключаю через require як і інші файли мого проекту?

Да, но выносить можно не тело функции и вызывать ее внутри callback-а, а саму функцию, и передавать ее как callback вторым параметром в app.post. Также обратите внимание на комментарий Костя Третяк, возможно он натолкнет на правильную структуру приложения в целом.

Підписатись на коментарі