写在前面

今天是大年初五了。。。

不知不觉,又要上班了,美好的假期只剩一天了,有点不舍呢!

也不知道为什么,总感觉像在做梦一样,像没睡醒一样,并不是因为眼睛小,更多应该是自寻烦恼,想得多罢了。

参数校验任务拆解

  • 对保存接口和查询接口增加参数校验
  • 校验不通过时,前端弹出错误提示

集成Validation做参数校验实现

1、引入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2、对查询接口增加参数校验

2.1、给对应实体字段添加校验,示例代码如下:


package com.rongrong.wiki.req; import lombok.Data; import javax.validation.constraints.Max;
import javax.validation.constraints.NotNull; @Data
public class PageReq {
private int page;
@NotNull(message = "【每页条数】不能为空")
@Max(value = 100, message = "【每页条数】不能超过100")
private int size;
}

2.2、再给对应接口添加校验,示例代码如下:

   /**
* 查询功能,支持按名字模糊查询
*
* @param eBookReq
* @return
*/
@GetMapping("/list")
public CommonResp list(@Valid EBookQueryReq eBookReq) {
CommonResp<PageResp<EBookResp>> resp = new CommonResp<>();
PageResp<EBookResp> list = eBookService.list(eBookReq);
resp.setMessage("执行查询成功!");
resp.setContent(list);
return resp;
}

2.3、测试接口是否有校验了

GET http://localhost:8888/ebook/list?page=1&size=1000

HTTP/1.1 400
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sat, 05 Feb 2022 01:26:51 GMT
Connection: close {
"timestamp": "2022-02-05T01:26:51.739+00:00",
"status": 400,
"error": "Bad Request",
"trace": "org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors\nField error in object 'EBookQueryReq' on field 'size': rejected value [1000]; codes [Max.EBookQueryReq.size,Max.size,Max.int,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [EBookQueryReq.size,size]; arguments []; default message [size],100]; default message [【每页条数】不能超过100]\r\n\tat org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:170)\r\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:170)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:807)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:626)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n",
"message": "Validation failed for object='EBookQueryReq'. Error count: 1",
"errors": [
{
"codes": [
"Max.EBookQueryReq.size",
"Max.size",
"Max.int",
"Max"
],
"arguments": [
{
"codes": [
"EBookQueryReq.size",
"size"
],
"arguments": null,
"defaultMessage": "size",
"code": "size"
},
100
],
"defaultMessage": "【每页条数】不能超过100",
"objectName": "EBookQueryReq",
"field": "size",
"rejectedValue": 1000,
"bindingFailure": false,
"code": "Max"
}
],
"path": "/ebook/list"
} Response code: 400; Time: 105ms; Content length: 5763 bytes

由上可知,已经触发了校验,但这样的提示,不是我们想要,需要对校验做进一步处理。

2.4、统一异常处理

加入后,将会对controller中的异常进行处理,示例代码如下:

package com.rongrong.wiki.controller;

