SpringBoot2.X Kotlin系列之数据校验和异常处理详解

在开发项目时,我们经常需要在前后端都校验用户提交的数据,判断提交的数据是否符合我们的标准,包括字符串长度,是否为数字,或者是否为手机号码等;这样做的目的主要是为了减少SQL注入攻击的风险以及脏数据的插入。提到数据校验我们通常还会提到异常处理,因为为了安全起见,后端出现的异常我们通常不希望直接抛到客户端,而是经过我们的处理之后再返回给客户端,这样做主要是提升系统安全性,另外就是给予用户友好的提示。

定义实体并加上校验注解

class StudentForm() {

 @NotBank(message = '生日不能为空')
 var birthday: String = ""

 @NotBlank(message = "Id不能为空")
 var id:String = ""

 @NotBlank(message = "年龄不能为空")
 var age:String = ""

 @NotEmpty(message = "兴趣爱好不能为空")
 var Interests:List<String> = Collections.emptyList()

 @NotBlank(message = "学校不能为空")
 var school: String = ""
 override fun toString(): String {
  return ObjectMapper().writeValueAsString(this)
 }
}

这里首先使用的是基础校验注解,位于javax.validation.constraints下,常见注解有@NotNull、@NotEmpty、@Max、@Email、@NotBank、@Size、@Pattern,当然出了这些还有很多注解,这里就不在一一讲解,想了解更多的可以咨询查看jar包。

这里简单讲解一下注解的常见用法:

  • @NotNull: 校验一个对象是否为Null
  • @NotBank: 校验字符串是否为空串
  • @NotEmpty: 校验List、Map、Set是否为空
  • @Email: 校验是否为邮箱格式
  • @Max @Min: 校验Number或String是否在指定范围内
  • @Size: 通常需要配合@Max @Min一期使用
  • @Pattern: 配合自定义正则表达式校验

定义返回状态枚举

enum class ResultEnums(var code:Int, var msg:String) {
 SUCCESS(200, "成功"),
 SYSTEM_ERROR(500, "系统繁忙,请稍后再试"),

}

自定义异常

这里主要是参数校验,所以定义一个运行时异常,代码如下:

class ParamException(message: String?) : RuntimeException(message) {
 var code:Int = ResultEnums.SUCCESS.code

 constructor(code:Int, message: String?):this(message) {
  this.code = code
 }
}

统一返回结构体定义

class ResultVo<T> {

 var status:Int = ResultEnums.SUCCESS.code

 var msg:String = ""

 var data:T? = null

 constructor()

 constructor(status:Int, msg:String, data:T) {
  this.status = status
  this.data = data
  this.msg = msg
 }

 override fun toString(): String {
  return ObjectMapper().writeValueAsString(this)
 }
}

全局异常处理

这里的全局异常处理,是指请求到达Controller层之后发生异常处理。代码如下:

@RestControllerAdvice
class RestExceptionHandler {

 private val logger:Logger = LoggerFactory.getLogger(this.javaClass)

 @ExceptionHandler(Exception::class)
 @ResponseBody
 fun handler(exception: Exception): ResultVo<String> {
  logger.error("全局异常:{}", exception)
  return ResultVo(500, "系统异常", "")
 }

 @ExceptionHandler(ParamException::class)
 @ResponseBody
 fun handler(exception: ParamException): ResultVo<String> {
  logger.error("参数异常:{}", exception.localizedMessage)
  return ResultVo(exception.code, exception.localizedMessage, "")
 }

}

这里得和Java处理的方式大同小异,无疑就是更加简洁了而已。

编写校验工具

object ValidatorUtils {

 private val validator = Validation.buildDefaultValidatorFactory().validator

 /**
  * 校验对象属性
  * @param obj 被校验对象
  * @param <T> 泛型
  * @return Map
 </T> */
 fun validate(obj: Any): Map<String, String> {
  var errorMap: Map<String, String>? = null
  val set = validator.validate(obj, Default::class.java)
  if (CollectionUtils.isEmpty(set)) {
   return emptyMap()
  }
  errorMap = set.map { it.propertyPath.toString() to it.message }.toMap()
  return errorMap
 }

 /**
  * 校验对象属性
  * @param obj 被校验对象
  * @param <T> 泛型
  * @return List
 </T> */
 fun validata(obj: Any): List<String> {
  val set = validator.validate(obj, Default::class.java)
  return if (CollectionUtils.isEmpty(set)) {
   emptyList()
  } else set.stream()
    .filter {Objects.nonNull(it)}
    .map { it.message }
    .toList()
 }
}

抽象校验方法

因为校验是通用的,几乎大部分接口都需要检验传入参数,所以我们把校验方法抽出来放在通用Controller层里,通用层这里不建议使用Class或者是抽象类,而是使用interface,定义如下:

@Throws(ParamException::class)
fun validate(t:Any) {
 val errorMap = ValidatorUtils.validate(t).toMutableMap()
 if (errorMap.isNotEmpty()) {
  throw ParamException(ResultEnums.SYSTEM_ERROR.code, errorMap.toString())
 }
}

