SpringBoot2.7+借助Validator帮助进行校验

Validator

实际上Validator在前端中后端中都有,本次介绍后端中我们常使用Validator进行数据校验

常用注解

注解说明
@AssertTrue用于boolean字段,该字段只能为true
@AssertFalse用于boolean字段,该字段只能为false
@CreditCardNumber对信用卡号进行一个大致的验证
DecimalMax只能小于或等于该值
@DecimalMin只能大于或等于该值
@Email检查是否是一个有效的email地址
@Future检查该字段的日期是否是属于将来的日期
@Length(min=,max=)检查所属的字段的长度是否在min和max之间,只能用于字符串
@Max该字段的值只能小于或等于该值
@Min该字段的值只能大于或等于该值
@NotNull不能为null
@NotBlank不能为空,检查时会将空格忽略
@NotEmpty不能为空,这里的空是指空字符串
@Pattern(regex=)被注释的元素必须符合指定的正则表达式
@URL(protocol=,host,port)检查是否是一个有效的URL,如果提供了protocol,host等,则该URL还需满足提供的条件

QuickStart

1.添加依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

2.编写Controller加上@Validated

@RestController
//开启校验
@Validated
public class TestController {}

3.简单校验(校验传递参数,非对象类型)

使用简单校验我们通常用于校验一些简单的数据传输的方法,比如get方法和delete方法,通常传输的是非表单类数据,此类数据不会解析成对象类型所以可以直接使用简单校验

基础请求

如:

    @RequestMapping("/test5")
    public String getId4(@NotNull(message = "id不能为空") Integer id) {
        System.out.println(id);
        return "over4!";
    }

这是一个最简单的请求,我们在参数前加上了@NotNull注解表示参数不能为空,接下来我们进行请求,结果如下
在这里插入图片描述

在这里插入图片描述
我们可以看到页面为500的错误并且,后端报了我们设置在@NotNull注解中的错误

Get请求

接下来我们测试Get请求

    @GetMapping("/test4")
    public String getId3(@NotNull(message = "id不能为空") Integer id) {
        System.out.println(id);
        return "over3!";
    }

经过测试,你将看到和刚刚基础的@RequestMapping相同的结果

借助@RequestParam注解引导接收的Get请求

这次我们使用@RequestParam("id")进行接收

    @GetMapping("/test3")
    public String getId2(@RequestParam("id") @NotNull(message = "id不能为空") Integer id) {
        System.out.println(id);
        return "over2!";
    }

这次的结果有些不同了
在这里插入图片描述
在这里插入图片描述
这次并不是500,而是400说明请求是一个不好的请求策略即Bad request,这是因为使用@RequestParam会将请求参数中的id与变量进行绑定,若你的请求并没有id则认为你的请求有问题,是一个Bad Request,而且后端也不会认为后端接收有问题,因为这说明是前端的问题!你自然在后端找不到任何报错,相应的也不会进行校验数据这步,所以根本不会报出@NotNull中的错误消息

使用RestFul风格进行Get请求

我们现在在开发中常会使用RestFul风格的请求,所以我们也来尝试一下

    @GetMapping("/test2/{id}")
    public String getId(@PathVariable("id") @NotNull(message = "id不能为空") Integer id) {
        System.out.println(id);
        return "over!";
    }

请求结果如下:
在这里插入图片描述
没错你会直接看到一个404,这是什么原因呢?
实际上RestFul风格是将请求参数看作静态资源进行请求,在处理的时候会获取{}中的参数再进行转化,所以不带有直接行为的请求当然找不到资源
这里的直接行为指的是直接把参数的数值绑定上去进行请求
所以后端也就认为你请求了一个我没有的资源我就无法给你提供处理,自然也不会进行校验

4.Post请求的校验

如下我们编写一个User类

User
package com.example.validatortest;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotEmpty;

@Data
public class User {
    @NotEmpty(message = "用户ID不能为空")
    @Length(max = 15,min = 6,message = "用户名长度为6~15位")
    private String username;
    @NotEmpty(message = "用户email不能为空")
    @Email(message = "邮箱错误请检查")
    private String email;
    @Max(value = 18,message = "超过18岁无法使用")
    private int age;
}


添加Post请求
    @PostMapping("/test")
    public String test( @Validated @RequestBody User user) {
        System.out.println(user);
        return "ok!";
    }
测试

首先测试正常请求
我们使用ApiFox进行发送请求
在这里插入图片描述
如下正常情况下通过
在这里插入图片描述
接下来我们测试请求参数错误情况
此时我们将用户名和年龄进行修改如下
在这里插入图片描述
请求后返回的结果是400说明请求是个Bad Request我们看控制台发现
在这里插入图片描述
控制台没有报Error而是报Warn
在这里插入图片描述
在这里插入图片描述
对Warn进行查看发现确实有输出我们设置校验错误的信息,说明是进行了校验的

分组校验

我们可以定义两个内部接口进行指定
示例如下

1.指定校验规则的组

  @NotEmpty(groups = {Define.class})

2.编写内部接口

只要写个空实现就可以,用于区别

 private interface Define{

    }

3.在@Validated中指明组

@Validated(value={Define.class})

这样标志位Define.class的就会进行校验,完成分组

统一异常处理

我这里直接给大家写好了,拿了直接用就行

package com.example.validatortest;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;

@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
public class CHandler {

    @ExceptionHandler({BindException.class, ConstraintViolationException.class})
    public String handler(HttpServletRequest request, Exception ex){
        String msg="";
        if (ex instanceof ConstraintViolationException){
            ConstraintViolationException ce = (ConstraintViolationException) ex;
            ConstraintViolation<?> next = ce.getConstraintViolations().iterator().next();
            msg = next.getMessage();
        }else if(ex instanceof BindException){
            BindException be = (BindException) ex;
            List<FieldError> fieldErrors = be.getBindingResult().getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                msg+=fieldError.getDefaultMessage()+"||||";
            }
        }
        return msg;
    }
}