import com.rongrong.wiki.exception.BusinessException;
import com.rongrong.wiki.resp.CommonResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; /**
* 统一异常处理、数据预处理等
*/
@ControllerAdvice
public class ControllerExceptionHandler { private static final Logger LOG = LoggerFactory.getLogger(ControllerExceptionHandler.class); /**
* 校验异常统一处理
* @param e
* @return
*/
@ExceptionHandler(value = BindException.class)
@ResponseBody
public CommonResp validExceptionHandler(BindException e) {
CommonResp commonResp = new CommonResp();
LOG.warn("参数校验失败:{}", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
commonResp.setSuccess(false);
commonResp.setMessage(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return commonResp;
} /**
* 校验异常统一处理
* @param e
* @return
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public CommonResp validExceptionHandler(BusinessException e) {
CommonResp commonResp = new CommonResp();
LOG.warn("业务异常:{}", e.getCode().getDesc());
commonResp.setSuccess(false);
commonResp.setMessage(e.getCode().getDesc());
return commonResp;
} /**
* 校验异常统一处理
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public CommonResp validExceptionHandler(Exception e) {
CommonResp commonResp = new CommonResp();
LOG.error("系统异常:", e);
commonResp.setSuccess(false);
commonResp.setMessage("系统出现异常,请联系管理员");
return commonResp;
}
}

再次调用查询接口,测试结果如下:

3、对保存接口增加参数校验

和上面的查询接口一样,也是需要将需要校验字段加上注解校验,再对接口加上对应注解,即完成校验操作,示例代码如下:


@NotNull(message = "【名称】不能为空")
private String name; /**
* 新增或编辑功能
*
* @param eBookSaveReq
* @return
*/
@PostMapping("/save")
public CommonResp save(@Valid @RequestBody EBookSaveReq eBookSaveReq) {
CommonResp resp = new CommonResp<>();
eBookService.save(eBookSaveReq);
return resp;
}

4、校验不通过时,前端弹出错误提示

在前段页面添加弹窗提示组件,示例代码如下:

import {message} from 'ant-design-vue';

      if (data.success) {
modalVisible.value = false;
modalLoading.value = false;
//重新加载列表
handleQuery({
page: 1,
size: pagination.value.pageSize,
});
}else {
message.error(data.message);
}
})

编译运行,测试结果

1、查询接口铅酸验证结果

2、保存接口前端验证结果

写在最后

就一个功能来说的话,肯定是要有对应的校验规则,才是严谨的,从某种角度来说,也体现了程序的健壮性。

当然,也可能有的同学会说,写校验规则多麻烦,基本功能能用不就行了吗。

其实不然,因为用户的行为,是最不可控的,不能保证每个使用者(用户),都能按照开发者预设的想法去使用软件。

如果那样话,那所有程序,都会没有bug了,你说是不是?

寻找写代码感觉(十六)之 集成Validation做参数校验的更多相关文章

  1. 寻找写代码感觉(二)之 Spring Boot 项目属性配置

    一.前言 写代码就和恋爱一样,有反馈就要趁热打铁,搞完了项目搭建,接下来就来搞搞项目配置. 二.IDEA设置 1.编码配置 这里所说的就是代码的编码格式,你可以不设置,但是可能要面临的是,很多未知的麻 ...

  2. 寻找写代码感觉(一)之使用 Spring Boot 快速搭建项目

    写在前面 现在已经是八月份了,我已经荒废了半年居多,不得不说谈恋爱确实是个麻烦的事,谈好了皆大欢喜,分手了就是萎靡不振,需要很长一段时间才能缓过来. 人还是要有梦想的,至于实现只不过是一个契机,但凡不 ...

  3. 寻找写代码感觉(三)之使用 Spring Boot 编写接口

    一.前言 项目配置完之后,接着就是写接口了,那咱们就开始吧. 二.项目配置补充知识点 上篇文章写的是关于项目属性配置的一些知识,这里针对上次遗忘内容进行补充如下: 2.1.获取配置文件的值 在appl ...

  4. 寻找写代码感觉(五)之Mybatis官方代码生成器的使用

    一.Mybatis Generator生成器 见名知意,官方给出的代码生成器.好处就是不用自己写实体类.接口.xml文件了,应对简单增删改查是可以的.复杂的还是需要自己手写sql的. 二.Mybati ...

  5. 寻找写代码感觉(八)之SpringBoot过滤器的使用

    一.什么是过滤器? 过滤器是对数据进行过滤,预处理过程,当我们访问网站时,有时候会发布一些敏感信息,发完以后有的会用*替代,还有就是登陆权限控制等,一个资源,没有经过授权,肯定是不能让用户随便访问的, ...

  6. Java开发笔记(二十六)方法的输出参数

    前面介绍了方法的输入参数,与输入参数相对应的则为输出参数,输出参数也被称作方法的返回值,意思是经过方法的处理最终得到的运算数值.这个返回值可能是整型数,也可能是双精度数,也可能是数组等其它类型,甚至允 ...

