当使用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 参数列表中。代码如下:

public void contextInitialized(ServletContextEvent servletContextEvent) {

        // return format json or xml, default is xml
String returnType = servletContextEvent.getServletContext()
.getInitParameter("returnType");
ValidateUtil.returnType = StringUtil.isNullOrWhiteSpace(returnType) ? ValidateUtil.returnType
: returnType; // get validateParam.xml path
String configLocation = servletContextEvent.getServletContext()
.getInitParameter("validateConfigLocation");
if (StringUtil.isNullOrWhiteSpace(configLocation)) {
System.err.println("validateConfigLocation config is not found");
return;
} // add namespace
SAXReader reader = new SAXReader();
Map<String, String> map = new HashMap<String, String>();
map.put("xmlns", "http://www.example.net/test");
reader.getDocumentFactory().setXPathNamespaceURIs(map); try {
String configPath = servletContextEvent.getServletContext()
.getRealPath(configLocation);
Document document = reader.read(new File(configPath)); // get errorResultClass
Node node = document
.selectSingleNode("/xmlns:validators/xmlns:errorResultClass");
ValidateUtil.errorResultClass = node == null ? "org.paramvalidate.vo.ErrorResultVO"
: node.getText(); // get all servlets
List<Element> servletValidators = document
.selectNodes("/xmlns:validators/xmlns:servletValidator"); // read all servletValidators
for (Element servletValidator : servletValidators) { String servletUrl = servletValidator.valueOf("@servletUrl");
List<AbstractParamValidator> paramValidators = new ArrayList<AbstractParamValidator>(); List<Element> validators = servletValidator
.selectNodes("xmlns:validator"); // read all validator in servletValidator
for (Element validator : validators) { String validatorClassName = validator
.valueOf("@validatorClass");
Class<?> validatorClass = Class.forName(validatorClassName);
Object validatorInstance = validatorClass.newInstance(); // read attributes in this validator
List<Attribute> attributes = validator.attributes();
for (Attribute attribute : attributes) { String attributeName = attribute.getName();
if (attributeName.equals("validatorClass"))
continue; String attributeValue = attribute.getValue(); Method method = validatorClass.getMethod(
StringUtil.getSetMethodString(attributeName),
String.class); method.invoke(validatorInstance, attributeValue); } // read params in this validator
List<Element> params = validator
.selectNodes("xmlns:validateParam");
for (Element element : params) { String nameString = element.valueOf("@name");
String errorCode = element.valueOf("@errorCode"); Method addParamMethod = validatorClass.getMethod(
"addParam", String.class, String.class); addParamMethod.invoke(validatorInstance, nameString,
errorCode);
} // all paramValidators in the servlet
paramValidators
.add((AbstractParamValidator) validatorInstance); } // all servlet validators
ValidateUtil.servletValidators.put(servletUrl, paramValidators); }
// error in jetty
// FilterRegistration filterRegistration = servletContextEvent
// .getServletContext().addFilter("ValidateFilter",
// "org.paramvalidate.filter.ValidateFilter");
// filterRegistration.addMappingForUrlPatterns(null, true, "/*"); } catch (DocumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} }

  

  下边就是filter了,filter根据请求的url取出对应的验证器列表进行验证,并返回错误码。

    public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// get request
HttpServletRequest req = (HttpServletRequest) request; // get servlet validators
String servletUrlString = req.getServletPath();
List<AbstractParamValidator> validators = ValidateUtil.servletValidators
.get(servletUrlString); if (validators != null) {
List<String> errorCodes = new ArrayList<String>();
// validate every
for (AbstractParamValidator validator : validators) {
try {
errorCodes.addAll(validator.validate(req));
} catch (Exception e) {
e.printStackTrace();
}
} if (!errorCodes.isEmpty()) {
AbstractErrorResultVO vo = null;
try {
vo = (AbstractErrorResultVO) Class.forName(
ValidateUtil.errorResultClass).newInstance();
vo.setErrorCodes(errorCodes); } catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} if (ValidateUtil.returnType == "json") {
response.getWriter().println(vo.toJsonString());
} else {
response.getWriter().println(vo.toXmlString());
}
return;
}
}
chain.doFilter(request, response);

  剩下的就很简单了,在web.xml中配置listener和filter即可。当然也可以在listener中动态注册filter,但是这要用到servlet-api.jar在tomcat中没啥问题。如果是用jetty服务器,那么jetty带的servlet-api.jar就不一定有动态注册的filter的方法了。

  上述只是介绍了实现原理,具体代码可能随时变动,也可能添加其他的功能,有什么错误还请大家指正。

  代码已经放到了GitHub上,地址:https://github.com/lulonglong/ParamValidator

实现一个可配置的java web 参数验证器的更多相关文章

  1. 自研后端HTTP请求参数验证器服务ParamertValidateService

    好处:方便了后端对HTTP请求中参数进行核验,只需一次编写效验器,一行代码便可对所有参数的pojo进行参数核验!而且更改效验逻辑时只需要更改效验器类即可,实现了解耦合. 只需要程序员按照规范开发一个P ...

  2. 【java web】拦截器inteceptor

    一.简介 java里的拦截器提供的是非系统级别的拦截,也就是说,就覆盖面来说,拦截器不如过滤器强大,但是更有针对性. Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现 ...

  3. SpringBoot自定义参数验证器

    前要 之前我们介绍了JSR-303验证方式,十分的方便Spring都帮我们封装好了,但是对一些复杂的验证,还是需要更加灵活的验证器的. JSR-303验证器传送门:https://www.jiansh ...

  4. IDEA创建一个Spring MVC 框架Java Web项目,Gradle构建

    注:此篇有些细节没写出,此文主要写重要的环节和需要注意的地方,轻喷 新建项目 选择Gradle , 勾选java 和 web.之后就是设定项目路径和名称,这里就不啰嗦了. build.gradle文件 ...

  5. Java web servlet 拦截器 以登陆为例子

    以登陆为例子............... public class LoginFilter implements Filter { @Override public void destroy() { ...

  6. 初学Java Web(3)——第一个Servlet

    这学期 Java Web 课程的第一节课就简短复习了一下 Java 的一些基础知识,所以觉得 Java 的基础知识还是很重要的,但当我想要去写一篇 Java 回顾的文章的时候发现很难,因为坑实在太多了 ...

  7. centos上安装配置java WEB环境_java(转)

    趁着十一期间,好好的写写随笔来记录自己所学.所践和所得,不足之处,欢迎各位拍砖~~~ 工具:Xftp 5.Xshell 5 一.安装jdk 1. 使用Xftp 5把jdk-8u65-linux-x64 ...

  8. 初学 Java Web 开发,请远离各种框架,从 Servlet 开发

    Web框架是开发者在使用某种语言编写Web应用服务端时关于架构的最佳实践.很多Web框架是从实际的Web项目抽取出来的,仅和Web的请求和响应处 理有关,形成一个基础,在开发别的应用项目的时候则可以从 ...

  9. 初学 Java Web 开发,从 Servlet 开发

    1. 基本要求:Java 编程基础 有良好的 Java 语言编程基础,这是必须的,在讨论 Web 开发技术时提了一个 Java 编程基础的问题会被鄙视的. 2. 环境准备 (Eclipse + Tom ...

随机推荐

  1. 在线elasticsearch集群批量写入变慢,导致kafka消息消费延迟

    写入报错如些: -- ::24.166 [elasticsearch[_client_][listener][T#1]] INFO com.mobanker.framework.es.Elastics ...

  2. ASP.NET MVC 微信JS-SDK认证

    layout: post title: ASP.NET MVC 微信JS-SDK认证 category: .net date: 2016-11-01 00:00:00 tags: .net javas ...

  3. bbblack的网络socket通信实验

    1. 本次用bbblack作网络的通信实验,对了,这个板子必须装SD卡才能启动吗?板载的4GB eMMC Flash 存储器,eMMC (Embedded Multi Media Card) 为MMC ...

  4. Grafana学习

    一.安装 Grafana最新版本4.3.1安装(后端使用mysql) 二.使用

  5. 你需要掌握的http知识

    作为一名前端er,http是我们必须要掌握的,那么我们到底需要掌握哪些东西呢 一.基础知识 这里我们介绍与http相关的TCP.IP.DNS.url.uri 1.IP IP地址是我们很熟悉的东西,最常 ...

  6. 「LeetCode」0002-Longest Substring Without Repeating Characters(C++)

    分析 贪心思想.注意更新每次判断的最长不同子串的左区间的时候,它是必须单调增的(有时候会在这里翻车). 代码 关掉流同步能有效提高速度. static const auto io_sync_off = ...

  7. Linux命令应用大词典-第17章 软件包管理

    17.1 rpm:RPM软件包管理器 17.2 rpmargs:处理RPM软件包 17.3 rpmbuild:构建RPM软件包 17.4 rpmdiff:比较两个软件包之间的不同 17.5 rpmel ...

  8. lesson 18 Electric currents in modern art

    lesson18 Electric currents in modern art electricity n. 电力:电流; electric adj. 电的:电动的; electronic adj. ...

  9. Java应用基础微专业-进阶篇

    第1章--使用对象 1.1 字符类型 char c = 65; // char --> int char c = '\u0041'; // \u: unicode + (Hex 41--> ...

  10. 微服务框架Dubbo与Springcloud的区别

    微服务框架Dubbo与Springcloud的区别 微服务主要的优势如下: 1.降低复杂度 将原来偶合在一起的复杂业务拆分为单个服务,规避了原本复杂度无止境的积累.每一个微服务专注于单一功能,并通过定 ...