当前位置: 首页 > article >正文

通过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);
}

http://www.kler.cn/a/232585.html

相关文章:

  • 算法每日双题精讲——滑动窗口(长度最小的子数组,无重复字符的最长子串)
  • 在 CentOS 系统中,您可以使用多种工具来查看网络速度和流量
  • 【AI构思渲染】网络直播——建筑绘图大模型生成渲染图
  • 火车车厢重排问题,C++详解
  • AUTOSAR_EXP_ARAComAPI的7章笔记(3)
  • 【大数据学习 | flume】flume的概述与组件的介绍
  • c++之说_13|模板 折叠表达式
  • 贪心算法的应用
  • 【LangChain-04】利用权重和偏差跟踪和检查LangChain代理的提示
  • Pymysql之Connection中常用API
  • 20240206作业
  • 【人工智能】Fine-tuning 微调:解析深度学习中的利器(7)
  • 【Java】eclipse连接MySQL数据库使用笔记(自用)
  • Java面试题2024(Java面试八股文)
  • C语言---计算n的阶乘
  • 云计算运营模式介绍
  • <网络安全>《18 数据安全交换系统》
  • K8S系列文章之 [使用 Alpine 搭建 k3s]
  • 【Flink状态管理(二)各状态初始化入口】状态初始化流程详解与源码剖析
  • 开源大数据集群部署(十)Ranger usersync部署
  • 【RT-DETR有效改进】利用SENetV2重构化网络结构 (ILSVRC冠军得主,全网独家首发)
  • Springboot 整合 Elasticsearch(二):使用HTTP请求来操作ES
  • 开源大型语言模型概览:多语种支持与中文专注
  • ruoyi若依框架SpringSecurity实现分析
  • leetcode (算法)66.加一(python版)
  • 美国服务器如何