这里如果有参数错误就直接抛出参数异常,然后交给全局异常处理器来捕获。

Controller层编写

@PostMapping("/student")
fun create(@RequestBody studentForm: StudentForm): ResultVo<StudentDTO> {
 this.validate(studentForm)
 val studentDTO = StudentDTO()
 BeanUtils.copyProperties(studentForm, studentDTO)
 return ResultVo(200, "", studentDTO)
}

1.传入一个空对象: 返回结果:

{
 "status": 500,
 "msg": "{school=学校不能为空, id=Id不能为空, age=年龄不能为空, Interests=兴趣爱好不能为空}",
 "data": ""
}

自定义校验规则

本篇文章开始之前我们提到过@Pattern,这个注解主要是方便我们定义自己的校验规则,假如我这里需要校验前端传入的生日,是否符合我所需要的格式,如下所示:

@NotBlank(message = "生日不能为空")
@Pattern(regexp="^(19|20)\\d{2}-(1[0-2]|0?[1-9])-(0?[1-9]|[1-2][0-9]|3[0-1])$", message="不是生日格式")
var birthday: String = ""

这里的校验逻辑可能不完善,大家使用的时候需要注意。

修改完成后我再次请求

请求示例

空值

入参:

{
 "age": "10",
 "id": "1",
 "school": "学校",
 "interests": ["户外运动"],
 "birthday": ""
}

出参:

{
 "status": 500,
 "msg": "{birthday=生日不能为空}",
 "data": ""
}

错误参数

入参:

{
 "age": "10",
 "id": "1",
 "school": "学校",
 "interests": ["户外运动"],
 "birthday": "1989-20-20"
}

出参:

{
 "status": 500,
 "msg": "{birthday=不是生日格式}",
 "data": ""
}

正确示例

入参:

{
 "age": "10",
 "id": "1",
 "school": "学校",
 "interests": ["户外运动"],
 "birthday": "1999-01-01"
}

出参:

{
 "status": 200,
 "msg": "",
 "data": {
  "id": "1",
  "birthday": "1999-01-01",
  "age": "10",
  "school": "学校"
 }
}

