Java 阶段三 Day14 统一响应结果及异常的处理及 Spring Validations 如何实现请求数据校验

一、统一响应结果的处理

首先,先了解一下什么是状态码。
状态码(StatusCode)是一种在计算机通信中用于表示请求的处理结果的数字或字符编码。它们通常用于HTTP协议中,但也可以在其他通信协议和系统中找到。状态码的目的是提供有关请求的处理情况的信息,以便请求发起方能够了解请求是否成功、失败以及失败的原因。

在HTTP协议中,状态码由三位数字组成,通常分为以下五个类别:

  1. 1xx(Informational): 这一类状态码表示服务器已经接收到请求,但请求的处理尚未完成。最常见的1xx状态码是100(Continue),它表示服务器已经准备好继续处理请求。

  2. 2xx(Successful): 这一类状态码表示请求已成功接收、理解并接受。最常见的2xx状态码是200(OK),它表示请求已成功处理。

  3. 3xx(Redirection): 这一类状态码表示客户端需要采取额外的操作以完成请求。通常,这些状态码用于重定向客户端到其他位置。例如,302(Found)状态码表示请求的资源已被临时移动到其他位置。

  4. 4xx(Client Error): 这一类状态码表示客户端发送了错误的请求或请求无法完成。最常见的4xx状态码是404(Not Found),它表示请求的资源未找到。

  5. 5xx(Server Error): 这一类状态码表示服务器在处理请求时发生了错误。最常见的5xx状态码是500(Internal Server Error),它表示服务器遇到了未处理的异常。

每个状态码都有特定的含义和用途,它们帮助客户端和服务器之间进行有效的通信,以确保请求的正确处理和错误处理。状态码是HTTP协议中的关键组成部分,它们允许客户端和服务器之间就请求的状态和结果进行明确的交流。

状态码类的定义(StatusCode)

  • 需求
    当项目中的状态码越来越多时,对状态码的定义没有统一规划,后续对状态的理解就会相当的困难,而且容易导致操作上的失败。

  • 解决方案【定义枚举类(enum类),统一封装状态码】

    import lombok.Data;
    import lombok.Getter;
    
    /**
    * 通过此枚举定义一些响应状态码信息,
    * 实际项目中会基于不同业务定义不同状态码对象
      */
    @Getter
    public enum StatusCode {
          
          
        NOT_LOGIN(1000,"未登录"), //所有实例必须是在最上面的
        LOGIN_SUCCESS(1001,"登录成功"),
        PASSWORD_ERROR(1002,"密码错误"),//对象(枚举实例)
        USERNAME_ERROR(1003,"用户名错误"),
        OPERATION_SUCCESS(2001,"操作成功"),
        OPERATION_FAILED(2002,"操作失败");
        /**表示状态码*/
        private int code;
        /**状态码信息*/
        private String msg;
        //所有构造方法默认使用private访问修饰符修饰
        StatusCode(){
          
          }
        /**构造方法*/
        StatusCode(int code,String msg){
          
          
            this.code=code;
            this.msg=msg;
        }
    }
    

