参数验证,防止用户恶意改变差数值。

可重用检验函数
验证工作就是决定何种格式的数据可以被特定的输入域接收。自然,很多输入域的要求是相似的:例如,输入域大都不能是空的,ZIP码和会员码都只能包含数字,等等。

因此,我们可以通过编写一些符合多种要求的可重用的函数,来解决数据验证的问题。我们的目标是使这些函数尽可能的通用;然后我们示范如何在需要这些函数的情况下调用它们。

检查你的字符串
我们从最简单的函数开始,它们由代码B列出。函数notNull用来检验它收到的字符串是否为空;isBlank函数检验字符串是否全部由空格组成;isSize函数察看字符串的长度是否符合要求。

代码B:几个字符串检验函数

 
function notNull(str) {
 if (str.length == 0 )
  return false
 else
  return true
}

function notBlank(str) {
 for (i = 0; i < str.length; i++) {
  if (str.charAt(i) != " ")
   return true
 }
 return false
}

function isSize(str, size) {
 if (str.length == size)
  return true
 else
  return false
}
notNull函数察看文本框中是否有东西输入。它调用String的length方法。如果发现传给它字符串长度等于零,它就认为该字符创是空的,并返回false;否则返回true。

检查字符串是否完全由空格组成的方法与上述原理有一些不同,但是基本类似。这个工作由notBlank函数完成。该函数提取字符串中的每一个字符,检查其是否为空格。如果在其中找到不是空格的字符,就返回true。如果只找到空格,函数就返回false。

输入的数据经常要求具有特定的长度。例如ZIP码应该由8位或9位数字组成;除此之外格式的输入都是错误数据。成员或者会员号码的长度也应该是特定的。

isSize函数有助于验证这类数据。该函数同时接收待检查的字符串和所期望的字符串长度。然后检查收到的长度和字符串的实际长度是否吻合。是的话就返回true,否则返回false。

处理数字
用户也许会在需要数字的地方键入了其它的东西,这是一个常见的问题。如果你的应用程序下一步要依靠这个数据进行计算,问题就显得十分严重了。要解决这个问题,在我们的验证数据的代码库中还提供了以下三个函数isDigits、isNumber和isInRange。它们都在代码C中列出。

代码C:用于检验数字的函数

   
function isDigits(str) {
 var i
 for (i = 0; i < str.length; i++) {
  mychar = str.charAt(i)
  if (mychar < "0" || mychar > "9")
   return false
 }
 return true
}

function isNumber(str) {
 numdecs = 0
 for (i = 0; i < str.length; i++) {
  mychar = str.charAt(i)
  if ((mychar >= "0" && mychar <= "9") || mychar
   == ".") {
   if (mychar == ".")
    numdecs++
  }
  else
   return false
 }
 if (numdecs > 1)
  return false 
return true
}

function isInRange(str, num1, num2) {
 var i = parseInt(str)
 return((i >= num1) && (i <= num2))

}

isDigits函数检查并确保字符串完全由数字组成。这里,函数也是从字符串中提取每一个字符,检查它们是否超出了0-9的范围。如果有任何字符不在这个区间内,函数就返回false;否则返回true。

假设你有一些美元,或者其它可以用小数表示的数据,这类数据包含一个小数点。这种情况下isDigits函数不能正确识别它们;我们需要识别一个字符串是否完全是数字,并且其中最多含有一个小数点。isNumber函数可以做到这一点。

isNumber函数稍稍有些复杂,它使用的算法与isDigits正好相反。该函数也是提取字符串中的每一个字符,但不是检查其是否超出0-9的范围,而是检查是否落在这个区间内。同时它还检查字符是否为小数点,是的话就把一个计数变量加一。若在检查中发现不符合上述内容的字符,函数立即返回false。如果运行到循环结束,函数检查再其中是否含有过多的小数点。如果小数点的个数多于一个,就返回false,否则返回true。

isInRange函数检查字符串是否在给定的数字区间内。除了接收待检查的字符串以外,它还接受两个数字,分别表示给定区间的起始值和终止值。该函数首先把接收到的字符串通过String的parseInt方法转换为一个整数,然后检查转换得到的数字是否在接收到的区间内。

小小的重格式化

IsDigits和isNumber函数固然很好,但是还有一些局限。有时候,用户输入的字符串符虽然是合理的,但仍然会无法通过验证,或者搞乱下一步的运算。例如,用户可能会在ZIP码中间插入空格或破折号,或者在他们的美元数字前加上美元符号($)。这些时候数据并不是真的有错,只是需要把其中的数字部分提取出来。

幸运的是,有一个通用的解决方法:将字符串重新格式化,去掉所有无关的字符,这样就可以得到需要的数字了。然后我们可以检查重新格式化后的字符串的长度、范围,等等。