本章内容就到此结束了,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Kotlin + Spring Boot 请求参数验证的代码实例

    编写 Web 应用程序的时候,经常要做的事就是要对前端传回的数据进行简单的验证,比如是否非空.字符长度是否满足要求,邮箱格式是否正确等等.在 Spring Boot 中,可以使用 Bean Validation (JSR-303) 技术通过注解的方式来进行参数验证. 准备 DTO 对象 data class UserRegisterModel( @get: NotEmpty(message = "User name is required") @get: Size(message =

  • Spring Boot和Kotlin的无缝整合与完美交融

    前言 本文讲解 Spring Boot2 基础下,如何使用 Kotlin,并无缝整合与完美交融.为了让读者更加熟悉 Kotlin 的语法糖,笔者会在未来的几篇文章中,聊聊 Kotlin 的新特性及其语法糖.下面话不多说了,来一起看看详细的介绍吧 环境依赖 修改 POM 文件,添加 spring boot 依赖. <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-bo

  • 利用Kotlin + Spring Boot实现后端开发

    前言 Spring官方最近宣布,将在Spring Framework 5.0版本中正式支持Kotlin语言.这意味着Spring Boot 2.x版本将为Kotlin提供一流的支持. 这并不会令人意外,因为Pivotal团队以广泛接纳​​JVM语言(如Scala和Groovy)而闻名. Kotlin 是一个基于 JVM 的编程语言,它的简洁.便利早已不言而喻.Kotlin 能够胜任 Java 做的所有事.目前,我们公司 C 端 的 Android 产品全部采用 Kotlin 编写.公司的后端项目

  • SpringBoot整合Kotlin构建Web服务的方法示例

    今天我们尝试Spring Boot整合Kotlin,并决定建立一个非常简单的Spring Boot微服务,使用Kotlin作为编程语言进行编码构建. 创建一个简单的Spring Boot应用程序.我会在这里使用maven构建项目: <?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  • 关于Spring Boot和Kotlin的联合开发

    一.概述 spring官方最近宣布,将在Spring Framework 5.0版本中正式支持Kotlin语言.这意味着Spring Boot 2.x版本将为Kotlin提供一流的支持. 这并不会令人意外,因为Pivotal团队以广泛接纳​​JVM语言(如Scala和Groovy)而闻名.下面我们用Spring Boot 2.x和Kotlin应用程序. 二.搭建环境 1.环境 IntelliJ和Eclipse都对Kotlin提供了支持,可以根据自己的喜好搭建Kotlin开发环境. 2.构建应用

  • spring boot + jpa + kotlin入门实例详解

    spring boot +jpa的文章网络上已经有不少,这里主要补充一下用kotlin来做. kotlin里面的data class来创建entity可以帮助我们减少不少的代码,比如现在这个User的Entity,这是Java版本的: @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String firstName; private S

  • SpringBoot2.X Kotlin系列之数据校验和异常处理详解

    在开发项目时,我们经常需要在前后端都校验用户提交的数据,判断提交的数据是否符合我们的标准,包括字符串长度,是否为数字,或者是否为手机号码等:这样做的目的主要是为了减少SQL注入攻击的风险以及脏数据的插入.提到数据校验我们通常还会提到异常处理,因为为了安全起见,后端出现的异常我们通常不希望直接抛到客户端,而是经过我们的处理之后再返回给客户端,这样做主要是提升系统安全性,另外就是给予用户友好的提示. 定义实体并加上校验注解 class StudentForm() { @NotBank(message

  • kotlin 协程上下文异常处理详解

    目录 引言 一.协程上下文 1.CoroutineContext 2.CorountineScope 3.子协程继承父协程 二.协程的异常传递 1.协程的异常传播 2.不同上下文(没有继承关系)之间协程异常会怎么样? 3.向用户暴露异常 三.协程的异常处理 使用SupervisorJob 异常捕获器CoroutineExceptionHandler Android中全局异常的处理 引言 从前面我们可以大致了解了协程的玩法,如果一个协程中使用子协程,那么该协程会等待子协程执行结束后才真正退出,而达

  • Kotlin + Retrofit + RxJava简单封装使用详解

    本文介绍了Kotlin + Retrofit + RxJava简单封装使用详解,分享给大家,具体如下: 实例化Retrofit object RetrofitUtil { val CONNECT_TIME_OUT = 30//连接超时时长x秒 val READ_TIME_OUT = 30//读数据超时时长x秒 val WRITE_TIME_OUT = 30//写数据接超时时长x秒 val retrofit: Retrofit by lazy { Log.d("RetrofitUtil"

  • Kotlin Flow操作符及基本使用详解

    目录 一.Flow的基本概念 二.Flow的生命周期与异常处理 2.1 开始与结束 2.2 异常的处理 2.3 retry的处理 2.4 超时的处理 2.5 Flow的取消 三.Flow的创建方式 四.Flow的接收方式 五.Flow的转换操作符 5.1 基本操作符 5.2 特殊操作符 5.3 组合与展平操作符 5.4 切换线程 总结 一.Flow的基本概念 Kotlin 的 Flow 相信大家都或多或少使用过,毕竟目前比较火,目前我把Flow的使用整理了一下.希望和大家所学对照一下,能有所启发

  • 基于java Servlet编码/异常处理(详解)

    1. Servlet输出中文 (1)为什么会有乱码? out.println方法在输出时或者表单提交的时候,浏览器会对表单中的中文参数值进行编码; 注:会使用表单所在的页面打开时使用的编码方式进行编码服务器端默认会使用ISO-8859-1进行解码所以会产生乱码 (2)如何解决? 1)post请求:requset.setcharcterencoding(string charset);添加到所有获取参数前并且该方法只支持post方法 2)get请求:修改服务器设置 2. 读取请求参数值 (1)St

  • Jquery揭秘系列:ajax原生js实现详解(推荐)

    讲到ajax这个东西,我们要知道两个对象XMLHTTPRequest和ActiveXObject ,提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力.可以同步或异步返回 Web 服务器的响应,并且能以文本或者一个 DOM 文档形式返回内容.XMLHTTPRequest基本上算是标准化了,兼容大部分浏览器ActiveXObject这玩儿意儿是微软的东西,所以是为了兼容IE版本,我们用的只是它的xmlHTTP功能. 为了功能的明确和清晰,我们

  • Java运行时数据区概述详解

    Java 虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途,如图所示: 程序计数器 程序计数器是一块比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器. 在虚拟机的概念模型中(仅是概念模型,各种虚拟机可能会通过一些更加高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 如果线程正在执行一个Java方法,则这个计数

  • Django REST 异常处理详解

    异常 异常处理-允许错误处理在程序结构的中心或者高层级的地方被清晰有条理的组织起来. Exceptions- allow error handling to be organized cleanly in a central or high-level place within the program structure. - Doug Hellmann, Python Exception Handling Techniques Rest框架视图中的异常处理 Exception handling

  • SpringBoot2 整合FreeMarker实现页面静态化示例详解

    一.页面静态化 1.动静态页面 静态页面 即静态网页,指已经装载好内容HTML页面,无需经过请求服务器数据和编译过程,直接加载到客户浏览器上显示出来.通俗的说就是生成独立的HTML页面,且不与服务器进行数据交互. 优缺点描述: 静态网页的内容稳定,页面加载速度极快: 不与服务器交互,提升安全性: 静态网页的交互性差,数据实时性很低: 维度成本高,生成很多HTML页面: 动态页面 指跟静态网页相对的一种网页编程技术,页面的内容需要请求服务器获取,在不考虑缓存的情况下,服务接口的数据变化,页面加载的

  • springboot2.3 整合mybatis-plus 高级功能及用法详解

    -学习并使用mybatis-plus的一些高级功能的用法例如: AR模式. 乐观锁 .逻辑删除 .自动填充.数据保护等功能 为了方便演示,咱们还是新建一个全新的项目 引入mp依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> &l

随机推荐