zoukankan      html  css  js  c++  java
  • 实现一个可配置的java web 参数验证器

      当使用java webservlet来做接口的时候,如果严格一点,经常会对请求参数做一些验证并返回错误码。我发现通常参数验证的代码就在servlet里边,如果参数不正确就返回相应的错误码。如果接口数量少,参数不多,没什么感觉。如果参数多了,那么可能验证参数的代码就会有一大堆,显得格外乱。接口数量多了,那工作量也是非常的庞大。其实参数验证是一件重复的工作,不同的接口对参数的验证基本是一样的,无非就是验证一下参数是否为空,是否符合指定格式。

      这样就可以将参数验证的逻辑提取出来,不放在servlet里,如果参数验证错误就直接返回错误码,根本不用经过servlet处理。说到这里就想到了filterfilter可以将请求做一下过滤,如果没问题再转交给servlet处理,servlet直接提取参数即可,根本不用担心参数的错误问题。那不同的接口有不同的验证参数,filter怎么知道该验证哪个接口的哪个参数呢,又是怎么知道这个参数该符合什么样的格式呢?那就想到了listenerlistener在程序启动时运行,我们可以将接口及验证参数和验证格式配置到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代表servleturl映射。然后里边有若干个validator,每个validator对应一个功能参数验证器,不同的validator有不同的功能。validatorClass代表验证器的classvalidator的子标签validateParam 代表这个验证器验证的参数及错误码。上边的例子是一个记录客户端登录信息的接口,userid不能为空(对应错误码0104101)且只能是数字(对应错误码0104102)。Phoneosdevicename不能为空,也有其对应的错误码。

      如上所示NullValidator负责验证参数是否为空,有多少种参数验证功能我们只需要提供多少种validator即可,例如取值范围在1234之间的,是否为大于某一个整数的,是否为字符’,’隔开的字符串。有多少种验证方式就提供多少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里遍历xmlservletValidator 并遍历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中配置listenerfilter即可。当然也可以在listener中动态注册filter,但是这要用到servlet-api.jartomcat中没啥问题。如果是用jetty服务器,那么jetty带的servlet-api.jar就不一定有动态注册的filter的方法了。

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

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

    本人学识尚浅,欢迎交流。 倘若文章帮到了您,那真是好极了。
  • 相关阅读:
    C语言关键字
    C语言返回值
    五、Vue:使用axios库进行get和post、用拦截器对请求和响应进行预处理、Mock(数据模拟)
    四、Vue过渡与动画、过渡css类名、自定义指定、过滤器
    三、深入Vue组件——Vue插槽slot、动态组件
    二、Vue组件(component):组件的相互引用、通过props实现父子组件互传值
    一、Vue环境搭建及基础用法
    Django(十三)状态保持 —— cookie与session+ajax异步请求+session记住登录状态+cookie记住登录名密码
    Django(十二)视图--利用jquery从后台发送ajax请求并处理、ajax登录案例
    Django(十一)视图详解:基本使用、登录实例、HttpReqeust对象、HttpResponse对象
  • 原文地址:https://www.cnblogs.com/luguo3000/p/3191263.html
Copyright © 2011-2022 走看看