SpringMVC_04

1. 服务端数据校验

1. 1 JSR303校验

1.1.1 简介

JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。
此实现与 Hibernate ORM 没有任何关系。 JSR 303 用于对 Java Bean 中的字段的值进行验证。
Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。
注:可以使用注解的方式进行验证

1.1.2 校验规则

  • 空检查
    @Null 验证对象是否为null
    @NotNull 验证对象是否不为null, 无法查检长度为0的字符串
    @NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
    @NotEmpty 检查约束元素是否为NULL或者是EMPTY.

  • Booelan检查
    @AssertTrue 验证 Boolean 对象是否为 true
    @AssertFalse 验证 Boolean 对象是否为 false

  • 长度检查
    @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
    @Length(min=, max=) Validates that the annotated string is between min and max included.

  • 日期检查
    @Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期
    @Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期
    @Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。

  • 数值检查
    建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为”“,Integer为null
    @Min 验证 Number 和 String 对象是否大等于指定的值
    @Max 验证 Number 和 String 对象是否小等于指定的值
    @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
    @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
    @Digits 验证 Number 和 String 的构成是否合法
    @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
    @Range(min=, max=) 被指定的元素必须在合适的范围内
    @Range(min=10000,max=50000,message=”range.bean.wage”)
    @Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
    @CreditCardNumber信用卡验证
    @Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
    @ScriptAssert(lang= ,script=, alias=)
    @URL(protocol=,host=, port=,regexp=, flags=)

1.2 用法

1.2.1 添加jar包

    <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.16.Final</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator-annotation-processor</artifactId>
      <version>6.0.16.Final</version>
    </dependency>

1.2.2 为参数对象添加注解

    @RequestMapping("/register")
    public String register(@Valid UserVo userVo, Errors error){
        //手动进行服务端数据校验
        //if(userVo.getAge() < 0 || userVo.getAge() > 120){
        //    error.reject("年龄只能在0-120之间");//手动添加错误
        //}
        //判断是否有错误
        if(error.hasErrors()){
            System.out.println(error);
            return "register";
        }
        System.out.println("UserController.register.userVo:" + userVo);
        return "success";
    }

1.2.3 为属性添加校验注解

在UserVo类中,在age属性上方添加如下注解

	@Range(min = 1,max = 120,message = "年龄必须在1­120之间")
    private Integer age;

运行程序,在注册页面年龄一栏输入非法年龄,控制台输出如下
org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'userVo' on field 'age': rejected value [123]; codes [Range.userVo.age,Range.age,Range.java.lang.Integer,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [userVo.age,age]; arguments []; default message [age],120,1]; default message [年龄必须在1­120之间]

1.2.4 为其他属性添加校验注解

    @NotEmpty(message = "用户名不能为空")
    @Pattern(regexp = "\\w{6,10}",message = "用户名只能包含数字、字母、下划线,且 长度为6­10位")
    private String username;

    @Length(min = 4,max = 10,message = "密码必须为4­10位")
    private String password;

    @Pattern(regexp = "(139|133|131)\\d{8}",message = "手机号码格式不正确")
    private String phone;

    @Email(message = "邮箱格式不正确")
    private String email;

2. 数据类型转换

我们在注册页面添加一个地址输入框,然后添加一个实体类Address,其有province和city属性。
在UserVo中添加属性Address,通过用POST提交的方式来获取表单中的地址信息。

Filter解决中文乱码问题

首先遇到一个小问题,就是在注册页面填写中文地址后,控制台界面输出乱码
在这里插入图片描述
只需要在web.xml中添加过滤器即可

    <filter>
      <filter-name>encodingFilter</filter-name>
      <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
      <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
      </init-param>
    </filter>
    <filter-mapping>
      <filter-name>encodingFilter</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>

添加完过滤器后,在注册页面输入地址[北京-海淀],控制台输出错误信息。从前端传入的地址信息是String类型,而在后端需要接收Address类型的数据,所以需要进行类型转换
在这里插入图片描述

2.1 类型转换的方式

数据绑定流程:获取值——>查找转换器——>转换——>后台校验——>数据绑定
两种解决方式:

  1. 方式一:使用PropertyEditor
  2. 方式二:使用Converter(推荐)

