5

我在模板中有一个表单,我根据GET请求呈现:

app.get('/register', function (req, res) {
  res.render('register', {
      title: 'Register'
    , twitterDetails: req.session.twitterDetails
    }
  );
});

POST然后我在请求中处理表单:

app.post('/register', function (req, res, next) {
  // Generic validation
  req.assert('name', 'Name is empty').notEmpty();
  req.assert('username', 'Username is empty').notEmpty();
  // Email validation
  req.assert('email', 'Email is invalid.').isEmail();
  req.assert('email', 'Email field is empty').notEmpty();
  // Password validation
  req.assert('password', 'Password too short. Must be 6 characters or more.').len(6);
  req.assert('password', 'Passwords do not match.').is(req.body.confirmPassword);
  req.assert('password', 'Password field is empty').notEmpty();
  req.assert('confirmPassword', 'Confirm password field is empty').notEmpty();
  var errors = req.validationErrors(true);
  if (errors) {
    console.log(errors);
    // What do to if there are errors?
  }
  // If there are no errors, continue handling the form…
});

在这个表单处理程序中,我正在检查express-validator模块的错误。这很好,但我的问题是如果出现错误该怎么办。AFAIK,有两种选择:

  1. 将用户重定向到/registerreq.flash用于将错误传递给下一个请求
  2. 重新渲染模板并将错误直接传递给模板

使用第一个选项,我会冒着让用户远离POST. 我也可以使用他们填写的数据抢占表单req.flash,但是刷新页面时这会消失。

对于第二个选项,我是在重复自己——特别是如果我向模板发送大量变量,这些变量都必须重复。我还需要通过模板传递所有表单数据并用这些值填写表单。

处理这样的表格的正确程序是什么?

4

4 回答 4

8

使用选项 1。这称为POST/Redirect/GET 模式,在任何地方都可以使用。

如果您害怕丢失数据,请使用会话。这就是他们的目的。在会话期间保持状态。

不要忘记 HTTP 是无状态的。

于 2012-12-20T07:54:42.030 回答
2

在我看来,您应该使用选项 2。

POST/Redirect/GET 模式主要适用于用户成功提交并有助于避免重复发布。这并不意味着您也应该将它用于失败的提交

对于失败的提交,通常的做法是不重定向到另一个页面,而是简单地重新呈现带有错误消息的表单。

选项 1 确实略微提高了可用性,因为用户在提交尝试失败后刷新页面时不会重新提交。另一方面,处理起来要复杂得多,因为它需要将表单数据存储在会话或 GET 参数中。

总的来说:虽然选项 1 在可用性方面可能是最好的,但选项 2 仍然是常见的做法,而且更实用。

另请参阅:如何在 Post/Redirect/Get 模式中处理服务器端错误?

于 2014-05-09T10:16:23.120 回答
1

Heres an example:

I add this to the tag at the bottom of the page inside my ./views/layout.ejs:

<script>
var app = window.app || {};
app.req = app.req || {};
app.req.err = <%- JSON.stringify(err) %>;
app.req.q = <%- JSON.stringify(q) %>;
</script>

Example real data from a POST error:

app.req.err = {"email":{"param":"email","msg":"Enter email","value":""}};
app.req.q = {"username":"chovy","email":"","password":"somepassword"};

I have middleware on each POST that does this:

  res.locals.q = req.body;
  res.locals.err = false;

If I encounter an error, then I populate it: ./routes/signup.js:

  req.assert('email', 'Enter email').notEmpty().isEmail();
  req.assert('username', 'Enter username').notEmpty().isAlphanumeric().len(3,20);
  req.assert('password', 'Enter password').notEmpty().notContains(' ').len(5,20);

  res.locals.err = req.validationErrors(true);

Then my global client side js runs this routine on every page load, which basically checks for app.req.err and handles form errors:

app.utils.form.errors();

Here is the function:

app.utils.form.errors = function(err){
    err = err || app.req.err;
    app.utils.form.prefill();
    for ( var e in err ) {
        var $field = $('[name='+e+']'),
        $el = $field.parents('p');

        $el.addClass('err');
        $el.append('<span class="msg">'+err[e].msg+'</span>');
    }
};

Prefill the form from app.req.q:

app.utils.form.prefill = function(){
    for ( var param in app.req.q ) {
        var $field = $('[name='+param+']');
        $field.val(app.req.q[param]);
    }
};

You can see it in action if you go to this signup form and just submit it blank, and then view source: http://wishd.me/signup the javascript is in http://wishd.me/app.js

The trick is leaving enough room for inline error messages on your form fields because all errors will be handled the same regardless of which field it is on. Of course I do have a few exceptions with css that might re-position the inline error message, color the label red, etc.

http://wishd.me/style.css look for .err class

于 2012-12-20T07:50:44.777 回答
1

我正在使用 expressjs4.0 在验证后重新填充表单字段:

router.route('/posts/new')
 .get(function(req, res) {
 res.render('posts/new', new Post({}));
});

下面 res.render 中的第二个参数将在视图中设置一些变量。

res.render('posts/new', new Post({}));

在我看来,我将表单字段设置如下:

...
<input type="text" name="title" value="<%- post.title %>">
<textarea name="article"><%- post.article %></textarea>
...

当您提交此表单时,它应该被您的路由器捕获,如下所示:

router.route('/posts')
  .post(function(req, res) {
    var post = new Post(req.body)
      post.save(function(err) {
       if (err) {
         res.locals.errors = err.errors;
         res.locals.post = post;
         return res.render('posts/new');
       }
      return res.redirect('/posts');
  });
  ...
})

这行代码重置了您视图中的表单域

res.locals.post = post;

我希望有人觉得这很有用;)

于 2015-01-16T16:35:02.023 回答