通过Spring @Validated 更优雅的实现参数校验

背景:我们在实现接口逻辑的时候,严谨的做法是给每个请求对象的参数增加相关校验,比如:

  • ID,是否必填(修改/删除)
  • 手机号,格式+长度,是否必填
  • 邮箱,格式+长度,是否必填
  • 姓名,长度,是否必填
  • 身份证,格式+长度,是否必填

比较直接的实现就是方法逻辑加上:

Assert.notNull(, "id不能为空");
Assert.hasText(phone, "手机号不能为空");

这样把参数校验和业务逻辑混在一起,不是很美观,那这部分代码可用独立出去吗?实际上spring 

validation 已经帮我们实现了,那么我们要做的就4个步骤:
  1. endpoint 在@RequestBody 后面增加 @Validated
  2. feign-client 在@RequestBody 后面增加 @Validated
  3. 在具体的(create/update/delete)req对象增加具体的校验注解,@NotNull @Email @Length等等
  4. 全局异常处理,因为被校验拦截的请求会抛出400(MethodArgumentNotValidException),Bad Request,通过全局异常处理,我们可以把具体的异常信息,以更友好的方式返回给前端
@RestController
public class MyErrorController extends BasicErrorController {

    public MyErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        super(errorAttributes, errorProperties);
    }

    public MyErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorProperties, errorViewResolvers);
    }

    private static final String METHOD_ARGUMENT_NOT_VALID_EXCEPTION = "org.springframework.web.bind.MethodArgumentNotValidException";
    private static final String HTTP_MESSAGE_NOT_READABLE_EXCEPTION = "org.springframework.http.converter.HttpMessageNotReadableException";

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        WebRequest webRequest = new ServletWebRequest(request);
        Throwable e = getError(webRequest);
        HttpStatus httpStatus = getStatus(request);
        if (httpStatus == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(httpStatus);
        }
        Map<String, Object> body = getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
        if (e == null) {
            String exception = String.valueOf(body.get("exception"));
            if (METHOD_ARGUMENT_NOT_VALID_EXCEPTION.equals(exception)) {
                Object errors = body.get("errors");
                if (errors instanceof List) {
                    List errorsArr = (List) errors;
                    if (errorsArr != null && !errorsArr.isEmpty()) {
                        FieldError fieldError = (FieldError) errorsArr.get(0);
                        String defaultMessage = fieldError.getDefaultMessage();
                        String field = fieldError.getField();
                        String code = fieldError.getCode();
                        if (StringUtils.isEmpty(defaultMessage)) {
                            defaultMessage = String.format("参数%s-%s校验不通过", field, code);
                        } else if ("NotNull".equals(code) && "不能为null".equals(defaultMessage)) {
                            defaultMessage = "参数" + field + "不能为空";
                        }
                        body.put("message", defaultMessage);
                        body.remove("error");
                        body.remove("errors");
                        return new ResponseEntity(body, httpStatus);
                    }
                }
            }
            if (HTTP_MESSAGE_NOT_READABLE_EXCEPTION.equals(exception)) {
                return new ResponseEntity(body, httpStatus);
            }
            String error = body.get("error").toString();
            body.put("message", error);
            body.remove("error");
            return new ResponseEntity(body, httpStatus);
        } else {
            if (e instanceof NestedServletException) {
                e = e.getCause();
            }
        }
        if (e instanceof SimpleException) {
            SimpleException simpleException = (SimpleException) e;
            String message = e.getMessage();
            if (StringUtils.isEmpty(message)) {
                message = simpleException.getRespMessage();
            }
            HttpStatus status = HttpStatus.valueOf(simpleException.getStatus());
            body.put("status", status.value());
            body.put("error", status.name());
            body.put("message", message);
            return new ResponseEntity(body, status);
        } else {
            String message = e.getMessage();
            if (StringUtils.isEmpty(message)) {
                message = "系统繁忙,请稍后再试";
            }
            body.put("message", message);
            return new ResponseEntity(body, httpStatus);
        }
    }

    private Throwable getError(WebRequest webRequest) {
        return (Throwable) this.getAttribute(webRequest, "javax.servlet.error.exception");
    }

    private Object getAttribute(RequestAttributes requestAttributes, String name) {
        return requestAttributes.getAttribute(name, 0);
    }
}

