dom xss验证_在DOM之外思考:组合验证器和数据收集

dom xss验证

这个迷你系列的第1部分中 ,我们讨论了许多JavaScript代码库共有的问题:紧密耦合的代码。 然后,我向您介绍了分离正交关注点的好处。 作为概念证明,我们开始了表单验证系统的开发,该系统不限于表单,甚至可以完全在DOM之外工作。

在第二部分和最后一部分中,我们将讨论组成的验证器,如何从表单收集数据以及如何报告错误。 最后,我将为您提供一个指向GitHub存储库的链接,其中包含此微型系列中开发的所有代码。

组合验证器

在上一篇文章中,我们开发了一种用于验证各个字段的系统。 一次用一个规则逐一验证字段是好事,但很多情况下还需要更多考虑。 您可以使用一个非常长的正则表达式来验证电子邮件地址,但是这样做只能使您告知用户该电子邮件是否可接受。 更好的方法是分别验证电子邮件地址的多个部分,并提供针对性的电子邮件验证错误。

当前的设计可以做到这一点:

var rules = [
  pattern('email', /@/, 'Your email is missing an @'),
  pattern('email', /^\S+@/, 'Please enter the username in your email address',
  // ...
];

尽管这将起作用,但可能会为该电子邮件地址生成多个错误消息。 它还要求我们为具有电子邮件语义的每个字段手动重复每个步骤。 即使我们还没有讨论错误消息的呈现,最好还是以只显示第一个违反规则的结果的方式对多个验证器进行分组的抽象。 事实证明,这是&&运算符的确切语义。 输入and验证者。 该验证器将多个验证器作为其参数,并将其全部应用,直到找到失败的验证器:

function and() {
  var rules = arguments;

  return function (data) {
    var result, l = rules.length;

    for (var i = 0; i < l; ++i) {
      result = rules[i](data);
      if (result) {
        return result;
      }
    }
  };
}

现在,我们可以用一种方式来表示我们的电子邮件验证器,以便一次只弹出一条错误消息:

var rules = [and(
  pattern('email', /@/, 'Your email is missing an @'),
  pattern('email', /^\S+@/, 'Please enter the username in your email address',
  // ...
)];

然后可以将其编码为单独的验证器:

function email(id, messages) {
  return and(
    pattern('email', /@/, messages.missingAt),
    pattern('email', /^\S+@/, messages.missingUser)
    // ...
  );
}

当我们讨论电子邮件地址时,人们经常犯的一个错误是在我们的国家顶级域名(例如“…@hotmail.no”)中键入Hotmail和Gmail地址。 能够在发生这种情况时提醒用户会非常有帮助。 用不同的措词表达:有时我们只想在满足某些条件时才执行某些检查。 为了解决这个问题,我们将介绍when函数:

function when(pred, rule) {
  return function (data) {
    if (pred(data)) {
      return rule(data);
    }
  };
}

如您所见, when是验证器,就像required一样。 您可以使用谓词(一个将接收要验证的数据的函数)和一个验证器来调用它。 如果谓词函数返回true ,则我们评估验证器。 否则, when被认为是成功的。

我们需要解决Hotmail难题的谓词是一个检查值是否与模式匹配的谓词:

function matches(id, re) {
  return function (data) {
    return re.test(data[id]);
  };
}

除了不是验证器之外,这与我们的pattern验证器非常接近。 还值得一提的是,这些功能中的大多数是很小的,以及组合在一起时(而不是单独使用时)它们真正发光的方式。 在这最后一个难题中,我们可以创建一个对最终用户真正有用的电子邮件验证器:

function email(id, messages) {
  return and(
    pattern(id, /@/, messages.missingAt),
    pattern(id, /^\S+@/, messages.missingUser),
    pattern(id, /@\S+$/, messages.missingDomain),
    pattern(id, /@\S+\.\S+$/, messages.missingTLD),
    when(matches(id, /@hotmail\.[^\.]+$/),
      pattern(id, /@hotmail\.com$/, messages.almostHotmail)
    ),
    when(matches(id, /@gmail\.[^\.]+$/),
      pattern(id, /@gmail\.com$/, messages.almostGmail)
    )
  );
}

可以这样使用:

email('email', {
  missingAt: 'Missing @',
  missingUser: 'You need something in front of the @',
  missingDomain: 'You need something after the @',
  missingTLD: 'Did you forget .com or something similar?',
  almostHotmail: 'Did you mean hotmail<strong>.com</strong>?',
  almostGmail: 'Did you mean gmail<strong>.com</strong>?'
});

如果您想使用此功能,我为您创建了一个CodePen

提取数据

现在我们可以验证数据了,我们还需要从表单中获取数据,以解决表单验证的最初问题。 基本上,我们需要改变这个:

<form action="/doit" novalidate>
  <label for="email">
    Email
    <input type="email" name="email" id="email" value="christian@cjohansen.no">
  </label>
  <label for="password">
    Password
    <input type="password" name="password" id="password">
  </label>
  <label class="faded hide-lt-pad">
    <input type="checkbox" name="remember" value="1" checked>
    Remember me
  </label>
  <button type="submit">Login</button>
</form>

变成这个:

{
  email: 'christian@cjohansen.no',
  password: '',
  remember: '1'
}

在测试中逐步实现此功能相当简单,但是将需要DOM元素。 以下是这些测试的示例:

describe('extractData', function () {
  it('fetches data out of a form', function () {
    var form = document.createElement('form');
    var input = document.createElement('input');
    input.type = 'text';
    input.name = 'phoneNumber';
    input.value = '+47 998 87 766';
    form.appendChild(input);

    assert.deepEqual(extractData(form), {'phoneNumber': '+47 998 87 766'});
  });
});

这还不是很糟糕,并且通过另一个小的抽象,我们可以将其收紧一些:

it('fetches data out of a form', function () {
  var form = document.createElement('form');
  addElement(
    form,
    'input',
    {type: 'text', name: 'phoneNumber', value: '+47 998 87 766'}
  );

  assert.deepEqual(extractData(form), {'phoneNumber': '+47 998 87 766'});
});

要提取数据,只需选择表单中的所有inputselecttextarea元素,然后提取其name属性和其当前值即可。 为了从复选框和单选按钮提取正确的值,需要进行一些特殊的处理。 主要功能如下:

function extractData(form) {
  return getInputs(form).reduce(function (data, el) {
    var val = getValue[el.tagName.toLowerCase()](el);
    if (val) { data[el.name] = val.trim(); }
    return data;
  }, {});
};

从该代码片段中可以看到, extractData()函数依赖于getInputs()函数。 此支持功能的目的是获得作为参数传递形式的DOM元素数组。 在本文中,我将不介绍它,因为该函数依赖于其他小的函数,并且我想避免Inception效应。 但是,如果您想进一步挖掘,可以看一下我创建的GitHub存储库 ,其中包含上一期和本期的所有文件。

免费学习PHP!

全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。

原价$ 11.95 您的完全免费

现在让我们看一下如何报告错误。

错误报告

为了报告错误,我们可以设计一个接受错误形式和错误数组的函数。 但是,有一个挑战需要解决:为了避免DOM中出现重复错误,该函数要么需要保持状态,否则它就知道它已经呈现了什么错误,或者需要假设当表单中的每个错误都可以清除时,呈现新的集合。 哪种解决方案合适取决于您的特定用例。

我不会深入介绍渲染实现的细节,但是建议使用以下简化的解决方案:

function renderErrors(form, errors) {
  removeErrors(form);
  errors.forEach(function (error) {
    renderError(form, error);
  });
}

要呈现错误,我们找到它所涉及的输入,并在其之前插入一个元素。 我们只渲染第一个错误。 这是一个非常基本的渲染策略,但效果很好:

function renderError(form, error) {
  var input = form.querySelector("[name=" + error.id + "]");
  var el = document.createElement("div");
  el.className = "error js-validation-error";
  el.innerHTML = error.messages[0];
  input.parentNode.insertBefore(el, input);
}

在上面的代码中,您可以看到我为该元素分配了两个类: errorjs-validation-error 。 前者仅用于样式目的。 后者旨在作为内部机制,由以下removeErrors()函数使用:

function removeErrors(form) {
  var errors = form.querySelectorAll(".js-validation-error");

  for (var i = 0, l = errors.length; i < l; ++i) {
    errors[i].parentNode.removeChild(errors[i]);
  }
}

这个CodePen展示了我们在本节中构建的错误报告系统的基本演示。

一起布线

现在,我们拥有了所有内容(的一个版本):从DOM读取,验证纯数据并将验证结果呈现回DOM。 现在我们需要的是一个高级界面,将它们绑定在一起:

validateForm(myForm, [
  required("login", "Please choose a login"),
  email("email", i18n.validation.emailFormat),
  confirmation("password", "password-confirmation", "Passwords don't match")
], {
  success: function (e) {
    alert("Congratulations, it's all correct!");
  }
});

与渲染一样,这种高层布线既可以是愚蠢的,也可以是复杂的。 在此代码大部分源自的项目中, validateForm()函数将在用户首次尝试提交表单之前不执行验证。 如果存在验证错误,它将进入一种“智能实时验证模式”:已修复的错误将尽快消除(例如,在keyup ),但是新错误只会在blur上添加。 该模型在即时反馈和na之间达到了很好的平衡(没有人喜欢在输入完全之前就听到“您的电子邮件不正确”)。

现在,我已经完成了最后一部分的描述,我邀请您看一下GitHub存储库中包含的演示。 它包括我们已经讨论过的所有代码,以及充实的测试用例。

结论

该模型的优势在于如何将外部输入/输出机制与“规则”实现完全脱钩,而这实际上是库的核心。 该模型可以轻松地用于其他类型的数据验证。 规则引擎也可以扩展为包括有关成功更正错误的信息(例如,通过返回类似{id: 'name', ok: true} ,或具有更多详细信息),以在成功完成的元素旁边添加绿色复选标记。 允许规则引擎处理异步操作也许也很有意义。

最后两个组件,渲染器和validateForm()函数包含通常将各种验证库区分开的功能。 将更多的工作投入到使它们更加灵活,甚至提供替代的实现以在应用程序的不同部分或跨应用程序中使用将是微不足道的。 这意味着包含所有验证逻辑的引擎可以保持非常稳定,需要频繁更改的代码越少,引入新错误的机会就越少。

翻译自: https://www.sitepoint.com/thinking-outside-dom-composed-validators-data-collection/

dom xss验证

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值