经过统一异常处理后测试POST

我们再次重新测试前面的POST请求发现:

在这里插入图片描述
返回结果已经完成了自定义的显示
当然再测试前面的GET请求也是一样

@Valid@Validated区别

  1. @Validated支持方法参数自动校验
  2. @Validated支持分组校验

校验拦截(我这样称呼)

我们需要实现校验发现第一个错误就直接停止,后续不需要进行校验,直接返回错误信息,这样可以大幅度缩减校验时间
其实我们称为快速失败返回模式
以下代码直接开启了全局的快速失败返回模式,使用了@Configuration注解

package com.example.validatortest;

import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

@Configuration
public class ValidateConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                //设置快速失败返回模式
                .addProperty("hibernate.validator.fail_fast","true")
                .buildValidatorFactory();
        return validFactory.getValidator();
    }

    //开启快速返回,参数校验有异常直接抛出不进入controller中,使用全局异常拦截
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor(){
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        postProcessor.setValidator(validator());
        return postProcessor;
    }
}

使用自定义注解方式开启

我们可以采用自定义注解方式控制快速失败返回模式是否开启

1.去除前面的@Configuration注解
package com.example.validatortest;

import org.hibernate.validator.HibernateValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;

import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;


public class ValidateConfiguration {
    @Bean
    public Validator validator(){
        ValidatorFactory validFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                //设置快速失败返回模式
                .addProperty("hibernate.validator.fail_fast","true")
                .buildValidatorFactory();
        return validFactory.getValidator();
    }

    //开启快速返回,参数校验有异常直接抛出不进入controller中,使用全局异常拦截
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor(){
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        postProcessor.setValidator(validator());
        return postProcessor;
    }
}

2.编写注解

目的是使用该注解可以直接配置ValidateConfiguration这个类,相当于完成@Configuration注解的事情

package com.example.validatortest;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ValidateConfiguration.class)
public @interface EnableQuickCallBack {
}

3.在启动类上加上自定义的注解开启
4.测试

加上之前
在这里插入图片描述
校验了所有的

加上之后
在这里插入图片描述

解决RestFul风格存在的校验问题

前面我们使用RestFul风格的请求都是反馈为404表示无法请求到资源
我们现在进行解决
首先我们需要知道RestFul的请求常使用在对用户资源请求是绑定userId,或是请求某类资源时,比如博客的文章资源,由于资源很多,所以采用uuid或者雪花算法类进行设置,这样的请求携带的参数是很长的一个ID值,我们后端不能使用Integer或int接收或者返回而是使用String,以为前端常会因为精度错误的问题导致一些类的错误

解决

  1. 将后端接收的参数设置为String类型进行接收
  2. 与前端约定传递包含默认值例如:0,解释一下,前端通过AJAX或Axios进行传递必然需要携带参数,将参数设定一个默认值表示请求资源需要被直接拦截
  3. 实际无需担心这类无资源的问题

这里解释一下第三点,因为若前端需要向后端请求RestFul风格的请求绝大多数情况下前端已经拿到了后端传递给前端的资源列表,从资源列表中获取值进行前端,本身就一定带有值,而且这个值本来就是后端传给前端的,对于后端来说只要这个资源没有在更新给前端之前就删除,前端一定能够根据给出的ID请求到,所以根本无需担心,而且我们的Validator的主要作用还是帮助我们进行表单的校验,不要本末倒置

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是Spring Boot Redis验证码登录功能的基本步骤: 1. 首先在Spring Boot项目中添加依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> ``` 2. 创建一个Redis配置类,并配置Redis连接信息: ```java @Configuration public class RedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379)); } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } } ``` 3. 创建一个用户实体类,并添加相应的字段: ```java @Data @AllArgsConstructor @NoArgsConstructor public class User { private String username; private String password; private String email; private String verificationCode; } ``` 4. 创建一个验证码登录控制器,并在其中添加验证码生成和校验逻辑: ```java @RestController public class VerificationCodeLoginController { private final RedisTemplate<String, Object> redisTemplate; public VerificationCodeLoginController(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } @PostMapping("/login") public String login(@RequestParam String username, @RequestParam String password, @RequestParam String verificationCode) { User user = new User(username, password, null, verificationCode); Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set<ConstraintViolation<User>> violations = validator.validate(user); if (!violations.isEmpty()) { return "Invalid user information."; } // Get the verification code from Redis. String key = "verification_code:" + username; String storedVerificationCode = (String) redisTemplate.opsForValue().get(key); if (storedVerificationCode == null || !storedVerificationCode.equals(verificationCode)) { return "Invalid verification code."; } // Remove the verification code from Redis. redisTemplate.delete(key); // Log the user in. // ... return "Login successful."; } @GetMapping("/verification-code") public String getVerificationCode(@RequestParam String username) { // Generate a random verification code. String verificationCode = RandomStringUtils.randomNumeric(6); // Save the verification code to Redis. String key = "verification_code:" + username; redisTemplate.opsForValue().set(key, verificationCode, 5, TimeUnit.MINUTES); // Return the verification code. return verificationCode; } } ``` 在以上代码中,`login()`方法用于校验用户名、密码和验证码,并根据校验结果执行登录逻辑。在校验过程中,它首先从Redis中获取该用户的验证码,并与传入的验证进行比较。如果两者不一致,则返回“Invalid verification code.”,否则从Redis中删除验证码,并执行登录逻辑。`getVerificationCode()`方法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值