/** 呼叫中心用户 */
@lombok.Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class ChatUserCreateReq extends org.xt.maven.plugin.dto.EntityReq {


    /**
     * 邮箱
     */
    @NotNull(message = "邮箱不能为空")
    @Email
    private java.lang.String email;

    /**
     * 手机号
     */
    @NotNull
    @Pattern(regexp = "")
    private java.lang.String phoneNo;

    /**
     * 用户名
     */
    @NotNull
    @Length(max = 32)
    private java.lang.String username;

    /**
     * 昵称
     */
    private java.lang.String nickName;

    /**
     * 性别
     */
    private java.lang.Integer gender;

    /**
     * 头像
     */
    private java.lang.String avatar;

    /**
     * 状态
     */
    private java.lang.Integer status;

    /**
     * 最近一次登录ip
     */
    private java.lang.String lastLoginIp;

    /**
     * 最近一次登录时间
     */
    private java.time.LocalDateTime lastLoginTime;

    /**
     * Default constructor.
     */
    public ChatUserCreateReq() {
    }
}

@EnableAutoConfiguration
@RestController
@RequestMapping("/api/chat/user")
@Api(description = "呼叫中心用户")
public class ChatUserEndpoint{

    @javax.annotation.Resource
    private ChatUserApiService chatUserApiService;


    @ApiOperation("新增")
    @RequestMapping(value = "/create",method = RequestMethod.POST)
    public Response<ChatUserCreateResp> create(@RequestBody @Validated ChatUserCreateReq req, final HttpServletRequest request) throws SimpleException {
        return chatUserApiService.create(req);
    }

    @ApiOperation(value = "查询")
    @RequestMapping(value = "/get", method = RequestMethod.POST)
    public Response<ChatUserGetResp> get(@RequestBody @Validated ChatUserGetReq req, final HttpServletRequest request) throws SimpleException {
        return chatUserApiService.get(req);
    }

    @ApiOperation(value = "更新")
    @RequestMapping(value = "/update",method = RequestMethod.POST)
    public Response<ChatUserUpdateResp> update(@RequestBody @Validated ChatUserUpdateReq req, final HttpServletRequest request) throws SimpleException {
        return chatUserApiService.update(req);
    }

    @ApiOperation(value = "删除")
    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public Response<ChatUserDeleteResp> delete(@RequestBody @Validated ChatUserDeleteReq req, final HttpServletRequest request) throws SimpleException {
        return chatUserApiService.delete(req);
    }

    @ApiOperation(value = "分页查询")
    @RequestMapping(value = "/find", method = RequestMethod.POST)
    public Response<PageInfo<ChatUserFindResp>> find(@RequestBody ChatUserFindReq req, final HttpServletRequest request) throws SimpleException {
        return chatUserApiService.find(req);
    }
}
@org.springframework.cloud.openfeign.FeignClient(name = "${feign.chat.name}", url = "${feign.chat.url:}", contextId = "feignChatUserApiService", primary = false)
public interface ChatUserApiService {

    /**
     * 分页查询
     *
     * @param req
     * @return
     * @throws SimpleException
     */
    @org.springframework.web.bind.annotation.PostMapping("/api/chat/user/find")
    Response<com.github.pagehelper.PageInfo<ChatUserFindResp>> find(@RequestBody ChatUserFindReq req);

    /**
     * 获取对象
     *
     * @param req
     * @return
     * @throws SimpleException
     */

    @org.springframework.web.bind.annotation.PostMapping("/api/chat/user/get")
    Response<ChatUserGetResp> get(@RequestBody @Validated ChatUserGetReq req);

    /**
     * 创建对象
     *
     * @param req
     * @return
     * @throws SimpleException
     */
    @org.springframework.web.bind.annotation.PostMapping("/api/chat/user/create")
    Response<ChatUserCreateResp> create(@RequestBody @Validated ChatUserCreateReq req);

    /**
     * 删除对象
     *
     * @param req
     * @return
     * @throws SimpleException
     */
    @org.springframework.web.bind.annotation.PostMapping("/api/chat/user/delete")
    Response<ChatUserDeleteResp> delete(@RequestBody @Validated ChatUserDeleteReq req);

    /**
     * 更新对象
     *
     * @param req
     * @return
     * @throws SimpleException
     */
    @org.springframework.web.bind.annotation.PostMapping("/api/chat/user/update")
    Response<ChatUserUpdateResp> update(@RequestBody @Validated ChatUserUpdateReq req);
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值