欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Springboot中使用ajax提交复杂对象+校验

程序员文章站 2022-03-01 20:36:27
...

ajax提交复杂对象

1. 提交的对象类UserDetail

public class User {

         @NotBlank

         private String id;

         @NotBlank

         private String userName

}

 

public class UserDetail {

         @NotNull

         private User user;

         private String details;

}

 

2. controller的代码:

    @RequestMapping(value = "api/user/add", method = { RequestMethod.PUT, RequestMethod.POST })

    @ResponseBody

    public String add(@RequestBodyUserDetail userDetail) {

        System.out.print(userDetail);

        return "success";

    }

 

3. ajax提交的代码,红色部分是关键代码,①处不写对会报400错;②处不这样写,后端无法接收

    var user = {

        userName : $("#userName").val()

    }

 

    var userDetail = {

        user : user,

        detail  : $("#detail").val()

    };

 

    $.ajax({

        type : 'POST',

        dataType : "JSON",

        contentType:"application/json", //

        cache : false,

        url : url,

        data : JSON.stringify(datas),

        async : false,

        success : function(result) {

            alert("成功");

        },

        error : function(result) {

            alert("失败");

        }

    });

后端校验

在做校验的时候踩了几个坑,这里主要是把坑记录下来

1

springboot的校验,首先考虑用@Valid做,于是在Controller代码里加入注解

    public String add(@Valid@RequestBodyUserDetail userDetail) {

        System.out.print(userDetail);

        return "success";

    }

很快发现问题,此处Valid的校验只会对按照UserDetail类里的校验规则做校验,而不会对其引用对象user校验,如User类里要求userName不为空,但此时校验不会校验userName。即没有对嵌套的内层对象做校验。

解决:在UserDetail类的user对象上加上@Valid注解

public class UserDetail {

         @NotNull

         @Valid

         private User user;

         private String details;

}

写个测试方法,测试正常:

publicclass ValidationTest {

    privatestaticfinal Validator validator;

 

    // 获得验证器实例

    static {

        Configuration<?> config = Validation.byDefaultProvider().configure();

        ValidatorFactory factory = config.buildValidatorFactory();

        validator = factory.getValidator();

        factory.close();

    }

 

    publicstaticvoid main(String[] args) throws ParseException {

        User user = new User();

        UserDetail userDetail = new UserDetail ();

        user.setSysUser(user);

 

        Set<ConstraintViolation<UserDetail>> violations = validator.validate(userDetail);

        if (violations.size() == 0) {

            System.out.println("No violations.");

        } else {

            System.out.printf("%s violations:%n", violations.size());

            violations.stream().forEach(ValidationTest::printError);

        }

    }

 

    privatestaticvoid printError(ConstraintViolation<?> violation) {

        System.out.println(violation.getPropertyPath() + " " + violation.getMessage());

    }

}

 

2

加上@Valid之后,前台直接报400错误,后台没有任何异常打印信息,userDetails也没有打印出来。推测此时验证已经生效,但绑定过程异常,并且该异常没有被BindException捕获(简单对象的验证错误是可以用这个异常捕获的)。

几经周折,最后发现这里报的异常是MethodArgumentNotValidException,于是添加对这个异常的全局处理,问题解决

 

改进:考虑了一下,在Controller层使用@Valid来做校验体验不好,直接手动校验逻辑会更加清楚

写一个校验工具类BeanValidators

publicclass BeanValidators {

 

    /**

     * 调用JSR303validate方法, 验证失败时抛出ConstraintViolationException.

     */

    @SuppressWarnings({ "unchecked", "rawtypes" })

    publicstaticvoid validateWithException(Validator validator, Object object, Class<?>... groups)

            throws ConstraintViolationException {

        Set constraintViolations = validator.validate(object, groups);

        if (!constraintViolations.isEmpty()) {

            thrownew ConstraintViolationException(constraintViolations);

        }

    }

 

    /**

     * 辅助方法,

     * 转换ConstraintViolationException中的Set<ConstraintViolations>中为List<message>.

     */

    publicstatic List<String> extractMessage(ConstraintViolationException e) {

        return extractMessage(e.getConstraintViolations());

    }

 

    /**

     * 辅助方法, 转换Set<ConstraintViolation>List<message>

     */

    @SuppressWarnings("rawtypes")

    publicstatic List<String> extractMessage(Set<? extends ConstraintViolation> constraintViolations) {

        List<String> errorMessages = Lists.newArrayList();

        for (ConstraintViolation violation : constraintViolations) {

            errorMessages.add(violation.getMessage());

        }

        returnerrorMessages;

    }

 

    /**

     * 辅助方法,

     * 转换ConstraintViolationException中的Set<ConstraintViolations>Map<property,

     * message>.

     */

    publicstatic Map<String, String> extractPropertyAndMessage(ConstraintViolationException e) {

        return extractPropertyAndMessage(e.getConstraintViolations());

    }

 

    /**

     * 辅助方法, 转换Set<ConstraintViolation>Map<property, message>.

     */

    @SuppressWarnings("rawtypes")

    publicstatic Map<String, String> extractPropertyAndMessage(

            Set<? extends ConstraintViolation> constraintViolations) {

        Map<String, String> errorMessages = Maps.newHashMap();

        for (ConstraintViolation violation : constraintViolations) {

            errorMessages.put(violation.getPropertyPath().toString(), violation.getMessage());

        }

        returnerrorMessages;

    }

 

    /**

     * 辅助方法,

     * 转换ConstraintViolationException中的Set<ConstraintViolations>List<propertyPath

     * message>.

     */

    publicstatic List<String> extractPropertyAndMessageAsList(ConstraintViolationException e) {

        return extractPropertyAndMessageAsList(e.getConstraintViolations(), " ");

    }

 

    /**

     * 辅助方法, 转换Set<ConstraintViolations>List<propertyPath message>.

     */

    @SuppressWarnings("rawtypes")

    publicstatic List<String> extractPropertyAndMessageAsList(

            Set<? extends ConstraintViolation> constraintViolations) {

        return extractPropertyAndMessageAsList(constraintViolations, " ");

    }

 

    /**

     * 辅助方法,

     * 转换ConstraintViolationException中的Set<ConstraintViolations>List<propertyPath

     * +separator+ message>.

     */

    publicstatic List<String> extractPropertyAndMessageAsList(ConstraintViolationException e, String separator) {

        return extractPropertyAndMessageAsList(e.getConstraintViolations(), separator);

    }

 

    /**

     * 辅助方法, 转换Set<ConstraintViolation>List<propertyPath +separator+ message>.

     */

    @SuppressWarnings("rawtypes")

    publicstatic List<String> extractPropertyAndMessageAsList(Set<? extends ConstraintViolation> constraintViolations,

            String separator) {

        List<String> errorMessages = Lists.newArrayList();

        for (ConstraintViolation violation : constraintViolations) {

            errorMessages.add(violation.getPropertyPath() + separator + violation.getMessage());

        }

        returnerrorMessages;

    }

}

 

Controller层调用代码(object是需要验证的类):

protected Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

 

 

        try {

            BeanValidators.validateWithException(validator, user);

        } catch (ConstraintViolationException e) {

            List<String> list = BeanValidators.extractPropertyAndMessageAsList(e, ": ");

            list.add(0, "数据验证失败:");

            //处理逻辑

 

        }