在项目里面,我们需要对前端传入的参数做一个简单的简单的校验,避免出现脏数据和业务逻辑错误。如果每个接口单独写校验逻辑的话,我们需要在controller层做逻辑判断。参数较少时,还勉强能够接受,如果参数和接口较多,无形中加重了工作量,也多了很多重复代码。所以引入注解式参数校验很有必要。
本文是基于springboot来实现参数校验,引入方式很简单,在pom中引入spring-boot-starter-validation即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
一般情况下,我们使用参数校验都需要返回异常信息,搭配全局异常捕获食用效果最佳。这里我就不介绍异常捕获的工作原理了,简单贴一下代码以供参考。先不需要考虑这的捕获器1、2、3、4是干什么的,下面会介绍作用。
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
//捕获器1
@ExceptionHandler(value = {MissingServletRequestParameterException.class})
public ResponseVO<String> handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
if (log.isErrorEnabled()) {
log.error(ex.getMessage(), ex);
}
return ResponseVO.error(ResponseConstant.ERROR_CODE, String.format("缺少必要参数[%s]", ex.getParameterName()), "");
}
//捕获器2
@ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
public ResponseVO<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
if (log.isErrorEnabled()) {
log.error(ex.getMessage(), ex);
}
BindingResult result = ex.getBindingResult();
FieldError error = result.getFieldError();
return ResponseVO.error(ResponseConstant.ERROR_CODE, null == error ? ResponseConstant.ERROR_MESSAGE : error.getDefaultMessage(), "");
}
//捕获器3
@ExceptionHandler(value = {BindException.class})
public ResponseVO<String> handleBindException(BindException ex) {
if (log.isErrorEnabled()) {
log.error(ex.getMessage(), ex);
}
BindingResult result = ex.getBindingResult();
FieldError error = result.getFieldError();
return ResponseVO.error(ResponseConstant.ERROR_CODE, null == error ? ResponseConstant.ERROR_MESSAGE : error.getDefaultMessage(), "");
}
//捕获器4
@ExceptionHandler(value = {ConstraintViolationException.class})
public ResponseVO<String> handleConstraintViolationException(ConstraintViolationException ex) {
if (log.isErrorEnabled()) {
log.error(ex.getMessage(), ex);
}
Optional<ConstraintViolation<?>> first = ex.getConstraintViolations().stream().findFirst();
return ResponseVO.error(ResponseConstant.ERROR_CODE, first.isPresent() ? first.get().getMessage() : ResponseConstant.ERROR_MESSAGE, "");
}
//其他所有异常捕获器
@ExceptionHandler(Exception.class)
public ResponseVO<String> otherErrorDispose(Exception e) {
// 打印错误日志
log.error("错误代码({}),错误信息({})", ResponseConstant.ERROR_CODE, e.getMessage());
e.printStackTrace();
return ResponseVO.error(ResponseConstant.ERROR_CODE, ResponseConstant.ERROR_MESSAGE, e.getMessage());
}
}
自定义异常返回和自定义常量
//自定义接口响应类
@Data
public class ResponseVO<T> implements Serializable {
// 状态码: 0-成功,其他-失败
private final Integer code;
// 返回信息
private final String message;
//返回值
private final T data;
//是否成功
private final Boolean success;
// 成功返回
public static <T> ResponseVO<T> success(T data) {
return new ResponseVO<>(data);
}
// 失败返回
public static <T> ResponseVO<T> error(Integer code, String message, T data) {
return new ResponseVO<>(code, message, data);
}
public ResponseVO(T data) {
this.code = ResponseConstant.SUCCESS_CODE;
this.message = ResponseConstant.OK;
this.data = data;
this.success = true;
}
public ResponseVO(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
this.success = code == ResponseConstant.SUCCESS_CODE;
}
}
//自定义常量类
public class ResponseConstant {
public static final String OK = "OK";
public static final String ERROR = "error";
public static final int SUCCESS_CODE = 200;
public static final int ERROR_CODE = 500;
public static final String ERROR_MESSAGE = "操作失败!!";
}
这里我就不介绍使用方式了,网上有很多详细的案例,包括每个注解的作用介绍,分组校验和自定义校验的使用方法(话说我自己都没有用过,只用过简单的注解)。
注解不生效的情况有很多,主要参考的解决思路:jar包冲突、加错注解、少了关键注解。
(1)jar包冲突可能是引入的时候引入了多个版本的jar包,注意检查pom,在springboot中只需要安装上述方式引入即可,无需再引入其他validator相关jar包。
(2)加错注解,主要是看你引入的注解是不是在下述这个路径下的。

