精华内容
下载资源
问答
  • 普通web请求可以通过 javax.validation.constraints package下的注解进行参数校验,那么校验后返回的校验失败的结果怎么友好型提醒呢?通过下面异常拦截处理: package com.oyo.supplier.web; import ...

    普通web请求可以通过 javax.validation.constraints package下的注解进行参数校验,那么校验后返回的校验失败的结果怎么友好型提醒呢?通过下面异常拦截处理:

    package com.oyo.supplier.web;
    
    import com.alibaba.fastjson.JSON;
    import com.google.common.collect.Lists;
    import com.oyo.common.enums.ResponseCode;
    import com.oyo.common.exception.ServiceException;
    import com.oyo.common.response.BaseResponse;
    import com.oyo.supplier.common.enums.CommonEnum;
    import com.oyo.supplier.web.response.ArgumentInvalidResponse;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.validation.FieldError;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpServletRequest;
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * @author kermit.liu on 2019/2/22
     */
    @ControllerAdvice
    @ResponseBody
    @Slf4j
    public class DefaultControllerAdvice {
    
        /**
         * 添加全局异常处理流程,根据需要设置需要处理的异常
         *
         * @param request   request
         * @param exception exception
         * @return BaseResponse
         */
        @ExceptionHandler(value = MethodArgumentNotValidException.class)
        public BaseResponse methodArgumentNotValidHandler(HttpServletRequest request, MethodArgumentNotValidException exception) {
            log.warn(request.getRequestURI() + " exception, params = " + JSON.toJSONString(request.getParameterMap()), exception);
            // 按需重新封装需要返回的错误信息
            List<ArgumentInvalidResponse> invalidArguments = Lists.newArrayList();
            // 解析原错误信息,封装后返回,此处返回非法的字段名称,原始值,错误信息
            StringBuilder msg = new StringBuilder();
            msg.append(ResponseCode.ILLEGAL_ARGUMENT.getMsg()).append(": ");
            for (FieldError error : exception.getBindingResult().getFieldErrors()) {
                ArgumentInvalidResponse argumentInvalidResponse = ArgumentInvalidResponse.builder()
                        .field(error.getField())
                        .defaultMessage(error.getDefaultMessage())
                        .rejectedValue(error.getRejectedValue())
                        .build();
                invalidArguments.add(argumentInvalidResponse);
                msg = msg.append(error.getDefaultMessage()).append(";");
            }
            BaseResponse<Object> fail = BaseResponse.fail(ResponseCode.ILLEGAL_ARGUMENT.getCode(), msg.toString());
            fail.setData(JSON.toJSONString(invalidArguments));
            return fail;
        }
    
        @ExceptionHandler(value = SQLException.class)
        public BaseResponse methodSQLHandler(HttpServletRequest request, SQLException exception) {
            log.warn(request.getRequestURI() + " exception, params = " + JSON.toJSONString(request.getParameterMap()), exception);
            BaseResponse<Object> fail = BaseResponse.fail(CommonEnum.RESPONSE.SQL_ERROR.getCode(), CommonEnum.RESPONSE.SQL_ERROR.getDesc());
            fail.setData(JSON.toJSONString(exception.getErrorCode()));
            return fail;
        }
    
        @ExceptionHandler({Exception.class})
        public BaseResponse handException(HttpServletRequest request, Exception e) {
            if (e instanceof ServiceException) {
                log.warn(request.getRequestURI() + " exception, params = " + JSON.toJSONString(request.getParameterMap()), e);
                return BaseResponse.fail(((ServiceException) e).getCode(), ((ServiceException) e).getMsg());
            } else {
                log.error(request.getRequestURI() + " exception, params = " + JSON.toJSONString(request.getParameterMap()), e);
                BaseResponse<Object> fail = BaseResponse.fail(CommonEnum.RESPONSE.SERVER_ERROR.getCode(), CommonEnum.RESPONSE.SERVER_ERROR.getDesc());
                fail.setData(e.getMessage());
                return fail;
            }
        }
    }
    

     

    展开全文
  • 对于后端开发而言,前端request请求中的参数校验是一个必可少的环节。无论传来的参数是id还是email还是其他的参数,我们都要对参数的类型、大小、格式等等做这样或者那样的校验,然后才进行逻辑处理,以确保逻辑...
  • 在实际开发中经常需要对前端传递的多个参数进行为空校验,可以使用python提供的all()函数 if not all([arg1, arg2, arg3]): # 当 arg1, arg2, arg3都为空时all函数返回true return jsonify(errno=RET.PARAMERR...
  • 主要介绍了Spring Boot 参数校验的具体实现方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 此时我们观察控制台可以发现接口已经引发MethodArgumentNotValidException异常了: 其实这样就已经达到我们想要的效果了,参数校验不通过自然就不执行接下来的业务逻辑,去掉BindingResult后会自动引发异常,异常...

    前言

    一个后端接口大致分为四个部分组成:接口地址(url)、接口请求方式(get、post等)、请求数据(request)、响应数据(response)。如何构建这几个部分每个公司要求都不同,没有什么“一定是最好的”标准,但一个优秀的后端接口和一个糟糕的后端接口对比起来差异还是蛮大的,其中最重要的关键点就是看是否规范!

    本文就一步一步演示如何构建起一个优秀的后端接口体系,体系构建好了自然就有了规范,同时再构建新的后端接口也会十分轻松。

    在文章末尾贴上了项目演示的github地址,clone下来即可运行,并且我将每一次的优化记录都分别做了代码提交,你可以清晰的看到项目的改进过程!

    所需依赖包

    这里用的是SpringBoot配置项目,本文讲解的重点是后端接口,所以只需要导入一个spring-boot-starter-web包就可以了:

    <!--web依赖包,web应用必备-->
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    本文还用了swagger来生成API文档,lombok来简化类,不过这两者不是必须的,可用可不用。

    参数校验

    一个接口一般对参数(请求数据)都会进行安全校验,参数校验的重要性自然不必多说,那么如何对参数进行校验就有讲究了。

    业务层校验

    首先我们来看一下最常见的做法,就是在业务层进行参数校验:

    public String addUser(User user) {
         if (user == null || user.getId() == null || user.getAccount() == null || user.getPassword() == null || user.getEmail() == null) {
             return "对象或者对象字段不能为空";
         }
         if (StringUtils.isEmpty(user.getAccount()) || StringUtils.isEmpty(user.getPassword()) || StringUtils.isEmpty(user.getEmail())) {
             return "不能输入空字符串";
         }
         if (user.getAccount().length() < 6 || user.getAccount().length() > 11) {
             return "账号长度必须是6-11个字符";
         }
         if (user.getPassword().length() < 6 || user.getPassword().length() > 16) {
             return "密码长度必须是6-16个字符";
         }
         if (!Pattern.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", user.getEmail())) {
             return "邮箱格式不正确";
         }
         // 参数校验完毕后这里就写上业务逻辑
         return "success";
     }
    

    这样做当然是没有什么错的,而且格式排版整齐也一目了然,不过这样太繁琐了,这还没有进行业务操作呢光是一个参数校验就已经这么多行代码,实在不够优雅。

    我们来改进一下,使用Spring Validator和Hibernate Validator这两套Validator来进行方便的参数校验!这两套Validator依赖包已经包含在前面所说的web依赖包里了,所以可以直接使用。

    Validator + BindResult进行校验

    Validator可以非常方便的制定校验规则,并自动帮你完成校验。首先在入参里需要校验的字段加上注解,每个注解对应不同的校验规则,并可制定校验失败后的信息:

    @Data
    public class User {
        @NotNull(message = "用户id不能为空")
        private Long id;
    
        @NotNull(message = "用户账号不能为空")
        @Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")
        private String account;
    
        @NotNull(message = "用户密码不能为空")
        @Size(min = 6, max = 11, message = "密码长度必须是6-16个字符")
        private String password;
    
        @NotNull(message = "用户邮箱不能为空")
        @Email(message = "邮箱格式不正确")
        private String email;
    }
    

    校验规则和错误提示信息配置完毕后,接下来只需要在接口需要校验的参数上加上@Valid注解,并添加BindResult参数即可方便完成验证:

    @RestController
    @RequestMapping("user")
    public class UserController {
        @Autowired
        private UserService userService;
    
        @PostMapping("/addUser")
        public String addUser(@RequestBody @Valid User user, BindingResult bindingResult) {
            // 如果有参数校验失败,会将错误信息封装成对象组装在BindingResult里
            for (ObjectError error : bindingResult.getAllErrors()) {
                return error.getDefaultMessage();
            }
            return userService.addUser(user);
        }
    }
    

    这样当请求数据传递到接口的时候Validator就自动完成校验了,校验的结果就会封装到BindingResult中去,如果有错误信息我们就直接返回给前端,业务逻辑代码也根本没有执行下去。

    此时,业务层里的校验代码就已经不需要了:

    public String addUser(User user) {
         // 直接编写业务逻辑
         return "success";
     }
    

    现在可以看一下参数校验效果。我们故意给这个接口传递一个不符合校验规则的参数,先传递一个错误数据给接口,故意将password这个字段不满足校验条件:

    {
        "account": "12345678",
        "email": "123@qq.com",
        "id": 0,
        "password": "123"
    }
    

    再来看一下接口的响应数据:

    这样是不是方便很多?不难看出使用Validator校验有如下几个好处:

    • 简化代码,之前业务层那么一大段校验代码都被省略掉了。

    • 使用方便,那么多校验规则可以轻而易举的实现,比如邮箱格式验证,之前自己手写正则表达式要写那么一长串,还容易出错,用Validator直接一个注解搞定。(还有更多校验规则注解,可以自行去了解哦)

    • 减少耦合度,使用Validator能够让业务层只关注业务逻辑,从基本的参数校验逻辑中脱离出来。

    使用Validator+ BindingResult已经是非常方便实用的参数校验方式了,在实际开发中也有很多项目就是这么做的,不过这样还是不太方便,因为你每写一个接口都要添加一个BindingResult参数,然后再提取错误信息返回给前端。

    这样有点麻烦,并且重复代码很多(尽管可以将这个重复代码封装成方法)。我们能否去掉BindingResult这一步呢?当然是可以的!

    Validator + 自动抛出异常

    我们完全可以将BindingResult这一步给去掉:

    @PostMapping("/addUser")
    public String addUser(@RequestBody @Valid User user) {
        return userService.addUser(user);
    }
    

    去掉之后会发生什么事情呢?直接来试验一下,还是按照之前一样故意传递一个不符合校验规则的参数给接口。此时我们观察控制台可以发现接口已经引发MethodArgumentNotValidException异常了:

    其实这样就已经达到我们想要的效果了,参数校验不通过自然就不执行接下来的业务逻辑,去掉BindingResult后会自动引发异常,异常发生了自然而然就不会执行业务逻辑。也就是说,我们完全没必要添加相关BindingResult相关操作嘛。

    不过事情还没有完,异常是引发了,可我们并没有编写返回错误信息的代码呀,那参数校验失败了会响应什么数据给前端呢?

    我们来看一下刚才异常发生后接口响应的数据:

    没错,是直接将整个错误对象相关信息都响应给前端了!这样就很难受,不过解决这个问题也很简单,就是我们接下来要讲的全局异常处理!

    全局异常处理

    参数校验失败会自动引发异常,我们当然不可能再去手动捕捉异常进行处理,不然还不如用之前BindingResult方式呢。又不想手动捕捉这个异常,又要对这个异常进行处理,那正好使用SpringBoot全局异常处理来达到一劳永逸的效果!

    基本使用

    首先,我们需要新建一个类,在这个类上加上@ControllerAdvice或@RestControllerAdvice注解,这个类就配置成全局处理类了。(这个根据你的Controller层用的是@Controller还是@RestController来决定)

    然后在类中新建方法,在方法上加上@ExceptionHandler注解并指定你想处理的异常类型,接着在方法内编写对该异常的操作逻辑,就完成了对该异常的全局处理!

    我们现在就来演示一下对参数校验失败抛出的MethodArgumentNotValidException全局处理:

    @RestControllerAdvice
    public class ExceptionControllerAdvice {
    
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public String MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
            // 从异常对象中拿到ObjectError对象
            ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
            // 然后提取错误提示信息进行返回
            return objectError.getDefaultMessage();
        }
    
    }
    

    我们再来看下这次校验失败后的响应数据:

    没错,这次返回的就是我们制定的错误提示信息!我们通过全局异常处理优雅的实现了我们想要的功能!以后我们再想写接口参数校验,就只需要在入参的成员变量上加上Validator校验规则注解,然后在参数上加上@Valid注解即可完成校验,校验失败会自动返回错误提示信息,无需任何其他代码!更多的校验思路:SpringBoot实现通用的接口参数校验

    自定义异常

    全局处理当然不会只能处理一种异常,用途也不仅仅是对一个参数校验方式进行优化。在实际开发中,如何对异常处理其实是一个很麻烦的事情。传统处理异常一般有以下烦恼:

    • 是捕获异常(try…catch)还是抛出异常(throws)

    • 是在controller层做处理还是在service层处理又或是在dao层做处理

    • 处理异常的方式是啥也不做,还是返回特定数据,如果返回又返回什么数据

    • 不是所有异常我们都能预先进行捕捉,如果发生了没有捕捉到的异常该怎么办?

    以上这些问题都可以用全局异常处理来解决,全局异常处理也叫统一异常处理,全局和统一处理代表什么?代表规范!规范有了,很多问题就会迎刃而解!

    全局异常处理的基本使用方式大家都已经知道了,我们接下来更进一步的规范项目中的异常处理方式:自定义异常。

    在很多情况下,我们需要手动抛出异常,比如在业务层当有些条件并不符合业务逻辑,我这时候就可以手动抛出异常从而触发事务回滚。那手动抛出异常最简单的方式就是throw new RuntimeException("异常信息")了,不过使用自定义会更好一些:

    • 自定义异常可以携带更多的信息,不像这样只能携带一个字符串。

    • 项目开发中经常是很多人负责不同的模块,使用自定义异常可以统一了对外异常展示的方式。

    • 自定义异常语义更加清晰明了,一看就知道是项目中手动抛出的异常。

    我们现在就来开始写一个自定义异常:

    @Getter //只要getter方法,无需setter
    public class APIException extends RuntimeException {
        private int code;
        private String msg;
    
        public APIException() {
            this(1001, "接口错误");
        }
    
        public APIException(String msg) {
            this(1001, msg);
        }
    
        public APIException(int code, String msg) {
            super(msg);
            this.code = code;
            this.msg = msg;
        }
    }
    

    在刚才的全局异常处理类中记得添加对我们自定义异常的处理:

    @ExceptionHandler(APIException.class)
    public String APIExceptionHandler(APIException e) {
        return e.getMsg();
    }
    

    这样就对异常的处理就比较规范了,当然还可以添加对Exception的处理,这样无论发生什么异常我们都能屏蔽掉然后响应数据给前端,不过建议最后项目上线时这样做,能够屏蔽掉错误信息暴露给前端,在开发中为了方便调试还是不要这样做。

    现在全局异常处理和自定义异常已经弄好了,不知道大家有没有发现一个问题,就是当我们抛出自定义异常的时候全局异常处理只响应了异常中的错误信息msg给前端,并没有将错误代码code返回。这就要引申出我们接下来要讲的东西了:数据统一响应

    数据统一响应

    现在我们规范好了参数校验方式和异常处理方式,然而还没有规范响应数据!比如我要获取一个分页信息数据,获取成功了呢自然就返回的数据列表,获取失败了后台就会响应异常信息,即一个字符串,就是说前端开发者压根就不知道后端响应过来的数据会是啥样的!所以,统一响应数据是前后端规范中必须要做的!

    自定义统一响应体

    统一数据响应第一步肯定要做的就是我们自己自定义一个响应体类,无论后台是运行正常还是发生异常,响应给前端的数据格式是不变的!那么如何定义响应体呢?关于异常的设计:如何更优雅的设计异常

    可以参考我们自定义异常类,也来一个响应信息代码code和响应信息说明msg:

    @Getter
    public class ResultVO<T> {
        /**
         * 状态码,比如1000代表响应成功
         */
        private int code;
        /**
         * 响应信息,用来说明响应情况
         */
        private String msg;
        /**
         * 响应的具体数据
         */
        private T data;
    
        public ResultVO(T data) {
            this(1000, "success", data);
        }
    
        public ResultVO(int code, String msg, T data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    }
    

    然后我们修改一下全局异常处理那的返回值:

    @ExceptionHandler(APIException.class)
    public ResultVO<String> APIExceptionHandler(APIException e) {
        // 注意哦,这里返回类型是自定义响应体
        return new ResultVO<>(e.getCode(), "响应失败", e.getMsg());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 注意哦,这里返回类型是自定义响应体
        return new ResultVO<>(1001, "参数校验失败", objectError.getDefaultMessage());
    }
    

    我们再来看一下此时如果发生异常了会响应什么数据给前端:

    OK,这个异常信息响应就非常好了,状态码和响应说明还有错误提示数据都返给了前端,并且是所有异常都会返回相同的格式!异常这里搞定了,别忘了我们到接口那也要修改返回类型,我们新增一个接口好来看看效果:

    @GetMapping("/getUser")
    public ResultVO<User> getUser() {
        User user = new User();
        user.setId(1L);
        user.setAccount("12345678");
        user.setPassword("12345678");
        user.setEmail("123@qq.com");
    
        return new ResultVO<>(user);
    }
    

    看一下如果响应正确返回的是什么效果:

    这样无论是正确响应还是发生异常,响应数据的格式都是统一的,十分规范!

    数据格式是规范了,不过响应码code和响应信息msg还没有规范呀!大家发现没有,无论是正确响应,还是异常响应,响应码和响应信息是想怎么设置就怎么设置,要是10个开发人员对同一个类型的响应写10个不同的响应码,那这个统一响应体的格式规范就毫无意义!所以,必须要将响应码和响应信息给规范起来。

    响应码枚举

    要规范响应体中的响应码和响应信息用枚举简直再恰当不过了,我们现在就来创建一个响应码枚举类:

    @Getter
    public enum ResultCode {
    
        SUCCESS(1000, "操作成功"),
    
        FAILED(1001, "响应失败"),
    
        VALIDATE_FAILED(1002, "参数校验失败"),
    
        ERROR(5000, "未知错误");
    
        private int code;
        private String msg;
    
        ResultCode(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
    }
    

    然后修改响应体的构造方法,让其只准接受响应码枚举来设置响应码和响应信息:

    public ResultVO(T data) {
        this(ResultCode.SUCCESS, data);
    }
    
    public ResultVO(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.data = data;
    }
    

    然后同时修改全局异常处理的响应码设置方式:

    @ExceptionHandler(APIException.class)
    public ResultVO<String> APIExceptionHandler(APIException e) {
        // 注意哦,这里传递的响应码枚举
        return new ResultVO<>(ResultCode.FAILED, e.getMsg());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<String> MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 注意哦,这里传递的响应码枚举
        return new ResultVO<>(ResultCode.VALIDATE_FAILED, objectError.getDefaultMessage());
    }
    

    这样响应码和响应信息只能是枚举规定的那几个,就真正做到了响应数据格式、响应码和响应信息规范化、统一化!

    全局处理响应数据

    接口返回统一响应体 + 异常也返回统一响应体,其实这样已经很好了,但还是有可以优化的地方。要知道一个项目下来定义的接口搞个几百个太正常不过了,要是每一个接口返回数据时都要用响应体来包装一下好像有点麻烦,有没有办法省去这个包装过程呢?当然是有滴,还是要用到全局处理。

    首先,先创建一个类加上注解使其成为全局处理类。然后继承ResponseBodyAdvice接口重写其中的方法,即可对我们的controller进行增强操作,具体看代码和注释:

    @RestControllerAdvice(basePackages = {"com.rudecrab.demo.controller"}) // 注意哦,这里要加上需要扫描的包
    public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
        @Override
        public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
            // 如果接口返回的类型本身就是ResultVO那就没有必要进行额外的操作,返回false
            return !returnType.getGenericParameterType().equals(ResultVO.class);
        }
    
        @Override
        public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest request, ServerHttpResponse response) {
            // String类型不能直接包装,所以要进行些特别的处理
            if (returnType.getGenericParameterType().equals(String.class)) {
                ObjectMapper objectMapper = new ObjectMapper();
                try {
                    // 将数据包装在ResultVO里后,再转换为json字符串响应给前端
                    return objectMapper.writeValueAsString(new ResultVO<>(data));
                } catch (JsonProcessingException e) {
                    throw new APIException("返回String类型错误");
                }
            }
            // 将原本的数据包装在ResultVO里
            return new ResultVO<>(data);
        }
    }
    

    重写的这两个方法是用来在controller将数据进行返回前进行增强操作,supports方法要返回为true才会执行beforeBodyWrite方法,所以如果有些情况不需要进行增强操作可以在supports方法里进行判断。对返回数据进行真正的操作还是在beforeBodyWrite方法中,我们可以直接在该方法里包装数据,这样就不需要每个接口都进行数据包装了,省去了很多麻烦。

    我们可以现在去掉接口的数据包装来看下效果:

    @GetMapping("/getUser")
    public User getUser() {
        User user = new User();
        user.setId(1L);
        user.setAccount("12345678");
        user.setPassword("12345678");
        user.setEmail("123@qq.com");
        // 注意哦,这里是直接返回的User类型,并没有用ResultVO进行包装
        return user;
    }
    

    然后我们来看下响应数据:

    成功对数据进行了包装!

    注意:beforeBodyWrite方法里包装数据无法对String类型的数据直接进行强转,所以要进行特殊处理,这里不讲过多的细节,有兴趣可以自行深入了解。

    总结

    自此整个后端接口基本体系就构建完毕了

    • 通过Validator + 自动抛出异常来完成了方便的参数校验

    • 通过全局异常处理 + 自定义异常完成了异常操作的规范

    • 通过数据统一响应完成了响应数据的规范

    • 多个方面组装非常优雅的完成了后端接口的协调,让开发人员有更多的经历注重业务逻辑代码,轻松构建后端接口

    再次强调,项目体系该怎么构建、后端接口该怎么写都没有一个绝对统一的标准,不是说一定要按照本文的来才是最好的,你怎样都可以,本文每一个环节你都可以按照自己的想法来进行编码,我只是提供了一个思路!

    GitHub地址:https://github.com/RudeCrab/rude-java/tree/master/project-practice/validation-and-exception-handler

    END

    展开全文
  • 主要介绍了springboot+dubbo+validation 进行rpc参数校验的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 主要介绍了如何在spring-boot中进行参数校验及lombok的使用详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • Spring Boot + validation + AOP 请求参数校验 一、validation 校验注解 通用 @Null 被注释的属性必须为 null @NotNull被注释的属性必须为 null @AssertTrue 被注释的属性必须为 true @AssertFalse 被注释的属性...
  • 项目参数校验并抛出统一异常

    千次阅读 2019-11-25 16:54:21
    1.在dto中使用注解做参数校验,并且为参数添加get set方法,这是必须的,例如 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> ...

    实现步骤:
    1.在dto中使用注解做参数校验,并且为参数添加get set方法,这是必须的,例如

    		<dependency>
    			<groupId>org.projectlombok</groupId>
    			<artifactId>lombok</artifactId>
    			<version>1.18.4</version>
    		</dependency>
    

    在这里插入图片描述

    2.在controller中,为接口方法需要校验的参数加上@Valid注解,并添加一个参数BindingResult紧跟在@Valid注解的参数后面,如果有多个参数加了@Valid,就有多个BindingResult。
    在这里插入图片描述
    3.编写一个切面类去统一处理返回的结果

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

    在这里插入图片描述

    package com.nelson.edu.aspect;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.FieldError;
    import org.springframework.validation.ObjectError;
    
    @Aspect
    @Component
    public class ValidateAspect {
    
        @Pointcut("execution(public * com.nelson..edu.controller.*.*(..))")
        public void validate() {
        }
    
        @Around("validate()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            BindingResult bindingResult = null;
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length != 0) {
                for (Object object : args) {
                    if (object instanceof BindingResult) {
                        bindingResult = (BindingResult) object;
                        break;
                    }
                }
            }
            if (bindingResult != null && bindingResult.hasErrors()) {
                StringBuffer sb = new StringBuffer();
                for (ObjectError objectError : bindingResult.getAllErrors()) {
                    sb.append(((FieldError) objectError).getField() + " : ").append(objectError.getDefaultMessage() + ",");
                }
                sb.deleteCharAt(sb.length() - 1);
                return sb.toString();
            }
            return joinPoint.proceed();
        }
    }
    
    展开全文
  • 下面小编就为大家带来一篇java自定义注解实现前后台参数校验的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 主要介绍的Springboot使用@Valid 和AOP做参数校验及日志输出问题,本文通过代码讲解的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
  • 基于Spring boot + maven,以注解+AOP方式实现的java后端项目接口参数校验框架。迄今为止使用最简单、最容易理解的参数校验方案。博客地址:https://blog.csdn.net/weixin_42686388/article/details/104009771
  • Spring Boot 参数校验

    2019-12-10 15:04:08
    Bean Validation是Java定义的一套基于注解的数据校验规范。这里简单实现了Spring Boot 参数校验,包括自定义注解,分组校验,全局异常处理等
  • 主要介绍了Spring MVC参数校验的相关资料,主要是针对`@RequestBody`返回`400`的问题,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面跟着小编来一起学习学习吧。
  • 参数校验参数是否为空接口,循环判断多个参数,可以直接使用
  • Koa2请求参数校验中间件,基于 安装 npm i -S koa-params-validator 环境要求 节点> = 7.6 Koa> = 2.0 快速开始 const validator = require ( 'koa-params-validator' ) const app = new require ( 'koa' ) ( ) ; ...
  • 故事的开始: 前端进行校验,这个概念应该是没有问题的吧?比如:前端输入手机号的、链接的、邮箱地址的,我们是不是web端传入参数的时候,要对用户填入的格式进行校验。...2、“后端进行参数校验,...

    故事的开始:

    前端进行校验,这个概念应该是没有问题的吧?比如:前端输入手机号的、链接的、邮箱地址的,我们是不是web端传入参数的时候,要对用户填入的格式进行校验。OK,这个概念大家都懂。但是,一般情况下,后端我们也要进行校验。

    为什么?后端为什么校验?

    我目前听到的两种说法:1、“客户端传来的参数都是不可信的”我大boss说的,具体原因不知道,就是说是行业的一种说法;2、“后端进行参数校验,是防止别人通过接口乱刷服务”。反正我是信第二种说法的,如果一些不安好心的人,通过接口刷我们的服务,随便哪个参数我们都允许填入,会导致数据库中导致大量的脏数据、风险。如果注入的是病毒怎么办?所以,后端是要进行参数校验的。

    怎么进行后端参数校验?

    我目前也是了解到了两种做法。

    1、通过注解的方法,在实体上加注解。通过工具,当客户端传入参数的时候,自动进行校验。

    大概的方法:pom中引入工具类;然后实体中添加注解;controller层传入数据时添加@validator进行参数校验。参考下面这篇文章:

    后端参数校验:通过对实体添加注解——Hibernate validator

    2、通过工具类,进行参数校验。

    思路:将常用的校验方法放在一个类中,然后哪个参数需要校验,就调用工具类中的方法。我自认为很好用。

    下面提供一个工具类,正则表达式工具类:RegexUtils.java

    
    
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * @author Meng
     * @Date 2018/12/14 14:00
     */
    public class RegexUtils {
    
            /**
             * 验证Email
             * @param email email地址,格式:zhangsan@zuidaima.com,zhangsan@xxx.com.cn,xxx代表邮件服务商
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkEmail(String email) {
                String regex = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?";
                return Pattern.matches(regex, email);
            }
    
            /**
             * 验证身份证号码
             * @param idCard 居民身份证号码15位或18位,最后一位可能是数字或字母
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkIdCard(String idCard) {
                String regex = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}";
                return Pattern.matches(regex,idCard);
            }
    
            /**
             * 验证手机号码(支持国际格式,+86135xxxx...(中国内地),+00852137xxxx...(中国香港))
             * @param mobile 移动、联通、电信运营商的号码段
             *<p>移动的号段:134(0-8)、135、136、137、138、139、147(预计用于TD上网卡)
             *、150、151、152、157(TD专用)、158、159、187(未启用)、188(TD专用)</p>
             *<p>联通的号段:130、131、132、155、156(世界风专用)、185(未启用)、186(3g)</p>
             *<p>电信的号段:133、153、180(未启用)、189</p>
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkMobile(String mobile) {
                String regex = "(\\+\\d+)?1[34578]\\d{9}$";
                return Pattern.matches(regex,mobile);
            }
    
            /**
             * 验证固定电话号码
             * @param phone 电话号码,格式:国家(地区)电话代码 + 区号(城市代码) + 电话号码,如:+8602085588447
             * <p><b>国家(地区) 代码 :</b>标识电话号码的国家(地区)的标准国家(地区)代码。它包含从 0 到 9 的一位或多位数字,
             *  数字之后是空格分隔的国家(地区)代码。</p>
             * <p><b>区号(城市代码):</b>这可能包含一个或多个从 0 到 9 的数字,地区或城市代码放在圆括号——
             * 对不使用地区或城市代码的国家(地区),则省略该组件。</p>
             * <p><b>电话号码:</b>这包含从 0 到 9 的一个或多个数字 </p>
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkPhone(String phone) {
                String regex = "(\\+\\d+)?(\\d{3,4}\\-?)?\\d{7,8}$";
                return Pattern.matches(regex, phone);
            }
    
            /**
             * 验证整数(正整数和负整数)
             * @param digit 一位或多位0-9之间的整数
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkDigit(String digit) {
                String regex = "\\-?[1-9]\\d+";
                return Pattern.matches(regex,digit);
            }
    
            /**
             * 验证整数和浮点数(正负整数和正负浮点数)
             * @param decimals 一位或多位0-9之间的浮点数,如:1.23,233.30
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkDecimals(String decimals) {
                String regex = "\\-?[1-9]\\d+(\\.\\d+)?";
                return Pattern.matches(regex,decimals);
            }
    
            /**
             * 验证空白字符
             * @param blankSpace 空白字符,包括:空格、\t、\n、\r、\f、\x0B
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkBlankSpace(String blankSpace) {
                String regex = "\\s+";
                return Pattern.matches(regex,blankSpace);
            }
    
            /**
             * 验证中文
             * @param chinese 中文字符
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkChinese(String chinese) {
                String regex = "^[\u4E00-\u9FA5]+$";
                return Pattern.matches(regex,chinese);
            }
    
            /**
             * 验证日期(年月日)
             * @param birthday 日期,格式:1992-09-03,或1992.09.03
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkBirthday(String birthday) {
                String regex = "[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}";
                return Pattern.matches(regex,birthday);
            }
    
            /**
             * 验证URL地址
             * @param url 格式:http://blog.csdn.net:80/xyang81/article/details/7705960? 或 http://www.csdn.net:80
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkURL(String url) {
                String regex = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?";
                return Pattern.matches(regex, url);
            }
    
            /**
             * <pre>
             * 获取网址 URL 的一级域
             * </pre>
             *
             * @param url
             * @return
             */
            public static String getDomain(String url) {
                Pattern p = Pattern.compile("(?<=http://|\\.)[^.]*?\\.(com|cn|net|org|biz|info|cc|tv)", Pattern.CASE_INSENSITIVE);
                // 获取完整的域名
                // Pattern p=Pattern.compile("[^//]*?\\.(com|cn|net|org|biz|info|cc|tv)", Pattern.CASE_INSENSITIVE);
                Matcher matcher = p.matcher(url);
                matcher.find();
                return matcher.group();
            }
            /**
             * 匹配中国邮政编码
             * @param postcode 邮政编码
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkPostcode(String postcode) {
                String regex = "[1-9]\\d{5}";
                return Pattern.matches(regex, postcode);
            }
    
            /**
             * 匹配IP地址(简单匹配,格式,如:192.168.1.1,127.0.0.1,没有匹配IP段的大小)
             * @param ipAddress IPv4标准地址
             * @return 验证成功返回true,验证失败返回false
             */
            public static boolean checkIpAddress(String ipAddress) {
                String regex = "[1-9](\\d{1,2})?\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))";
                return Pattern.matches(regex, ipAddress);
            }
    
    
    }

     怎么用?我是直接在controller层进行调用的。!RegexUtils.checkEmail(obj.getContactsEmail())

    
        @RequestMapping(value = "/submit", method = RequestMethod.POST)
        @ResponseBody
        public BaseResponse submitTenantInfo(@RequestBody WebObjRequest<TenantInfo> request) {
            TenantInfo obj = request.getObj();
            if (obj == null || !request.isCorrectParams()|| !RegexUtils.checkEmail(obj.getContactsEmail())) {
                return new BaseResponse(RespConstants.GLOBAL_PARAM_ERROR);
            }
           ```````
            return new BaseResponse(RespConstants.GLOBAL_SUCCESS);
        }

    最后,我想说的是。客户端校验和服务端校验的不同:我的理解是,客户端校验的提示,是提示用户正确输入,加强用户体验。服务端校验是为了接口安全,防止非法用户乱刷。所以,服务端进行参数校验的时候,返回的错误码不要具体到具体的错误,返回“参数异常”相关异常的错误即可。

    相关拓展:常用的正则表达式

    你是技术宅么?那就加入我们吧!点击下方链接 或 扫描二维码 即可。

    欢迎加入 CSDN技术交流群2:(点击即可加群)QQ群:456543087

                   CSDN技术交流群(已满员):(点击即可加群)QQ群:681223095。  

    商务合作@群主,谢谢!

    因经常有人留言,未能及时查看到和回复,所以特建此群,以方便交流。方便问题讨论,有问题和没有问题的小伙伴均可加入,用作自我学习和共同进步。本博主不一定长期在线,但是qq群里会有很多热心的小伙伴,大家一起讨论解决问题。

     

     

     

    展开全文
  • 主要原因在于:API的参数校验目前缺少比较流行的方案、现有方案适用与自己的项目、项目部署在内网要求高、或者干脆参数校验这种事情丢给前端来做 hibernate-validator hibernate-validator是Hibernate项目中的一...
  • 1)代码的可读性变差,函数中充斥着参数校验的代码,尤其是当参数特别多时; 2)不同的开发者对参数的校验方式不同,可能会在参数校验中引入缺陷,如: val=intval($val); if($val<=0){ //参数错误 } 当$val参数为'...
  • sap发票校验两个常用的用户参数sap发票校验两个常用的用户参数sap发票校验两个常用的用户参数sap发票校验两个常用的用户参数
  • Java参数校验

    千次阅读 2019-01-23 16:19:56
    基本步骤 引入pom依赖 ...-- 参数校验 --&amp;amp;gt; &amp;amp;lt;!-- start --&amp;amp;gt; &amp;amp;lt;dependency&amp;amp;gt; &amp;amp;lt;groupId&amp;amp;gt
  • 前言 在 Web 开发中, 我们经常需要校验各种参数,这是一件繁琐又重要的事情,对于很多人来说,...有多少参数,我就写多少个 if 语句做判断,校验不通过的都写一句友好的提示,如: 工具型 自己写个参数校验的通用工...
  • SpringBoot里参数校验/参数验证

    万次阅读 多人点赞 2019-05-08 13:47:56
    在控制器类的方法里自己写校验逻辑代码当然也可以,只是代码比较丑陋,有点“low”。业界有更好的处理方法,分别阐述如下。 2、PathVariable校验 @GetMapping("/path/{group:[a-zA-Z0-9_]+}/{userid}") @...
  • SpringBoot使用validation-api实现对枚举类参数校验 前言 之前写了一个博客是关于使用SpringBoot使用validation-api实现参数校验,当时使用的注解都是validation-api自带的注解只能完成对空值、长度等简单的校验,在...
  • 主要介绍了SpringBoot中的参数校验,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • java后台参数校验 一. 单个参数的校验方式 实现原理:@ControllerAdvice,@Validated注解对参数进行校验 实现方式: 1.创建统一的系统常量 package com.hm.middle_service.common; /** * @Author:huangzhimin * ...
  • 使用SpringBoot通过自定义注解+AOP+全局异常处理实现参数统一非空校验
  • Spring参数校验--List类型参数校验

    千次阅读 2019-03-06 00:05:27
    1.遇到的问题: 今天开发接口,遇到请求...发现参数校验失效。 // 控制器 @RequestMapping(value = "/saveVideo", method = RequestMethod.POST) @ResponseBody public List&lt;SaveVideoRe...
  • 今天就看到了一段代码,里面判断参数是否为空长度是多少,格式对对等就是一堆判断,然后代码中就很多判断,或者单独写了判断方法,如果请求类复用,或者复用但是判断都一样的话可以优先使用请求层校验,或者接口...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 377,422
精华内容 150,968
关键字:

参数校验不通过