当使用java web的servlet来提供接口的时候,如果严格一点,经常会对请求参数做一些验证并返回错误码。我发现通常参数验证的代码就在servlet里边,如果参数不正确就返回相应的错误码。如果接口数量少,参数不多还没什么感觉。如果参数多了,那么可能验证参数的代码就会有一大堆,显得格外乱。接口数量多了,那工作量也是非常的庞大。其实参数验证是一件重复的工作,不同的接口对参数的验证基本是一样的,无非就是验证一下参数是否为空,是否符合指定格式。
这样就可以将参数验证的逻辑提取出来,不放在servlet里,如果参数验证错误将直接返回错误码,都不用经过servlet处理。说到这里就想到了filter,filter可以将请求做一下过滤,如果没问题再转交给servlet处理,servlet直接大胆的提取参数用即可,根本不用担心参数的错误问题。那不同的接口有不同的验证参数,filter怎么知道该验证哪个接口的哪个参数呢,又是怎么知道这个参数该符合什么样的格式呢?那就想到了listener,listener在程序启动时运行,我们可以将接口及验证参数和验证格式配置到xml文件中,程序启动时读取xml文件。请求到来时,filter读取listener事先整理好的接口验证器进行验证即可。
接下来先看一下参数验证的xml:
<validators xmlns="http://www.example.net/test" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.net/test validateParam.xsd">
<servletValidator servletUrl="/RecordLogInInfo">
<validator validatorClass="com.validator.RegexValidator" pattern="[0-9]+">
<validateParam name="userid" errorCode="0104102" />
</validator>
<validator validatorClass="com.validator.NullValidator">
<validateParam name="userid" errorCode="0104101" />
<validateParam name="phoneos" errorCode="0104103" />
<validateParam name="devicename" errorCode="0104104" />
</validator>
</servletValidator>
</validators>
Validators代表跟元素,servletValidator 代表对应servlet的参数验证器,servlet有多少就有多少servletValidator 。servletUrl代表servlet的url映射。然后里边有若干个validator,每个validator对应一个功能参数验证器,不同的validator有不同的功能。validatorClass代表验证器的class,validator的子标签validateParam 代表这个验证器验证的参数及错误码。上边的例子是一个记录客户端登录信息的接口,userid不能为空(对应错误码0104101)且只能是数字(对应错误码0104102)。Phoneos、devicename不能为空,也有其对应的错误码。
如上所示NullValidator负责验证参数是否为空,有多少种参数验证功能我们只需要提供多少种validator即可,例如取值范围在1、2、3、4之间的,是否为大于某一个整数的,是否为字符’,’隔开的字符串。有多少种验证方式就提供多少validator是不是太麻烦了呢,确实是这样。幸好大多数的验证都是可以通过正则表达式来验证的。这样我们就省下了很多的工作量,如RegexValidator就是一个正则表达式验证器,只要正则表达式能表示的就可以用这个验证器。
那我们的验证器应该怎么来写呢。我们有一个抽象的validator:
public abstract class AbstractParamValidator { protected Map<String, String> paramsMap = new HashMap(); public void addParam(String paramName, String errorCode) { this.paramsMap.put(paramName, errorCode); } public List<String> validate(HttpServletRequest req) throws Exception { List errorCodes = new ArrayList(); for (String paramName : this.paramsMap.keySet()) { if (isError(req.getParameter(paramName))) { errorCodes.add((String) this.paramsMap.get(paramName)); } } return errorCodes; } protected abstract boolean isError(String paramString) throws Exception; }
自己定义的validator只需要继承自这个抽象类并实现isError即可。比如RegexValidator:
public class RegexValidator extends AbstractParamValidator { private String pattern=null; @Override protected boolean isError(String content) throws Exception { return StringUtil.isNotNull(content)&&!content.matches(pattern); } public void setPattern(String pattern) { this.pattern = pattern; } }
其中pattern就是我们在xml配置中配置的,值得注意的是也要有一个标准的set方法与之对应,并且不管是什么类型,set方法只接受string类型参数,如果需要转换成其他类型则在方法里进行转换就好了。当然每个验证器需要的条件是不一样的,根据自己的需要来配置就好了,名字对就行了,当然也可以配置多个。
验证器写完了,下一步来看一下listener是怎么实现的。首先我们有一个全局的Map<String, List<AbstractParamValidator>>,在listener里遍历xml的servletValidator 并遍历servletValidator 里的validator ,利用反射将validator 实例化,再遍历validator 里的validateParam 并调用addParam方法将参数及错误码添加到validator 参数列表中。(详细代码会附在最后共下载查看)。
下边就是filter了,filter根据请求的url取出对应的验证器列表进行验证,并返回错误码。(详细代码会附在最后共下载查看)
剩下的就很简单了,在web.xml中配置listener和filter即可。当然也可以在listener中动态注册filter,但是这要用到servlet-api.jar在tomcat中没啥问题。如果是用jetty服务器,那么jetty带的servlet-api.jar就不一定有动态注册的filter的方法了。
有什么错误还请大家指正。
附件,反编译一下就好了,参数验证jar包
<script type="text/javascript"> </script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>