我们提供了两个用于重格式化的函数:stripNonDigits和stripChars,如代码D所示。StripNonDigits函数提取收到的字符串中的每一个字符,并将其传给isDigits。如果该字符是一个数字,stripNonDigits就把它加入到一个新字符串变量中,从而建立一个仅仅由数字组成的字符串。当循环结束的时候,函数返回新建立的字符。

代码D:从字符串中剔除一切非数字字符的函数


function stripNonDigits(str) {
 var i
 var newstring = ""
 for (i = 0;  i < str.length; i++) {
  mychar = str.charAt(i)
  if (isDigits(mychar))
   newstring += mychar
 }
 return newstring
}

function stripChars(str, chars) {
 var i
 var newstring = ""
 for (i = 0;  i < str.length; i++) {
  mychar = str.charAt(i)
  if (chars.indexOf(mychar) == -1)
   newstring += mychar
 }
 return newstring
}
  
另一方面,函数stripChars用来从字符串中剔除指定的字符。这个函数有两个参数:str是输入的字符串,另一个参数chars是字符串,由希望从字符串中的剔除的字符组成。通过String的indexOf方法检查字符串str中的每一个字符是否被chars包含;如果不是,这个字符就被加入到一个新建的字符串中。注意如果所查找的子串不在被查找的母字符串中,String的indexOf方法返回-1。当函数运行结束的时候,返回新建的字符串。


第二步:验证输入域

接下来的一步我们创建用于验证输入域的函数。这可以有多种方法完成。不论采用何种方法,有两个要点必须始终牢记。

首先,如果用户输入了错误数据,应该在焦点还保持在当前输入域上的时候就有能改正错误的机会,不要让用户事后再返回去改正错误。第二,用户如果犯了错误,应该得到提示。提示的具体细节依具体的应用程序而定。

简单的字符串验证
我们从验证除了需要输入非空字符串之外没有其它要求的输入域开始。这类验证适用于姓名、地址和城市区域的输入,我们可以创建一个通用的函数处理所有这些情况。该函数名为validateString,如代码E所列。

代码E:检验字符串的代码

  
//Global variable set at start of script
var emptyString = " field is blank. Please enter a "

function validateString(myfield, s) {
 if (notNull(myfield.value)&& notBlank(myfield.value))
  return true
 else {
  myfield.focus()
  alert("The " + s + emptyString + s)
  return false
 }
}

函数validateString接受两个参数:第一个是指向待验证输入域的引用,另一个是用于提示用户错误情况的字符串。注意这个函数依靠我们在第一步中创建的可重用函数isNull和isBlanks来完成任务。如果这两个函数都没有返回false,函数validateString就返回true。

不论何时,一旦错误出现,validateString函数就采取三个步骤处理:

将焦点保持在当前输入域上,让用户有机会输入更合适的数据。
向用户提供一个有关错误的提示。
向调用它的函数返回false。
函数validateString所显示的警告信息存储在一个全局变量emptyString中,如代码E所示。在脚本开始处这一字符串变量被赋值为"field is blank. Please enter a"。函数validateString把这个字符串和接受到的字符串参数s连接起来,组成一条与所验证的输入域相对应的警告信息。例如,如果我们用如下语句调用validateString:

validateString(form.firstname, "first name")

这里字符串"first name"传给s,我们可以在图B中看到输出的提示。

图B

验证数字输入
函数validateMemNum和validatePledge用来验证数字输入,由代码F列出。第一个函数validateMemNum用于验证我们的member#域,该域要求输入一个介于100到999之间的数字。(做的好,这表明站点有一定的会员基础。)

代码F:验证数字输入的函数

 
function validateMemNum(myfield) {
 if (isDigits(myfield.value) && isInRange(myfield.value,100, 999))
  return true

 else {
  myfield.focus()
  alert("Invalid customer number. Please enter a three digit number between 100 and 999")
  return false
 }
}