2.2 使用PropertyEditor方式

2.2.1 使用步骤

  1. 定义属性编辑器
package editor;

import entity.Address;

import java.beans.PropertyEditorSupport;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AddressEditor extends PropertyEditorSupport {
    @Override
    public String getAsText() {
        Address address = (Address)getValue();
        return "[" + address.getProvince() + "-" + address.getCity() + "]";
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        Pattern pattern = Pattern.compile("\\[(.*)-(.*)\\]");
        Matcher matcher = pattern.matcher(text);
        if(matcher.matches()){
            String city = matcher.group(2);
            String province = matcher.group(1);
            Address address = new Address();
            address.setCity(city);
            address.setProvince(province);
            setValue(address);
        }
    }
}

  1. 在Controller层注册属性编辑器
    在UserController.java中添加如下代码
    @InitBinder
    public void initBinder(DataBinder binder){
        binder.registerCustomEditor(Address.class, new AddressEditor());
    }

配置完之后,程序就可以正常获取前端传过来的Address值
在这里插入图片描述

2.2.2 缺点

  • 代码嵌套在Controller层中
  • 只能从字符串转换

2.3 使用Converter方式

步骤:

  1. 定义转换器
package converter;

import entity.Address;
import org.springframework.core.convert.converter.Converter;

import javax.management.RuntimeErrorException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class String2AddressConverter implements Converter<String, Address> {

    @Override
    public Address convert(String s) {
        Pattern pattern = Pattern.compile("\\[(.*)-(.*)\\]");
        Matcher matcher = pattern.matcher(s);
        if(matcher.matches()){
            String city = matcher.group(2);
            String province = matcher.group(1);
            Address address = new Address();
            address.setCity(city);
            address.setProvince(province);
            return address;
        }else {
            throw new RuntimeException("地址转换失败");
        }
    }
}
public class Address2StringConverter implements Converter<Address, String> {
    @Override
    public String convert(Address address) {
        return "[" + address.getProvince() + "-" + address.getCity() + "]";
    }
}
  1. 管理自定义转换器
    在springmvc.xml中添加管理自定义转换器的代码
    <!--管理自定义转换器-->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="converter.String2AddressConverter"/>
                <bean class="converter.Address2StringConverter"/>
            </set>
        </property>
    </bean>
  1. 加载应用自定义转换器
    以前在springmvc.xml写过如下代码:<mvc:annotation-driven/>,这是mvc的注解驱动,可以用来简化配置。而且默认加载内置的类型转换器。如果要加载自定义的类型转换器,需要作如下声明
<mvc:annotation-driven conversion-service="conversionService"/>

配置完成之后,自定义的转换器就配置好了

3. @SessionAttribute

3.1

在实际开发中,注册页面一般分为几个步骤,第一个页面只需要填用户名和密码,之后再补充详细资料。我们也试着这样完成。
于是,添加一个SessionController.java

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import vo.UserVo;

@Controller
@RequestMapping("/session")
public class SessionController {

    @RequestMapping("/step1")
    public String step1(UserVo userVo){
        System.out.println("step2.userVo: " + userVo);
        return "step1";
    }

    @RequestMapping("/step2")
    public String step2(UserVo userVo){
        System.out.println("step2.userVo: " + userVo);
        return "step2";
    }

    @RequestMapping("/step3")
    public String step3(UserVo userVo){
        System.out.println("step3.userVo: " + userVo);
        return "step3";
    }

    @RequestMapping("/register")
    public String register(UserVo userVo){
        System.out.println("register.userVo: " + userVo);
        return "success";
    }
}

将register.jsp页面分成三个step1.jsp、step2.jsp、step3.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>register1</title>
</head>
<body>
    <h2>用户注册</h2>
    <form action="${pageContext.request.contextPath}/session/step2" method="post">
        用户名:<input type="text" name="username"> <br>
        密码:<input type="password" name="password"> <br>
        <input type="submit" value="下一步">
    </form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>register2</title>
