当前位置:   article > 正文

java后台参数字段的验证_java constraintviolation

java constraintviolation

前言

       参数验证是一个常见的问题,无论是前端还是后台,都需要对用户输入进行验证,以此来保证系统数据的正确性。对于web来说,有些人可能理所当然的想在前端验证就行了,但是这样是非常错误的想法,前端代码对于用户来说是透明的,稍微有点技术的人就可以绕过这个验证,直接提交数据到后台。无论是前端网页接口的提交还是提供给外部的接口,参数验证随处可见,也是必不可少的。前端做验证只是为了用户体验,比如控制按钮的显示隐藏,单页应用的路由跳转等等,后端才是最重的保障。总之一切用户的输入都是不可信的,需要我们去进行验证辨别。

常见的验证方式

前端的校验是必须的,后台的校验更是必须的,下面介绍几种验证的方式:

1.表现层验证:SpringMVC提供对JSR-303的表现层验证;

2.业务逻辑层验证:Spring3.1提供对业务逻辑层的方法验证(当然方法验证也可以出现在其他层,但个人认为方法验证应该属于业务逻辑层);

3.DAO层验证:Hibernate提供DAO层的模型数据的验证;

4.数据库端的验证:通过数据库约束来进行;

5.客户端验证支持:JSR-303页提供编程式验证支持

1.通过if-if判断

  1. if(string.IsNullOrEmpty(info.UserName))
  2. {
  3. return FailJson("用户名不能为空");
  4. }

这种方式简单,但是,参数多了的话就会很繁琐了;

2.自定义注解实现参数校验

切面拦截controller方法,然后捕获带@ChechParam注解方法参数实例,最后反射实例校验。

controller:

  1. @RequestMapping(value = "update" )
  2. @ResponseBody
  3. public ResultBean update(@CheckParam User user){
  4. return ResultBean.ok();
  5. }

model:

  1. public class User implements Serializable{
  2. @CheckParam(notNull = true)
  3. private String username;
  4. }

