实现一个可配置的java web 参数验证器
当使用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 参数验证器的更多相关文章
- 自研后端HTTP请求参数验证器服务ParamertValidateService
好处:方便了后端对HTTP请求中参数进行核验,只需一次编写效验器,一行代码便可对所有参数的pojo进行参数核验!而且更改效验逻辑时只需要更改效验器类即可,实现了解耦合. 只需要程序员按照规范开发一个P ...
- 【java web】拦截器inteceptor
一.简介 java里的拦截器提供的是非系统级别的拦截,也就是说,就覆盖面来说,拦截器不如过滤器强大,但是更有针对性. Java中的拦截器是基于Java反射机制实现的,更准确的划分,应该是基于JDK实现 ...
- SpringBoot自定义参数验证器
前要 之前我们介绍了JSR-303验证方式,十分的方便Spring都帮我们封装好了,但是对一些复杂的验证,还是需要更加灵活的验证器的. JSR-303验证器传送门:https://www.jiansh ...
- IDEA创建一个Spring MVC 框架Java Web项目,Gradle构建
注:此篇有些细节没写出,此文主要写重要的环节和需要注意的地方,轻喷 新建项目 选择Gradle , 勾选java 和 web.之后就是设定项目路径和名称,这里就不啰嗦了. build.gradle文件 ...
- Java web servlet 拦截器 以登陆为例子
以登陆为例子............... public class LoginFilter implements Filter { @Override public void destroy() { ...
- 初学Java Web(3)——第一个Servlet
这学期 Java Web 课程的第一节课就简短复习了一下 Java 的一些基础知识,所以觉得 Java 的基础知识还是很重要的,但当我想要去写一篇 Java 回顾的文章的时候发现很难,因为坑实在太多了 ...
- centos上安装配置java WEB环境_java(转)
趁着十一期间,好好的写写随笔来记录自己所学.所践和所得,不足之处,欢迎各位拍砖~~~ 工具:Xftp 5.Xshell 5 一.安装jdk 1. 使用Xftp 5把jdk-8u65-linux-x64 ...
- 初学 Java Web 开发,请远离各种框架,从 Servlet 开发
Web框架是开发者在使用某种语言编写Web应用服务端时关于架构的最佳实践.很多Web框架是从实际的Web项目抽取出来的,仅和Web的请求和响应处 理有关,形成一个基础,在开发别的应用项目的时候则可以从 ...
- 初学 Java Web 开发,从 Servlet 开发
1. 基本要求:Java 编程基础 有良好的 Java 语言编程基础,这是必须的,在讨论 Web 开发技术时提了一个 Java 编程基础的问题会被鄙视的. 2. 环境准备 (Eclipse + Tom ...
随机推荐
- Prism for WPF 搭建一个简单的模块化开发框架 (一个节点)
原文:Prism for WPF 搭建一个简单的模块化开发框架 (一个节点) 这里我就只贴图不贴代码了,看看这个节点之前的效果 觉得做的好的地方可以范之前的文章看看 有好的建议也可以说说 填充数据 ...
- 【原创】Odoo开发文档学习之:构建接口扩展(Building Interface Extensions)(边Google翻译边学习)
构建接口扩展(Building Interface Extensions) 本指南是关于为Odoo的web客户创建模块. 要创建有Odoo的网站,请参见建立网站;要添加业务功能或扩展Odoo的现有业务 ...
- 一个小白的测试环境docker化之路
本文来自网易云社区 作者:叶子 学习docker搭建测试环境断断续续也有三个多月了,希望记录一下这个过程.常言道,总结过去,展望未来嘛~文章浅显,还望各位大神路过轻拍. 按照国际惯例,先说一下背景: ...
- 11gR2RAC更换CRS磁盘组文档
磁盘(pv)准备 在生产环境中,提前从存储上划分一些磁盘挂载到RAC系统的两个节点上(node1,node2). 新增加磁盘组为(hdisk14--hdisk24) 1.1磁盘使用规划 ...
- Error -26377: No match found for the requested parameter
Error -26377: No match found for the requested parameter
- 安卓客户端浏览器ajax注意
这两天被一个bug搞疯了,就是公司安卓app上我负责的网页死活不进ajax,一开始我用的是post方式提交的,但是参数那一栏没写,直接把参数写在url上了,后来老大跟我说post不写参数会出问题,后来 ...
- 【第三章】MySQL数据库的字段约束:数据完整性、主键、外键、非空、默认值、自增、唯一性
一.表完整性约束 作用:用于保证数据的完整性和一致性==============================================================约束条件 说明PRIM ...
- 【RL系列】Multi-Armed Bandit笔记——UCB策略与Gradient策略
本篇主要是为了记录UCB策略与Gradient策略在解决Multi-Armed Bandit问题时的实现方法,涉及理论部分较少,所以请先阅读Reinforcement Learning: An Int ...
- ajax获取动态列表数据后的分页问题
ajax获取动态列表数据后的分页问题 这是我在写前台网站时遇到的一个分页问题,由于数据是通过ajax的方式来请求得到的,如果引入相应的js文件来做分页,假如只是静态的填放数据到列表各项内容中(列表条数 ...
- php面试全套
7.mvc是什么?相互间有什么关系? 答:mvc是一种开发模式,主要分为三部分:m(model),也就是模型,负责数据的操作;v(view),也就是视图,负责前后台的显示;c(controller), ...