响应结果封装类的定义(ResponseVO)

  • 需求
    为更好定义服务端返回值的格式,统一客户端对服务端响应结果的处理,将服务端返回到客户端的数据再次进行封装。

  • 解决方案(定义ResultVO对象)

    import lombok.*;
    
    /**
     * 统一响应结果的封装类型
     */
    @Setter
    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public class ResultVO {
          
          
        /**响应状态码(业务状态码)*/
        private int code;
        /**服务端要返回给客户端的消息(例如,密码不正确)*/
        private String msg;
        /**服务端要返回给客户端的具体数据*/
        private Object data;
    
        /**通过构造方法对属性初始化*/
        public ResultVO(Object data){
          
          
            this.code=StatusCode.SUCCESS.getCode();
            this.msg=StatusCode.SUCCESS.getMsg();
            this.data=data;
        }
        /**通过构造方法对属性初始化*/
        public ResultVO(StatusCode statusCode){
          
          
            this.code=statusCode.getCode();
            this.msg=statusCode.getMsg();
        }
        /**通过构造方法对象属性初始化*/
        public ResultVO(StatusCode statusCode,Object data){
          
          
            this(statusCode);//调用本类中其它方法
            this.data=data;
        }
    }
    
  • 应用ResultVO对象
    在WeiboController中添加如下方法,然后进行访问测试

    @GetMapping("doSelectIndex")
    @ApiOperation(value = "微博列表的统一封装")
    public ResultVO doSelectIndex(){
          
          
        return new ResultVO(weiboMapper.selectIndex());
    }
    

    在UserController中添加如下方法,然后进行访问测试

     /**登录功能*/
     @PostMapping("doLogin")
     @ApiOperation(value = "封装登录响应结果")
     // 1.添加会话参数
     public ResultVO doLogin(@RequestBody UserLoginDTO userLoginDTO, HttpSession session){
          
          
     	System.out.println("userLoginDTO = " + userLoginDTO);
        UserVO userVO = userMapper.selectByUsername(userLoginDTO.getUsername());
    
        if (userVO != null){
          
          
            // 判断密码
        	if (userVO.getPassword().equals(userLoginDTO.getPassword())){
          
          
            	// 2.将该用户的登录状态的会话保存起来
                session.setAttribute("user", userVO);
                return new ResultVO(StatusCode.LOGIN_SUCCESS); // 登录成功
            }
            return new ResultVO(StatusCode.PASSWORD_ERROR); // 密码错误
        }
        return new ResultVO(StatusCode.USERNAME_ERROR); // 用户名不存在
    }
    

Tomcat处理流程

在这里插入图片描述

Spring 中的统一响应结果的封装

在这里插入图片描述

Spring MVC请求响应处理流程

在这里插入图片描述

二、统一异常的处理

在Spring MVC中有两个级别的异常处理方式:

  • Controller级别:它只能处理当前Controller类中出现的异常。
  • 全局的异常处理级别:所有的Spring MVC中的拦截机器(HandlerInterceptor),处理器(@Controller)中的异常。

Spring MVC提供了多种方式来处理异常,以确保在应用程序中发生异常时能够进行适当的处理和响应。以下是一些常见的Spring MVC中的异常处理方式:

  1. 使用@ControllerAdvice注解处理全局异常
    可以创建一个带有@ControllerAdvice注解的类,用于集中处理全局异常。在这个类中,你可以定义方法来处理不同类型的异常,然后使用@ExceptionHandler注解将它们与特定的异常类型关联起来。这些异常处理方法可以返回适当的HTTP状态码和错误消息。

    @ControllerAdvice
    public class GlobalExceptionHandler {
          
          
    
        @ExceptionHandler(Exception.class)
        public ModelAndView handleException(Exception ex) {
          
          
            ModelAndView modelAndView = new ModelAndView("error");
            modelAndView.addObject("errorMessage", "An error occurred: " + ex.getMessage());
            return modelAndView;
        }
    }
    
  2. 使用@ExceptionHandler注解处理Controller内的异常
    你还可以在单个Controller内使用@ExceptionHandler注解来处理该Controller内部的异常。这允许你为特定的Controller定义异常处理方法。

    @Controller
    public class MyController {
          
          
    
        @ExceptionHandler(MyCustomException.class)
        public ModelAndView handleCustomException(MyCustomException ex) {
          
          
            ModelAndView modelAndView = new ModelAndView("error");
            modelAndView.addObject("errorMessage", "Custom error: " + ex.getMessage());
            return modelAndView;
        }
    }
    
  3. 使用SimpleMappingExceptionResolver
    Spring提供了SimpleMappingExceptionResolver,可以用于配置全局异常处理。你可以定义异常类型与视图名称的映射,以及其他配置选项。这个解析器可以在Spring配置文件中进行配置。

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="MyCustomException">errorView</prop>
            </props>
        </property>
    </bean>
    
  4. 使用自定义异常处理器
    你还可以创建自定义的异常处理器类,实现HandlerExceptionResolver接口。这个自定义异常处理器可以根据需要进行高度定制,例如,将异常信息记录到日志、发送邮件通知等。

    public class MyExceptionHandler implements HandlerExceptionResolver {
          
          
    
        @Override
        public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
          
          
            // Custom exception handling logic
            ModelAndView modelAndView = new ModelAndView("error");
            modelAndView.addObject("errorMessage", "An error occurred: " + ex.getMessage());
            return modelAndView;
        }
    }
    