  7. ElasticSearch7.3学习(二十六)----搜索(Search)参数总结、结果跳跃(bouncing results)问题解析

    1.preference 首先引入一个bouncing results问题,两个document排序,field值相同:不同的shard上,可能排序不同:每次请求轮询打到不同的replica shar ...

  8. 第十六篇 nginx主配置文件参数解释

    # 指定拥有运行nginx权限的用户 #user nobody; # 指定开启的进程数,建议设置为CPU核心数 worker_processes ; # 指定全局错误日志级别,包括:debug/inf ...

  9. CTF---Web入门第十六题 天下武功唯快不破

    天下武功唯快不破分值:10 来源: 北邮天枢战队 难度:易 参与人数:10787人 Get Flag:2264人 答题人数:3373人 解题通过率:67% 看看响应头 格式:CTF{ } 解题链接: ...

随机推荐

  1. 洛谷 P2397:yyy loves Maths VI (mode)(摩尔投票算法)

    题目背景 自动上次redbag用加法好好的刁难过了yyy同学以后,yyy十分愤怒.他还击给了redbag一题,但是这题他惊讶的发现自己居然也不会,所以只好找你 题目描述 [h1]udp2:第一题因为语 ...

  2. 5G的到来

    通信改变未来,从古至今信息的传输和获取从来就没有缺少过,之所以谁能取得胜利就是谁掌握的资源多,其中信息资源尤为重要,只要获取到更多的信息你就能提前做出应对策略.因此未来一定是信息的未来,作为信息传输的 ...

  3. Adversarial Training with Rectified Rejection

    目录 概 主要内容 rejection 实际使用 代码 Pang T., Zhang H., He D., Dong Y., Su H., Chen W., Zhu J., Liu T. Advers ...

  4. Blazor组件的new使用方式与动态弹窗

    1. 前言 在Blazor中的无状态组件文中,我提到了无状态组件中,有人提到这个没有diff,在渲染复杂model时,性能可能会更差.确实,这一点确实是会存在的.以上文的方式来实现无状态组件,确实只要 ...

  5. 使用 JavaScript 用循环嵌套输出乘法表。外循环控制行数,内循环控制当前行要输出的乘法表达式,在页面上输出九九乘法表

    查看本章节 查看作业目录 需求说明: 在页面上输出九九乘法表,实现效果如图所示 实现思路: 创建HTML页面 在页面中嵌入 <script type="text/javascript& ...

  6. CVE-2021-45232 Apache APISIX 从未授权访问到RCE

    00x1漏洞环境 Apache APISIX Dashboard 2.7 - 2.10 版本受到影响 通过git拉取在docker搭建环境 git clone https://github.com/a ...

  7. 三角网格上的寻路算法Part.2—A*算法

    背景 继上一篇三角网格Dijkstra寻路算法之后,本篇将继续介绍一种更加智能,更具效率的寻路算法-A*算法,本文将首先介绍该算法的思想原理,再通过对比来说明二者之间的相同与不同之处,然后采用类似Di ...

  8. 【17MKH】我在框架中对.Net依赖注入的扩展

    说明 依赖注入(DI)是控制反转(IoC)的一种技术实现,它应该算是.Net中最核心,也是最基本的一个功能.但是官方只是实现了基本的功能和扩展方法,而我呢,在自己的框架 https://github. ...

  9. Swoole 中使用 Table 内存表实现进程间共享数据

    背景 在多进程模式下进程之间的内存是相互隔离的,在一个工作进程中的全局变量和超全局变量,在另一个工作进程中是无法读取和操作的. 如果只有一个工作进程,则不存在进程隔离问题,可以使用全局变量和超全局变量 ...

  10. 深入 Laravel 内核之装饰模式

    装饰模式核心内容: 装饰模式可以在不影响组件对象的情况下,以动态.透明的方式从外部给对象添加功能: 装饰器模式的本质就是动态组合.动态是手段,组合是目的.装饰模式是通过把复杂的功能简单化.分散化,在运 ...