Java教程

day68(Validation框架,跨域问题,关于客户端提交请求参数的格式,处理登录,开发流程)

本文主要是介绍day68(Validation框架,跨域问题,关于客户端提交请求参数的格式,处理登录,开发流程),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

day68(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:可以配置minmax属性,同时限制最小值和最大值

  • @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种做法

4.处理登录

1.开发流程

1.先整理出当前项目涉及的数据的类型

  • 例如:电商类包含用户、商品、购物车、订单等

2.再列举各种数据类型涉及的数据操作

  • 例如:用户类型涉及注册、登陆等

3.再挑选相对简单的数据类型先处理

  • 简单的易于实现,且可以积累经验

4.在各类数据涉及到的数据操作中,大致遵循增、查、删、改的开发顺序

  • 只有先增,还可能查、删、改

  • 只有查了以后,才能明确有哪些数据,才便于实现删、改

  • 删和改相比,删一般更加简单,所以先开发删,再开发改

5.在开发具体的数据操作时,应该大致遵循持久层 >> 业务逻辑层 >> 控制器层 >> 前端页面的开发顺序

2.管理员登陆-持久层

1.创建或配置(第一次开发)

  1. 配置(Spring Boot项目)

    1. 使用@MapperScan配置接口所在的根包

    2. 在配置文件中通过mybatis.mapper-locations配置XML文件的位置

  2. 处理某种类型数据的持久层访问,需要:

    1. 创建接口

    2. 创建XML文件

2.规划需要执行的SQL语句

  1. SQL大致语句:

    select * from ams_admin where username=?
  2. 由于不允许使用*号,因此细分为:

    select id,username,password,nickname,avatar,is_enable from ams_admin username=?
  3. 提示:理论上,还应该查出login_count,当登录成功后,还应该更新login_countgmt_last_login等数据,此次暂不考虑。

3.在接口中添加抽象方法(含创建必要的VO类)

1.所有查询结果都应该使用VO类

  1. 不要使用实体类,根据阿里的开发规范,每张数据表中都应该有idgmt_creategmt_modified这3个字段,而gmt_creategmt_modified这2个字段都是用于特殊情况下排查问题的,一般情况下均不会使用,所以,如果使用实体类,必然存在多余的属性,同时,由于不使用星号作为字段列表,则一般也不会查询这2个字段的值,会导致实体类对象中永远至少存在2个属性为null

  2. 根据以上提示,以前已经写好的getByUsername()是不规范的,应该调整已存在此方法,本次并不需要添加新的抽象方法。

  3. 创建cn.tedu.boot.demo.pojo.vo.AdminSimpleVO类,添加此次查询时需要的属性:

    package cn.tedu.boot.demo.pojo.vo;
    ​
    @Data
    public class AdminSimpleVO implements Serializable {
        private Long id;
        private String username;
        private String password; 
        private String nickname; 
        private String avatar;
        private Integer isEnable;
    }

2.修改AdminMapper接口文件

  1. 将Admin getByUsername(String username)改为

AdminSimpleVO getByUsername(String username);
  1. 因为修改了源代码,所以调用了原方法的代码都会出现错误,包括:

    1. 测试

    2. 业务逻辑层的实现类

  2. 因及时修改,但是由于未完成SQL配置,相关代码暂时不能运行

4.在XML中的配置SQL

1.调整AdminMapper.xml

  • 删除<sql>中不必查询的字段,注意:此字段不要有多余的逗号

  • 修改<resultMap>节点的type属性值

  • <resultMap>节点下,删除不必要的配置

    <select id="getByUsername" resultMap="BaseResultMap">
        select
            <include refid="BaseQueryFields" />
        from
             ams_admin
        where
             username=#{username}
    </select>
    ​
    <sql id="BaseQueryFields">
        <if test="true">
            id,
            username,
            password,
            nickname,
            avatar,
            is_enable
        </if>
    </sql>
    ​
    <resultMap id="BaseResultMap" type="cn.tedu.boot.demo.pojo.vo.AdminSimpleVO">
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <result column="nickname" property="nickname" />
        <result column="avatar" property="avatar" />
        <result column="is_enable" property="isEnable" />
    </resultMap>

5.编写并执行测试

此次并不需要编写新的测试,使用原有的测试即可!

注意:由于本次是修改了原“增加管理员”就已经使用的功能,应该检查原功能是否可以正常运行。

3.管理员登录-业务逻辑层

1.创建

如果第1次处理某种类型数据的业务逻辑层访问,需要:

  • 创建接口

  • 创建类,实现接口,并在类上添加@Service注解

本次需要开发的“管理员登录”并不需要再做以上操作

2.在接口中添加抽象方法(含创建必要的DTO类)

3.在实现类中设计(打草稿)业务流程与业务逻辑(含创建必要的异常类)

4.在实现类中实现业务

5.编写并执行测试

4.管理员登录-控制器层

1.创建

如果是整个项目第1次开发控制器层,需要:

  • 创建统一处理异常的类

    • 添加@RestControllerAdvice

  • 创建统一的响应结果类型及相关类型

    • 例如:JsonResultState

如果第1次处理某种类型数据的控制器层访问,需要:

  • 创建控制器类

    • 添加@RestController

    • 添加@RequestMapping

本次需要开发的“管理员登录”并不需要再做以上操作

2.添加处理请求的方法

3.处理异常(按需)

4.测试

5.管理员登录-前端页面

这篇关于day68(Validation框架,跨域问题,关于客户端提交请求参数的格式,处理登录,开发流程)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!