这些是Spring MVC中处理异常的常见方式。你可以根据应用程序的需求选择合适的方式来处理异常,并提供适当的错误信息和响应。无论选择哪种方式,都能够提高应用程序的稳定性和可维护性。

当然,Spring MVC中有内置的异常处理对象,但是呈现的结果对于用户端不友好,所以实际项目我们一般会自定义异常处理。

全局异常处理对象的定义

我们可以在spring boot启动类所在的包或子包中定义Spring MVC全局异常处理类,这个类的特点是需要使用@RestControllerAdvice注解进行描述,并且可以在类中定义多个异常处理方法(需要使用@HandlerException注解进行描述), 例如:

package com.zhikai.exception;

import com.zhikai.common.response.ResultVO;
import com.zhikai.common.response.StatusCode;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

/**
 * RestControllerAdvice 描述的类型为一个全局异常处理对象类型,
 * 当某个Controller方法中出现了异常,此时在Controller类内部,又没有定义对应的
 * 异常处理方法(使用ExcetpionHandler注解描述的方法),系统底层就会查找有没有定义全局异常处理对象。
 * 这个全局异常处理对象中有没有定义对应的异常处理方法,假如有就调用此方法处理异常。
 */

@Slf4j //lombok提供的日志注解,在代码层面会为我们提供一个org.slf4j.Logger对象
@RestControllerAdvice //=@ControllerAdvice+@ResponseBody
public class GlobalExceptionHandler {
    
    
    //private static final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);
    /**
     *  @ExceptionHandler 描述的方法为一个异常处理方法,在此注解内部可以定义具体的异常处理
     *  类型(例如RuntimeException.class),此注解描述的方法需要定义一个异常类型的形式参数,
     *  通过这个参数接收具体的异常对象(也可以接收其异常类型对应的子类类型的异常)。
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public ResultVO doHandleRuntimeException(RuntimeException ex){
    
    
        log.error("error is {}",ex.getMessage());//日志级别trace<debug<info<warn<error
        return new ResultVO(0,ex.getMessage());
    }
}

这里的@Slf4j注解为Lombok提供,此注解描述类时会在类中创建一个日志对象,基于日志对象可以输出一些日志,
日志的级别可以在application.properties文件中进行配置,例如logging.level.com.liner=debug,这里的debug表示日志级别(trace < debug < info < warn < error),当配置的日志界别为debug时,当前这个日志级别以及比这个日志级别高的日志级别会输出日志。

三、Spring Validation 如何实现请求数据校验

在实际项目需要对客户端传递到服务端的参数进行校验,用于判定请求参数的合法性,假如请求参数不合法,不可以再去执行后续的业务了。有两种方法:

  • 在控制层方法中每次都自己进行参数有效值的判断,不合法可以抛出异常,但是工作量和代码复杂度会比较高。
  • 采用市场上主流的 Spring Validation 框架去实现校验。

Spring Validation的应用

Spring Validation 是 Spring 框架提供的一种用于验证数据的机制,它可以帮助你确保用户输入的数据符合预期的规则和约束。Spring Validation 主要用于验证用户提交的表单数据,例如登录信息、注册信息等,以及 API 请求中的数据。

以下是在 Spring 中使用验证的一般步骤:

  1. 创建验证规则类
    首先,你需要创建一个用于验证数据的 Java 类,通常称为验证规则类。这个类应该实现 Spring 的 Validator 接口,它包括两个方法:supports()validate()

    import org.springframework.validation.Errors;
    import org.springframework.validation.ValidationUtils;
    import org.springframework.validation.Validator;
    
    public class UserValidator implements Validator {
          
          
    
        @Override
        public boolean supports(Class<?> clazz) {
          
          
            return User.class.isAssignableFrom(clazz);
        }
    
        @Override
        public void validate(Object target, Errors errors) {
          
          
            User user = (User) target;
    
            ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "NotEmpty.user.username");
            ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty.user.password");
    
            // 进一步的自定义验证逻辑
            if (user.getUsername().length() < 5) {
          
          
                errors.rejectValue("username", "Size.user.username");
            }
    
            // 其他验证规则
        }
    }
    
  2. 在控制器中应用验证规则
    在 Spring 控制器中,将上述的验证规则类应用到需要验证的数据上。你可以使用 @InitBinder 注解来将验证器绑定到控制器方法上。

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.InitBinder;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    @Controller
    public class UserController {
          
          
    
        @Autowired
        private UserValidator userValidator;
    
        @InitBinder
        protected void initBinder(WebDataBinder binder) {
          
          
            binder.setValidator(userValidator);
        }
    
        @RequestMapping(value = "/register", method = RequestMethod.POST)
        public String register(@ModelAttribute("user") User user, BindingResult result, Model model) {
          
          
            userValidator.validate(user, result);
    
            if (result.hasErrors()) {
          
          
                return "registrationForm";
            }
    
            // 处理注册逻辑
            return "registrationSuccess";
        }
    }
    
  3. 在视图中显示错误信息
    在视图中,你可以使用标签库或Thymeleaf等模板引擎来显示验证失败的错误信息。Spring 会将错误信息存储在 BindingResult 对象中,你可以从中获取错误并在页面上显示。

    <form:form method="POST" modelAttribute="user" action="/register">
        <label for="username">Username:</label>
        <form:input path="username"/>
        <form:errors path="username" cssClass="error"/>
    
        <label for="password">Password:</label>
        <form:password path="password"/>
        <form:errors path="password" cssClass="error"/>
    
        <input type="submit" value="Register"/>
    </form:form>
    
  4. 定义错误消息
    在 Spring 中,你可以定义错误消息,以便在验证失败时显示给用户。这些消息可以存储在 properties 文件中,以支持国际化。

    NotEmpty.user.username=Username is required.
    NotEmpty.user.password=Password is required.
    Size.user.username=Username must be at least 5 characters long.
    

通过以上步骤,你可以使用 Spring Validation 来验证用户输入的数据,并在验证失败时返回错误消息。这有助于确保应用程序接受有效的数据,并提高用户体验。

Spring Validation注解

以下是Spring Framework中常用的Validation注解及其说明:

  1. @NotNull:用于标记字段不能为null。

  2. @NotEmpty:用于标记字符串、集合或数组字段不能为空,即长度必须大于0。

  3. @NotBlank:用于标记字符串字段不能为空且必须至少包含一个非空白字符。

  4. @Size:用于指定字符串、集合或数组字段的长度范围。

  5. @Min:用于指定数值字段的最小值。

  6. @Max:用于指定数值字段的最大值。

  7. @Email:用于验证字符串字段是否为有效的电子邮件地址。

  8. @Pattern:用于使用正则表达式验证字符串字段。

  9. @AssertTrue:用于验证字段是否为true。

  10. @AssertFalse:用于验证字段是否为false。

  11. @DecimalMin:用于验证数字字段的最小值,通常用于BigDecimal或double类型。

  12. @DecimalMax:用于验证数字字段的最大值,通常用于BigDecimal或double类型。

  13. @Digits:用于验证数字字段的整数位数和小数位数。

  14. @Future:用于验证日期字段是否在将来。

  15. @Past:用于验证日期字段是否在过去。

  16. @FutureOrPresent:用于验证日期字段是否在现在或将来。

  17. @PastOrPresent:用于验证日期字段是否在现在或过去。

  18. @CreditCardNumber:用于验证信用卡号的有效性。

  19. @Length:用于验证字符串字段的长度。

  20. @NotBlank:用于验证字符串字段不能为空且必须至少包含一个非空白字符。

  21. @URL:用于验证字符串字段是否为有效的URL。

  22. @Valid:用于标记字段应该递归验证,通常用于嵌套对象的验证。

  23. @Min@Max:用于验证整数或长整数字段的最小值和最大值。

  24. @Size: 检查字符串、集合或数组的长度是否在指定范围内

  25. @Positive: 检查数字是否为正数

  26. @PositiveOrZero: 检查数字是否为非负数

  27. @Negative: 检查数字是否为负数

  28. @NegativeOrZero: 检查数字是否为非正数

  29. @ConvertGroup: 用于分组校验,可以指定校验的分组,根据不同的分组执行不同的校验规则

  30. @GroupSequence: 用于定义校验分组的顺序,指定不同分组的执行顺序

  31. @NotNull(message = “{user.name.notnull}”): 使用国际化消息提示

  32. @NotBlank(message = “{user.name.notblank}”): 使用国际化消息提示

  33. @NotEmpty(message = “{user.name.notempty}”): 使用国际化消息提示

  34. @Email(message = “{user.email.format}”): 使用国际化消息提示

  35. @Pattern:用于指定一个字符串必须匹配的正则表达式模式。这在输入验证或数据格式化中非常有用。以下是如何在Java中使用它的示例:

    import java.util.regex.Pattern;
    
    public class Example {
          
          
        @Pattern(regexp = "[A-Za-z0-9]+")
        private String someField;
    
        // 构造函数、getter和setter方法在这里...
    }
    

    在这个示例中,@Pattern 注解应用于 someField 变量。regexp 参数指定了 someField 字符串必须遵循的正则表达式模式。在这种情况下,它要求字符串只包含字母和数字字符。

这些注解允许你在实体类的字段上添加验证规则,以确保输入数据满足特定条件或约束。当在Spring MVC控制器中使用@Valid注解时,这些注解可以触发验证过程,并在验证失败时生成相应的错误信息。使用这些注解可以简化数据验证的过程,提高应用程序的可读性和可维护性。

附:@ExceptionHandler

@ExceptionHandler 是一个注解,通常用于Spring框架中,用于处理控制器中的异常。当在控制器中发生异常时,@ExceptionHandler 注解允许您指定一个方法来处理该异常,而不是让异常传播到更高层级的异常处理器。

以下是如何在Spring框架中使用 @ExceptionHandler 注解的示例:

@Controller
public class MyController {
    
    

    @GetMapping("/example")
    public String example() {
    
    
        // 业务逻辑,可能会抛出异常
        if (someCondition) {
    
    
            throw new CustomException("Something went wrong");
        }
        // ...
        return "successPage";
    }

    @ExceptionHandler(CustomException.class)
    public ModelAndView handleCustomException(CustomException ex) {
    
    
        ModelAndView modelAndView = new ModelAndView("errorPage");
        modelAndView.addObject("errorMessage", ex.getMessage());
        return modelAndView;
    }
}

在上面的示例中,@ExceptionHandler 注解用于处理 CustomException 类型的异常。当在 example 方法中抛出 CustomException 异常时,Spring框架会调用 handleCustomException 方法来处理该异常。在 handleCustomException 方法中,您可以执行自定义的异常处理逻辑,例如,返回一个包含错误消息的视图。

重要注意事项:

  • @ExceptionHandler 方法的参数可以包括异常类型、HttpServletRequestHttpServletResponse 等,以便您可以访问请求和响应对象以及有关异常的信息。
  • 您可以在同一个控制器中定义多个 @ExceptionHandler 方法,每个方法处理不同类型的异常。
  • 如果没有找到与抛出的异常类型匹配的 @ExceptionHandler 方法,异常将传播到更高级别的异常处理器(例如,全局异常处理器)。
  • @ExceptionHandler 不仅可以用在控制器类上,还可以用在 @ControllerAdvice 类中,以全局方式处理异常。

猜你喜欢

转载自blog.csdn.net/weixin_44693429/article/details/133117527