day68(SpringBoot4:Validation框架,跨域问题,关于客户端提交请求参数的格式,处理登录,开发流程)
day68(SpringBoot4:Validation框架,跨域问题,关于客户端提交请求参数的格式,处理登录,开发流程)
1. Validation框架
1.validation作用
当客户端向服务器提交请求时,如果请求数据出现明显的问题(例如关键数据为null
、字符串的长度不在可接受范围内、其它格式错误),应该直接响应错误,而不是将明显错误的请求参数传递到Service!
关于判断错误,只有涉及数据库中的数据才能判断出结果的,都由Service进行判断,而基本的格式判断,都由Controller进行判断。
Validation框架是专门用于解决检查数据基本格式有效性的,最早并不是Spring系列的框架,目前,Spring Boot提供了更好的支持,所以,通常结合在一起使用。
2.添加依赖
在Spring Boot项目中,需要添加spring-boot-starter-validation
依赖项,例如:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
3.添加注解
在控制器中,首先,对需要检查数据格式的请求参数添加@Valid
或@Validated
注解(这2个注解没有区别),例如:
@RequestMapping("/add-new")
public JsonResult<Void> addNew(@Validated AdminAddNewDTO adminAddNewDTO) {
adminService.addNew(adminAddNewDTO);
return JsonResult.ok();
}
真正需要检查的是AdminAddNewDTO
中各属性的值,所以,接下来需要在此类的各属性上通过注解来配置检查的规则,例如:
@Data
public class AdminAddNewDTO implements Serializable {
@NotNull // 验证规则为:不允许为null
private String username;
// ===== 原有其它代码 =====
}
4.处理报错
重启项目,通过不提交用户名的URL(例如:http://localhost:8080/admins/add-new)进行访问,在浏览器上会出现400错误页面,并且,在IntelliJ IDEA的控制台会出现以下警告:
2022-06-07 11:37:53.424 WARN 6404 --- [nio-8080-exec-8] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [
org.springframework.validation.BindException:
org.springframework.validation.BeanPropertyBindingResult: 1 errors<EOL>Field error in object 'adminAddNewDTO' on field 'username': rejected value [null]; codes [NotNull.adminAddNewDTO.username,NotNull.username,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [adminAddNewDTO.username,username]; arguments []; default message [username]]; default message [不能为null]]
从警告信息中可以看到,当验证失败时(不符合所使用的注解对应的规则时),会出现org.springframework.validation.BindException
异常,则自行处理此异常即可!
1.添加枚举
首先,在State
中添加新的枚举:
public enum State {
OK(200),
ERR_USERNAME(201),
ERR_PASSWORD(202),
ERR_BAD_REQUEST(400), // 新增
ERR_INSERT(500);
// ===== 原有其它代码 =====
}
2.添加异常处理方法
然后,在GlobalExceptionHandler
中添加新的处理异常的方法:
@ExceptionHandler(BindException.class)
public JsonResult<Void> handleBindException(BindException e) {
return JsonResult.fail(State.ERR_BAD_REQUEST, e.getMessage());
}
完成后,再次重启项目,继续使用为null
的用户名提交请求时,可以看到异常已经被处理,此时,响应的JSON数据例如:
{
"state":400,
"message":"org.springframework.validation.BeanPropertyBindingResult: 2 errors\nField error in object 'adminAddNewDTO' on field 'username': rejected value [null]; codes [NotNull.adminAddNewDTO.username,NotNull.username,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [adminAddNewDTO.username,username]; arguments []; default message [username]]; default message [不能为null]\nField error in object 'adminAddNewDTO' on field 'password': rejected value [null]; codes [NotNull.adminAddNewDTO.password,NotNull.password,NotNull.java.lang.String,NotNull]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [adminAddNewDTO.password,password]; arguments []; default message [password]]; default message [不能为null]"
}
3.@NotNull注解
关于错误提示信息,以上内容中出现了不能为null
的字样,是默认的提示文本,可以通过@NotNull
注解的message
属性进行配置,例如:
@Data
public class AdminAddNewDTO implements Serializable {
@NotNull(message = "添加管理员失败,请提交用户名!")
private String username;
@NotNull(message = "添加管理员失败,请提交密码!")
private String password;
// ===== 原有其它代码 =====
}
4.自定义提示文本
然后,在处理异常时,通过异常信息获取自定义的提示文本:
@ExceptionHandler(BindException.class)
public JsonResult<Void> handleBindException(BindException e) {
BindingResult bindingResult = e.getBindingResult();
String defaultMessage = bindingResult.getFieldError().getDefaultMessage();
return JsonResult.fail(State.ERR_BAD_REQUEST, defaultMessage);
}
再次运行,在不提交用户名和密码的情况下,会随机的提示用户名或密码验证失败的提示文本中的某1条。
5.validation注解
在Validation框架中,还有其它许多注解,用于进行不同格式的验证,例如:
@NotEmpty
:只能添加在String
类型上,不许为空字符串,例如""
即视为空字符串@NotBlank
:只能添加在String
类型上,不允许为空白,例如普通的空格可视为空白,使用TAB键输入的内容也是空白,(虽然不太可能在此处出现)换行产生的空白区域也是空白@Size
:限制大小@Min
:限制最小值@Max
:限制最大值@Range
:可以配置min
和max
属性,同时限制最小值和最大值@Pattern
:只能添加在String
类型上,自行指定正则表达式进行验证- 其它
以上注解,包括@NotNull
是允许叠加使用的,即允许在同一个参数属性上添加多个注解!
以上注解均可以配置message
属性,用于指定验证失败的提示文本。
通常:
- 对于必须提交的属性,都会添加
@NotNull
- 对于数值类型的,需要考虑是否添加
@Range
(则不需要使用@Min
和@Max
) - 对于字符串类型,都添加
@Pattern
注解进行验证
2.跨域问题
1.出现原因
因为现在开发大部分都是前后端分离开发,两个作为独立的项目,独立开发与部署。所以前端直接向后端发送异步请求,前端会出现以下错误
Access to XMLHttpRequest at 'http://localhost:8080/admins/add-new' from origin 'http://localhost:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
这种问题被称为跨域问题,关键字是CORS
2.解决方法
1.配置配置类
基于SpringMVC项目框架中,需要一个SpringMVC的配置类(实现了WebMvcConfigurer接口的类),并重新方法,以允许指定符合条件的进行跨域
package cn.tedu.boot.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
3.关于客户端提交请求参数的格式
1.直接通过&
拼接各参数与值
例如:
// username=root&password=123456&nickname=jackson&phone=13800138001&email=jackson@baidu.com&description=none
let data = 'username=' + this.ruleForm.username
+ '&password=' + this.ruleForm.password
+ '&nickname=' + this.ruleForm.nickname
+ '&phone=' + this.ruleForm.phone
+ '&email=' + this.ruleForm.email
+ '&description=' + this.ruleForm.description;
2.使用JSON语法来组织各参数与值
例如:
let data = {
'username': this.ruleForm.username, // 'root'
'password': this.ruleForm.password, // '123456'
'nickname': this.ruleForm.nickname, // 'jackson'
'phone': this.ruleForm.phone, // '13800138001'
'email': this.ruleForm.email, // 'jackson@baidu.com'
'description': this.ruleForm.description // 'none'
};
3.使用场景
具体使用哪种做法,取决于服务器端的设计:
- 如果服务器端处理请求的方法中,在参数前添加了
@RequestBody
,则允许使用以上第2种做法(JSON数据)提交请求参数,不允许使用以上第1种做法(使用&
拼接) - 如果没有使用
@RequestBody
,则只能使用以上第1种做法