(3)少了关键注解
//@Validated
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("test1")
public ResponseVO<Integer> test2(@NotNull(message = "最小值不能为空") Integer minNum,
@NotNull(message = "最大值不能为空") @Min(value = 10,message = "参数必须大于10") Integer maxNum) {
return ResponseVO.success(11);
}
}
如上明明写了@NotNull,也确认了引入的注解是对的,但是就是不返回错误信息。像这种参数没有放在一个对象中,而是直接写在接口上的情况,需要在类上加@Validated注解,否则不会生效。
@Validated
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("test3")
public ResponseVO<Integer> test3(BlacklistPageParamVO vo) {
return ResponseVO.success(11);
}
@PostMapping("test4")
public ResponseVO<Integer> test4(@Valid @RequestBody BlacklistPageParamVO vo) {
return ResponseVO.success(11);
}
}
@Data
public class BlacklistPageParamVO{
@NotBlank(message = "日期不能为空")
private String date;
@NotBlank(message = "日期2不能为空")
private String date2;
@Valid
@NotNull(message = "内部对象不能为空")
private InnerVO innerVO;
}
@Data
public class InnerVO {
@NotNull(message = "num1不能为空")
private Integer num1;
@NotNull(message = "num2不能为空")
private Integer num2;
}
第二种情况就是,我的校验字段在一个对象里面,这个时候需要在对象前面加上@Valid注解,所以test3的检验不会生效。这里扩展一下如果一个对象中还有另一个对象,且内部的对象也有需要检验的字段,需要给这个内部对象也加上@Valid注解才会生效。
@Validated
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("test1")
public ResponseVO<Integer> test1(@NotNull(message = "最小值不能为空") @RequestParam Integer minNum,
@NotNull(message = "最大值不能为空") @RequestParam Integer maxNum) {
return ResponseVO.success(11);
}
}
像上面这种情况如果在参数前加了@RequestParam表示参数必传,可以理解为作用和@NotNull是一样的。此时调用接口时,忘记传参数minNum了,我期待返回的是“最小值不能为空”,但是实际上返回的是“Required Integer parameter ‘minNum’ is not present”,且控制台打印的错误日志如下:

从错误名称,我们就可以看出,这是由于加了@RequestParam注解导致,让你传,你不传,所以报了这个错。这个错误对应的就是我们上文中的捕获器1。而且由此可以看出MissingServletRequestParameterException异常是比validation的异常优先级高的。
MissingServletRequestParameterException
加了@RequestParam注解,但是接口调用时没有传指定的参数(注意:是没有传,而不是传了,但是值是null)。
MethodArgumentNotValidException
经过测试,当校验的参数放在对象中,接口的请求方式是post请求,用@Valid @RequestBody方式接受参数时,如果报错,会被该捕获器捕获。
BindException
经过测试,当校验参数写在类中,接口请求方式是get请求时,报错会被该捕获器捕获。
ConstraintViolationException
传了值,但是不符合要求。@NotNull(message = “最大值不能为空”) @Min(value = 10,message = “参数必须大于10”),要求传非null值,且值必须大于10,否则会返回错误信息。经过测试,当校验参数直接写在接口上,而不是写在类中,报错会被该捕获器捕获。
4个捕获器对应4种不同的场景。
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option
我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano
我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use
对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一
我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些
我已经在mountainlion上成功安装了rbenv和rubybuild。运行rbenvinstall1.9.3-p392结束于:校验和不匹配:ruby-1.9.3-p392.tar.gz(文件已损坏)预期f689a7b61379f83cbbed3c7077d83859,得到1cfc2ff433dbe80f8ff1a9dba2fd5636它正在下载的文件看起来没问题,如果我使用curl手动下载文件,我会得到同样不正确的校验和。有没有人遇到过这个?他们是如何解决的? 最佳答案 tl:博士;使用浏览器从http://ftp.rub