annotation:

  1. @Target(value={ElementType.PARAMETER,ElementType.FIELD,ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface CheckParam {
  5. boolean notNull() default false;
  6. }

aspect:

  1. import org.aspectj.lang.ProceedingJoinPoint;
  2. import org.aspectj.lang.annotation.Around;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Pointcut;
  5. import org.aspectj.lang.reflect.MethodSignature;
  6. import org.springframework.core.GenericTypeResolver;
  7. import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
  8. import org.springframework.core.MethodParameter;
  9. import org.springframework.stereotype.Component;
  10. import org.springframework.util.StringUtils;
  11. import java.lang.reflect.Field;
  12. import java.lang.reflect.Method;
  13. @Component
  14. @Aspect
  15. public class CheckParamAspect {
  16. @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
  17. public void methodPointCut() {}
  18. /**
  19. * 环绕切入方法
  20. **/
  21. @Around("methodPointCut()")
  22. public Object around(ProceedingJoinPoint point) throws Throwable {
  23. MethodSignature msig = (MethodSignature) point.getSignature();
  24. Method method = msig.getMethod();
  25. LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
  26. Object[] args = point.getArgs();
  27. for (int i = 0; i < args.length; i++) {
  28. Object obj = args[i];
  29. MethodParameter mp = new MethodParameter(method,i);
  30. mp.initParameterNameDiscovery(u);
  31. GenericTypeResolver.resolveParameterType(mp, method.getClass());//Spring处理参数
  32. //String paramName = mp.getParameterName();//参数名
  33. CheckParam anno = mp.getParameterAnnotation(CheckParam.class);//参数注解
  34. if(anno != null){
  35. check(obj);
  36. }
  37. }
  38. return point.proceed();
  39. }
  40. /**
  41. * 校验成员变量
  42. **/
  43. private void check(Object obj) throws IllegalAccessException {
  44. Class clazz = obj.getClass();
  45. for(Field field : clazz.getDeclaredFields()){
  46. CheckParam cp = field.getAnnotation(CheckParam.class);
  47. if(cp != null){
  48. check(obj,clazz, field,cp);
  49. }
  50. }
  51. }
  52. /**
  53. * 取出注解,校验变量
  54. **/
  55. private void check(Object obj, Class clazz, Field field, CheckParam cp) throws IllegalAccessException {
  56. if(cp.notNull()){
  57. field.setAccessible(true);
  58. Object f = field.get(obj);
  59. if(StringUtils.isEmpty(f)){
  60. throw new IllegalArgumentException("类" + clazz.getName() + "成员" + field.getName() + "检测到非法参数");
  61. }
  62. }
  63. }
  64. }

3.自定义ValidationUtils

表单验证工具类ValidationUtils,依赖包commons-lang

  1. import org.apache.commons.lang3.StringUtils;
  2. import org.apache.commons.lang3.math.NumberUtils;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import java.util.regex.Pattern;
  6. public class ValidateUtils {
  7. /**
  8. * @param fields
  9. * @param params
  10. * @return
  11. * 不存在的校验规则:返回true
  12. * 关键字不按要求写:返回true
  13. */
  14. public static SKResult validate(ValidField[] fields, Map<String, String> params){
  15. try {
  16. for(ValidField field : fields){
  17. String name = field.getName();
  18. String desc = field.getDes();
  19. boolean isValid = field.isValid();
  20. String[] rules = field.getRules();
  21. String value = params.get(name); // 对应请求参数值
  22. if(!isValid){
  23. return new SKResult(true, "");
  24. }
  25. for(String rule : rules){
  26. String[] arr = rule.replaceAll(" ", "").split(":");
  27. String arr1 = arr[0]; // required
  28. String arr2 = arr[1]; // true
  29. switch (arr1) {
  30. case "required": // 必须项 required:true|false
  31. if(Boolean.parseBoolean(arr2)){
  32. if(value==null || value.trim().length()==0){
  33. return new SKResult(false, desc+"不能为空");
  34. }
  35. }
  36. break;
  37. case "number": // 必须输入合法的数字(负数,小数) number:true|false
  38. if(Boolean.parseBoolean(arr2)){
  39. try{
  40. Double.valueOf(value);
  41. }catch(Exception e){
  42. return new SKResult(false, desc+"数值类型不合法");
  43. }
  44. }
  45. break;
  46. default:
  47. break;
  48. }
  49. }
  50. }
  51. } catch (Exception e) {
  52. e.printStackTrace();
  53. System.out.println("===ValidField格式不合法,请注意检查!");
  54. return new SKResult(true, "ValidField格式不合法");
  55. }
  56. return new SKResult(true, "校验通过");
  57. }
  58. public static void main(String[] args) {
  59. Map<String, String> params = new HashMap<String, String>();
  60. params.put("username", "18702764599");
  61. params.put("password", "123");
  62. ValidField[] fields = {
  63. new ValidField("username", "手机号", true, new String[]{
  64. "required:true",
  65. "isTel:true"
  66. "min:5"
  67. "max:5"
  68. }),
  69. new ValidField("password", "密码", true, new String[]{
  70. "required:true",
  71. "isPassword:true",
  72. "equalTo:#username"
  73. "max:2"
  74. })
  75. };
  76. SKResult sk = ValidateUtils.validate(fields, params);
  77. System.out.println(sk);
  78. //SKResult [result=true, respMsg=校验通过, obj=null, type=null]
  79. }
  80. }

SKResult :

  1. public class SKResult {
  2. // 返回代码
  3. private boolean result;
  4. // 错误信息
  5. private String respMsg;
  6. private Object obj;
  7. //set.get方法
  8. @Override
  9. public String toString() {
  10. return "SKResult [result=" + result + ", respMsg=" + respMsg + ", obj="
  11. + obj + ", type=" + type + "]";
  12. }
  13. }

ValidField :

  1. public class ValidField {
  2. /**
  3. * 字段名
  4. */
  5. private String name;
  6. /**
  7. * 字段描述
  8. */
  9. private String des;
  10. /**
  11. * 为true必须校验
  12. */
  13. private boolean isValid = false;
  14. /**
  15. * 校验规则
  16. */
  17. private String[] rules;
  18. public String[] getRules() {
  19. return rules;
  20. }
  21. public void setRules(String[] rules) {
  22. this.rules = rules;
  23. }
  24. public String getName() {
  25. return name;
  26. }
  27. public void setName(String name) {
  28. this.name = name;
  29. }
  30. public String getDes() {
  31. return des;
  32. }
  33. public void setDes(String des) {
  34. this.des = des;
  35. }
  36. public boolean isValid() {
  37. return isValid;
  38. }
  39. public void setValid(boolean isValid) {
  40. this.isValid = isValid;
  41. }
  42. public ValidField(String name, String des, boolean isValid, String[] rules) {
  43. super();
  44. this.name = name;
  45. this.des = des;
  46. this.isValid = isValid;
  47. this.rules = rules;
  48. }
  49. }

4.JSR-303规范,Bean Validation

JSR 303(Java Specification Requests 规范提案)是JAVA EE 6中的一项子规范,一套JavaBean参数校验的标准,叫做Bean Validation。JSR 303用于对Java Bean中的字段的值进行验证,Spring MVC 3.x之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。

  1. <!--jsr 303-->
  2. <dependency>
  3. <groupId>javax.validation</groupId>
  4. <artifactId>validation-api</artifactId>
  5. <version>1.1.0.Final</version>
  6. </dependency>
  7. <!-- hibernate validator-->
  8. <dependency>
  9. <groupId>org.hibernate</groupId>
  10. <artifactId>hibernate-validator</artifactId>
  11. <version>5.2.0.Final</version>
  12. </dependency>
  1. 在想要校验的类的属性上面加入@NotNull等注解
  2. package com.example.demo;
  3. import javax.validation.ConstraintViolation;
  4. import javax.validation.Validation;
  5. import javax.validation.ValidationException;
  6. import javax.validation.Validator;
  7. import javax.validation.constraints.NotNull;
  8. import javax.validation.constraints.Pattern;
  9. import java.util.Iterator;
  10. import java.util.Set;
  11. /**
  12. * @author lanxinghua
  13. * @date 2018/08/05 15:51
  14. * @description
  15. */
  16. public class ValidateTestClass {
  17. @NotNull(message = "reason信息不可以为空")
  18. @Pattern(regexp = "[1-7]{1}", message = "reason的类型值为1-7中的一个类型")
  19. private String reason;
  20. public void setReason(String reason) {
  21. this.reason = reason;
  22. }
  23. public void validateParams() {
  24. //调用JSR303验证工具,校验参数
  25. Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
  26. Set<ConstraintViolation<ValidateTestClass>> violations = validator.validate(this);
  27. Iterator<ConstraintViolation<ValidateTestClass>> iter = violations.iterator();
  28. if (iter.hasNext()) {
  29. String errMessage = iter.next().getMessage();
  30. throw new ValidationException(errMessage);
  31. }
  32. }
  33. }
  1. 在controller里面的方法入参前面加上@Valid,参数后面加上BindingResult
  2. 在方法里面通过bindingResult.hasErrors()判断是否有错误,如果有错误返
  3. 回当前页面
  4. @RequestMapping("/register")
  5. public ModelAndView register(@Valid User user,BindingResult bindingResult) {
  6. ModelAndView modelAndView = new ModelAndView();
  7. if(bindingResult.hasErrors()){
  8. String message=bindingResult.getFieldError().getDefaultMessage();
  9. System.out.println(message);
  10. }
  11. modelAndView.setViewName("/index");
  12. return modelAndView;
  13. }

以上方法是在controller层对参数进行校验,然而当我们需要在server层对参数字段等进行校验的时候则需要手动进行校验,栗子如下:

     首先在需要检验的类上添加相关的校验注解,此处不做赘述;

     第二步创建校验的工具类:

  1. import javax.validation.ConstraintViolation;
  2. import javax.validation.Validation;
  3. import javax.validation.Validator;
  4. import java.util.Set;
  5. public class CommonTool {
  6. public static void validate(Object o) throws Exception {
  7. String error="";
  8. Validator validator= Validation.buildDefaultValidatorFactory().getValidator();
  9. Set<ConstraintViolation<Object>> constraintViolations =validator.validate(o);
  10. if (constraintViolations.size()>0){
  11. for (ConstraintViolation<Object> constraintViolation:constraintViolations){
  12. error=error+constraintViolation.getMessage()+";";
  13. }
  14. throw new Exception(error);
  15. }
  16. }
  17. }

     第三步,手动校验

  1. public Department addDepartment(Department dt) throws Exception {
  2. //手动进行校验
  3. CommonTool.validate(dt);
  4. return departmentDao.save(dt);
  5. }

5.JSR-303规范,Bean Validation在ssm项目中使用

JSR和Hiberbate validator的校验只能对Object的属性进行校验。

1.Model中添加校验注解

  1. public class Book {
  2. private long id;
  3. @NotEmpty(message = "书名不能为空")
  4. private String bookName;
  5. @NotNull(message = "ISBN号不能为空")
  6. private String bookIsbn;
  7. @DecimalMin(value = "0.1",message = "单价最低为0.1")
  8. private doubleprice; // getter setter ....... }

2.在controller中使用此校验

  1. @RequestMapping(value = "/book",method = RequestMethod.POST)
  2. public void addBook(@RequestBody @Valid Book book) {
  3. System.out.println(book.toString());
  4. }

3.分组验证

对同一个Model,我们在增加和修改时对参数的校验也是不一样的,这个时候我们就需要定义分组验证,步骤如下:

   定义两个空接口,分别代表Person对象的增加校验规则和修改校验规则

  1. //可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
  2. public interface PersonAddView {}
  3. public interface PersonModifyView {}

   Model上添加注解时使用指明所述的分组

  1. public class Person {
  2. private long id;
  3. /**
  4. * 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用
  5. */
  6. @NotNull(groups = {PersonAddView.class, PersonModifyView.class}, message= "添加、修改用户时名字不能为空",payload = ValidateErrorLevel.Info.class)
  7. @ListNotHasNull.List({
  8. @ListNotHasNull(groups = {PersonAddView.class}, message = "添加上Name不能为空"),
  9. @ListNotHasNull(groups = {PersonModifyView.class}, message = "修改时Name不能为空")})
  10. private String name;
  11. @NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")
  12. private String address;
  13. @Min(value = 18, groups = {PersonAddView.class}, message = "姓名不能低于18岁")
  14. @Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")
  15. private int age;
  16. //getter setter 方法......
  17. }

   此时启用校验和之前的不同,需要指明启用哪一组规则

  1. /**
  2. * 备注:此处@Validated(PersonAddView.class)表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,若两个规则同时加上去,则只有第一套起作用
  3. * 修改Person对象
  4. * 此处启用PersonModifyView这个验证规则
  5. */
  6. @RequestMapping(value = "/person", method = RequestMethod.PUT)
  7. public void modifyPerson(@RequestBody @Validated(value ={PersonModifyView.class}) Person person) {
  8. System.out.println(person.toString());
  9. }

6.Spring validator 方法级别的校验

JSR和Hibernate validator的校验只能对Object的属性进行校验,不能对单个的参数进行校验,Spring在此基础上进行了扩展,添加了MethodValidationPostProcessor拦截器,可以实现对方法参数的校验

  1. public @NotNull UserModel get2(@NotNull @Size(min = 1) Integer uuid) {
  2. //获取 User Model
  3. UserModel user = new UserModel(); //此处应该从数据库获取
  4. return user;
  5. }

JSR提供的校验注解:

  1. @Null 被注释的元素必须为 null
  2. @NotNull 被注释的元素必须不为 null
  3. @AssertTrue 被注释的元素必须为 true
  4. @AssertFalse 被注释的元素必须为 false
  5. @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  6. @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  7. @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  8. @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  9. @Size(max=, min=) 被注释的元素的大小必须在指定的范围内
  10. @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
  11. @Past 被注释的元素必须是一个过去的日期
  12. @Future 被注释的元素必须是一个将来的日期
  13. @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式

Hibernate Validator提供的校验注解:

  1. @NotBlank(message =) 验证字符串非null,且长度必须大于0
  2. @Email 被注释的元素必须是电子邮箱地址
  3. @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
  4. @NotEmpty 被注释的字符串的必须非空
  5. @Range(min=,max=,message=) 被注释的元素必须在合适的范围内

 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号