function validatePledge(myfield) {
 if (notNull(myfield.value)) {
  newstring = stripChars(myfield.value, "$")
  if  (isNumber(newstring))
   return true
  else {
   myfield.focus()
   alert("Invalid pledge amount. Please enter a
    valid dollar amount.")
  }
 }
 return false
}

函数validateMemNum同样依靠我们先前建立的可重用函数。(你是否已经总结出这个模式了?)该函数调用isDigits检查输入数据是否全是数字,然后调用isInRange检查该数字是否介于第二个和第三个参数之间。如果这些要求没有得到满足,该函数的处理方法与我们前面看到的validateString类似。

有时,除了验证整数值,还需要验证所抵押的美元数量这样包含小数点的数据。我们采用函数validatePledge。在validatePledge中,使用stripChars来剔除用户可能输入的美元符号,然后调用isNumber函数检查输入的数量是否是一个合理的整数值或小数值。

检验州缩写或ZIP码
代码G列出了检验ZIP码和州缩写的代码。验证州缩写的代码举例说明了如何验证那些有限选项的输入。如果输入的待选项很少(10个或以下),你可以使用选择元素,这样可以省去验证数据的麻烦。但是,50个州的缩写将会组成一个十分笨重的列表,因此我们采用别的方法。

代码G: 验证洲和邮政编码的代码


var STATECODES = AL/AK/AZ/AR/CA/CO/CT/DE/DC/FL/GA/HI/ID/
 IL/IN/IA/KS/LA/ME/MD/MA/MI/MN/MS/MO/MT/NV/NH/NJ/NM/NY/
 NC/ND/OH/OK/OR/PA/PR/RI/SC/TN/TX/UT/VT/VA/WA/WV/WI/WY"

function isStateCode (str) {
 var newstring = str.toUpperCase()
 if (STATECODES.indexOf(newstring) != -1 && str.indexOf("/") == -1)
  return true
 else
  return false
}

function validateState(myfield) {
 if (notNull(myfield.value) && isSize(myfield.value, 2) && isStateCode(myfield.value))
  return true
 else {
  myfield.focus()
  alert("Invalid state code. Please enter 2-letter state postal abbreviation.")
  return false
 }
}

function validateZip(myfield) {
 if (notNull(myfield.value)) {
  newstring = stripNonDigits(myfield.value)
  if (isSize(newstring,5) || isSize(newstring, 9))
   return true
 }
 myfield.focus()
 alert("Invalid zip code. Please enter 5-digit or 9-digit zip code.")
 return false
}

由于输入的选项是有限的,所以我们把这些选项都放入一个名为STATECODES的全局字符串变量中。选项之间由斜线分隔(/)。函数isStateCode的功能就是检查输入数据能否在字符串STATECODES找到。

这个函数首先提取用户输入的字符串,将其转换为大写。通过这种方式,无论键入的缩写是何种格式(大写或小写),我们都可以用处理大写值的方式处理。然后函数检查输入的字符串能否在STATECODES找到。

if (STATECODES.indexOf(newstring) != -1

第二步检查确保用户没有输入我们使用的分隔符,/。

str.indexOf("/") == -1)
代码G也列出了validateState所调用的isStateCode函数。validateState函数使用notNull和isSize函数对输入的字符串进行基本检查,然后调用isStateCode确保该字符串是一个合理的输入。如果这些测试中有一项失败,函数就采取同前面的检验函数相同的处理方法。

代码G所列的最后一个函数用于检查ZIP码。回想一下,ZIP码由8位或者9位数字组成,头5位和其它位之间有可能被分隔符分开。我们使用stripNonDigits函数将输入的字符串重新格式化,这样就可以仅仅注意输入的数字了。在确保输入域非空之后,我们剔除所有的非数字字符,然后开始计算输入数据的长度是否符合ZIP码的长度。

第三步,最后一步

现在,我们已经完成了所有检验函数的编写,如何把它们用于网页呢?

创建验证函数库
首先,由于这些函数是通用的,所以我们希望能够在尽可能多的页面上使用它们。一个好办法是把它们都放入一个JavaScript库文件中,以.js作为扩展名。任何页面只要在SCRIPT块中包含了指向这个文件的引用,就可以访问到这些函数。例如,我们将代码A列出的所有全局变量和函数都剪贴到一个文本文件中,取名为validate.js,然后,在任何一个Web页面的HEAD块中写入下列代码:


  <script language="JavaScript" src = "validate.js">
  </script>

这样,我们就可以在这个页面中调用JS文件中包含的任何函数了。

获取结果
用于启动检验过程的代码由代码H列出。该函数置于包含我们的表单的页面中的另一个SCRIPT块中。

代码H:表单页面的脚本块


<SCRIPT>
function checkform(form) {
return (validateString(form.firstname, "first name") &&
 validateString(form.lastname, "last name") &&
 validateMemNum(form.membernum, "member number") &&
 validateState(form.state) &&
 validateZip(form.zip) &&
 validatePledge(form.pledge))
}
</SCRIPT>

回头看一看代码A包含的创建表单的代码,下面的语句表示点击Sumbit按钮可以引发checkform函数的运行:


  <input type = "submit" value = "Submit"
 onClick = "return checkform(this.form)" name="submit">

函数checkform将表单作为参数接受,然后将表单中的每一个输入域交给相应的检验函数处理。

结果如何处理?回想一下,如果Submit按钮对象的事件处理函数返回false,该对象就会取消提交动作。因此,如果checkform调用的检验函数中有任何一个返回false,checkform就返回false,表单就不会被提交;如果所有的函数都返回true,填满经过验证的数据的表单就会被提交。

最后,有一条忠告:上面列出的函数可以指导你建立层次化结构的检验函数,并且为你的数据检验工作提供一些技巧。它们并不能保证满足所有数据验证的需要,因为你所需要输入的数据是变化多样的。


 

原文地址:https://www.cnblogs.com/IsNull/p/1370740.html