</head>
<body>
    <h2>用户注册</h2>
    <form action="${pageContext.request.contextPath}/session/step3" method="post">
        年龄:<input type="text" name="age"> <br>
        手机号:<input type="text" name="phone"> <br>
        <input type="submit" value="下一步">
    </form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>register3</title>
</head>
<body>
    <h2>用户注册</h2>
    <form action="${pageContext.request.contextPath}/session/register" method="post">
        邮箱:<input type="text" name="email"> <br>
        地址:<input type="text" name="address"> <br>
        <input type="submit" value="注册">
    </form>
</body>
</html>

程序运行后,在每个页面输入用户数据,注册成功后,查看控制台输出
在这里插入图片描述
发现userVo里的数据在每一次请求中都不一样,这时候@SessionAttributes就能排上用场了

3.2 @SessionAttribute简介

  • 若希望在多个请求之间共用数据,则可以在控制器类上标注一个 @SessionAttributes,配置需要在session中存放的数据范围,SpringMVC将存放在model中对应的数据暂存到 HttpSession 中。
  • @SessionAttributes只能使用在类定义上。
  • @SessionAttributes 除了可以通过属性名指定需要放到会 话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中 例如:
    • @SessionAttributes(types=User.class)会将model中所有类型为 User的属性添加到会话中。
    • @SessionAttributes(value={“user1”, “user2”}) 会将model中属性名为user1和user2的属性添加到会话中。
    • @SessionAttributes(types={User.class, Dept.class}) 会将model中所有类型为 User和Dept的属性添加到会话中。
    • @SessionAttributes(value={“user1”,“user2”},types={Dept.class})会将model中属性名为user1和user2以及类型为Dept的属性添加到会话中。

于是,我们在SessionController类添加上@SessionAttributes注释

@Controller
@RequestMapping("/session")
@SessionAttributes("userVo")
public class SessionController {

再此运行程序,就会得到完整的信息,控制台输出如下
在这里插入图片描述

4. 统一异常处理

4.1 简介

默认的异常页是对用户或者调用者而言都是不友好的,所以一般上我们都会进行实现自己业务的异常提示信息。
统一异常处理的方式:

  • 使用web技术提供的统一异常处理
  • 使用SpringMVC提供的统一异常处理

4.2 使用web技术

在web.xml中,根据不同的错误码,对不同的错误进行处理

  <!--404异常-->
  <error-page>
    <error-code>404</error-code>
    <location>/404.jsp</location>
  </error-page>
  <!--500异常-->
  <error-page>
    <error-code>500</error-code>
    <location>/500.jsp</location>
  </error-page>

如果出现404异常,系统就会使用404.jsp作为信息提示页
但是,使用web技术处理异常的方式比较粗略,使用SpringMVC提供的异常处理技术可以对异常进行仔细的划分。

4.3 使用SpringMVC提供的异常处理技术

步骤1

首先创建一个异常测试类

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/exception")
public class ExceptionTest {

    @RequestMapping("/test1")
    public String test1(){
        int i = 5/0;
        return "success";
    }

    @RequestMapping("/test2")
    public String test2(){
        String s = null;
        s.toString();
        return "success";
    }
}

步骤2

定义一个异常处理类ExceptionAdvice.java(通知),添加@ControllerAdvice
在异常处理类中添加定义异常处理方法,添加@ExceptionHandler

package controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler(ArithmeticException.class)
    public String arithmetic(ExceptionTest e){
        System.out.println("程序出现异常:" + e);
        return "error/arithmetic";
    }

    @ExceptionHandler(NullPointerException.class)
    public String nullPointer(ExceptionTest e){
        System.out.println("程序出现异常:" + e);
        return "error/nullpointer";
    }


    @ExceptionHandler(java.lang.Exception.class)
    public String exception(ExceptionTest e){
        System.out.println("程序出现异常:" + e);
        return "error/exception";
    }
}

步骤3

添加异常处理反馈页面,以arithmetic.jsp为例

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>arithmetic</title>
</head>
<body>
程序出现了异常:ArithmeticException
</body>
</html>
步骤4 测试

跳转到url/exception/test1时,会跳转到如下页面
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值