lan最近一直在做前后端分离的项目,前端提交的参数很多,用Bean来接前端还要写每个Bean的前缀,索性就直接传参了。我们在controller用一个Map接参。
我看网上很少关于Map接参校验的,我就直接贴代码了。
1、首先是自定义一个异常类
package com.test.validator.exception;
/**
* @author MrWang
* @version v1.0
* @date 2019/02/20
* @Description
* 自定义异常
*/
public class TestException extends RuntimeException{
private String code;
private String msg;
private Object data;
public TestException(String code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
public Object getData() {
return data;
}
}
首先这个类必须继承RuntimeException,其次定义你想往前端返回的统一格式。生成构造函数和getset方法。
2、创建全局捕捉异常类
package com.test.validator.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
* @author MrWang
* @version v1.0
* @date 2019/02/20
* @Description
* 全局捕捉异常处理类
* @ControllerAdvice注解可以加参数basePackages:指定捕捉异常的包范围
*/
@ControllerAdvice
public class TestControllerAdvice {
/**
* 自定义异常处理
* @param ex
* @return
*/
@ResponseBody
@ExceptionHandler(value = TestException.class)
public Map myErrorHandler(TestException ex) {
Map map = new HashMap(3);
map.put("code", ex.getCode());
map.put("message", ex.getMsg());
map.put("data",ex.getData());
return map;
}
}
这个类的作用就是捕捉全局异常 然后向前端返回错误信息。
3、自定义校验工具类
package com.test.validator.common;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.test.validator.exception.TestException;
/**
* @author MrWang
* @version v1.0
* @date 2019/02/20
* @Description
* 自定义校验工具类
*/
public class FieldUtils {
/**
* 字符串非空校验
* @param filed 要校验的字段
* @param message message
* @param code code
* @param data data
*/
public static void stringIsNullCheck(String filed,String message,String code,Object data){
if (StrUtil.isBlank(filed)){
throw new TestException(code,message,data);
}
}
/**
* 数字校验
* @param filed 要校验的字段
* @param message message
* @param code code
* @param data data
*/
public static void isNumberCheck(String filed,String message,String code,Object data){
if (!NumberUtil.isNumber(filed)){
throw new TestException(code,message,data);
}
}
/**
* 整数校验
* @param filed 要校验的字段
* @param message message
* @param code code
* @param data data
*/
public static void isIntCheck(String filed,String message,String code,Object data){
if (!NumberUtil.isInteger(filed)){
throw new TestException(code,message,data);
}
}
/**
* 邮件校验
* @param filed 要校验的字段
* @param message message
* @param code code
* @param data data
*/
public static void isEmailCheck(String filed,String message,String code,Object data){
if (!Validator.isEmail(filed)){
throw new TestException(code,message,data);
}
}
/**
* 手机号校验
* @param filed 要校验的字段
* @param message message
* @param code code
* @param data data
*/
public static void isMobileCheck(String filed,String message,String code,Object data){
if (!Validator.isMobile(filed)){
throw new TestException(code,message,data);
}
}
}
因为是一个测试的小demo,所以就写了几个校验,可以模仿以上的写法往上加。
5、校验层代码
package com.test.validator.service.impl;
import com.test.validator.common.FieldUtils;
import com.test.validator.service.TestService;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author MrWang
* @version v1.0
* @date 2019/02/20
* @Description
*/
@Service
public class TestServiceImpl implements TestService {
private static final String CODE = "Y50000";
@Override
public Map post(Map<String,String> record) {
FieldUtils.stringIsNullCheck(record.get("username"),"用户名不能为空",CODE,"");
FieldUtils.stringIsNullCheck(record.get("password"),"密码不能为空",CODE,"");
FieldUtils.stringIsNullCheck(record.get("sex"),"性别不能为空",CODE,"");
FieldUtils.isNumberCheck(record.get("age"),"输入的年龄不合法",CODE,"");
FieldUtils.isIntCheck(record.get("age2"),"请输入整数",CODE,"");
FieldUtils.isEmailCheck(record.get("email"),"请输入正确的邮件",CODE,"");
FieldUtils.isMobileCheck(record.get("mobile"),"请输入正确的手机号",CODE,"");
//dao逻辑操作
return record;
}
}
我们一般都是在service层进行字段校验,可见我们从Map中取出参数后挨个校验参数,如有异常,直接抛掉。是不是比以前的校验清晰多了。
6、实践
我们使用postman对这些参数进行测试校验
工具类使用的hutool特别好用,给大家安利一下。这是官网
源码地址
GitHub:https://github.com/chuanzige/springboot_validator
码云:https://gitee.com/QingJiaoWoChuanZiGe/springboot_validator
2021/01/05更新
结合springboot自定义非空注解 注解类
/**
* @author whc
* @version v1.0
* @date 2020/9/15
* @Description 字段判空注解
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldsRequire {
/**
* 默认 ""
*/
String message() default "";
}
拦截器类 ServiceException可以参考上边TestException
/**
* @author whc
* @version v1.0
* @date 2020/9/15
* @Description 拦截@FieldsRequire注解的必填字段校验拦截器
*/
public class FieldsRequireInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
List<Map<String,String>> paramsList = getParamsName((HandlerMethod) handler);
for (Map<String,String> result:paramsList){
String parameter = request.getParameter(result.get("parameter"));
if (StrKit.isBlank(parameter)){
//如果注解没有写message 则以字段名为提示
throw new ServiceException(ResponseUtil.ERROR_CODE,StrKit.notBlank(result.get("message"))?result.get("message"):result.get("parameter")+"不能为空",null);
}
}
return true;
}
/**
* 获取使用了该注解的参数名称
*/
private List<Map<String,String>> getParamsName(HandlerMethod handlerMethod) {
Parameter[] parameters = handlerMethod.getMethod().getParameters();
List<Map<String,String>> list = new ArrayList<>();
for (Parameter parameter : parameters) {
//判断这个参数时候被加入了FieldsRequire的注解
if(parameter.isAnnotationPresent(FieldsRequire.class)){
FieldsRequire annotation = parameter.getAnnotation(FieldsRequire.class);
annotation.message();
Map<String,String> result = new HashMap<>(8);
result.put("parameter",parameter.getName());
result.put("message",annotation.message());
list.add(result);
}
}
return list;
}
}
controller层调用即可
/**
*
* @param code
* @return Map<String, Object>
* @author whc
*/
@RequestMapping("/login")
public Map<String, Object> wxLogin(@FieldsRequire(message = "code不能为空") String code) {
return appletService.wxLogin(code);
}
记得把拦截器注入到配置中哦