首页 > 其他分享 >瞧瞧别人家的参数校验,那叫一个优雅!

瞧瞧别人家的参数校验,那叫一个优雅!

时间:2024-12-03 18:34:04浏览次数:5  
标签:String 校验 class 瞧瞧 ResponseEntity message 优雅 public

前言

对于开发人员来说,对用户输入的参数或者系统参数做校验,是日常工作之一。

很多小伙伴在写接口的时候,可能都会碰到一个问题:参数校验应该怎么写?

比如,开发一个用户注册接口,需要校验以下条件:

  • 用户名不能为空,长度在 3 到 20 个字符之间;
  • 密码不能为空,长度至少为 8 个字符;
  • 年龄必须是正整数,不能超过 120;
  • 邮箱必须符合标准格式。

乍一看,这种校验逻辑看起来很简单嘛,直接写几个 if 就完事了。

但真的这么简单吗?

接下来我们就从传统的参数校验入手,看看问题出在哪,然后再聊聊 Spring Boot 中如何优雅地实现参数校验,希望对你会有所帮助。

一、传统参数校验的问题

很多人可能会直接在 Controller 里手写校验逻辑,比如下面这个代码:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> register(@RequestBody Map<String, Object> request) {
        String username = (String) request.get("username");
        if (username == null || username.length() < 3 || username.length() > 20) {
            return ResponseEntity.badRequest().body("用户名不能为空,且长度必须在3到20之间");
        }

        String password = (String) request.get("password");
        if (password == null || password.length() < 8) {
            return ResponseEntity.badRequest().body("密码不能为空,且长度至少为8个字符");
        }

        Integer age = (Integer) request.get("age");
        if (age == null || age <= 0 || age > 120) {
            return ResponseEntity.badRequest().body("年龄必须是正整数,且不能超过120");
        }

        return ResponseEntity.ok("注册成功!");
    }
}

这段代码乍一看没什么问题,但如果仔细分析,会发现一堆隐患:

  1. 代码冗余:校验逻辑散落在 Controller 里,写起来麻烦,后期维护更是灾难。
  2. 重复劳动:类似的校验逻辑可能会出现在多个接口里,导致代码重复度极高。
  3. 用户体验差:返回的错误信息不统一、不规范,前端开发还得猜用户输入到底哪儿错了。
  4. 扩展性差:万一某天需要加新的校验规则,你可能要到处改代码。

所以,这种手写参数校验的方式,在简单场景下勉强能用,但如果业务变复杂,问题会越来越多。

那么问题来了,那有没有更优雅的方式来处理这些问题呢?

答:当然是有的。

二、Spring Boot 的参数校验机制

在 Spring Boot 中,我们可以使用 Hibernate Validator(Bean Validation 的参考实现)来实现参数校验。

它的核心思路是:把校验逻辑从业务代码里抽离出来,用注解的方式声明校验规则

接下来我们一步步来看怎么实现。

1. 使用注解进行参数校验

首先,定义一个用于接收用户注册参数的 DTO 对象:

@Data
public class UserRegistrationRequest {

    @NotNull(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度必须在3到20之间")
    private String username;

    @NotNull(message = "密码不能为空")
    @Size(min = 8, message = "密码长度至少为8个字符")
    private String password;

    @NotNull(message = "年龄不能为空")
    @Min(value = 1, message = "年龄必须是正整数")
    @Max(value = 120, message = "年龄不能超过120")
    private Integer age;

    @Email(message = "邮箱格式不正确")
    private String email;
}

这里我们用了几个常见的校验注解:

  • @NotNull:字段不能为空;
  • @Size:限制字符串长度;
  • @Min@Max:限制数值范围;
  • @Email:校验邮箱格式。

这些注解由 Hibernate Validator 提供,基本涵盖了日常开发中的大部分校验需求。

然后,在 Controller 中这样写:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> register(@Valid @RequestBody UserRegistrationRequest request) {
        return ResponseEntity.ok("注册成功!");
    }
}

注意这里的 @Valid 注解,它的作用是告诉 Spring:对请求参数进行校验

2. 统一处理校验错误

如果前端传的参数不合法,Spring 会抛出一个 MethodArgumentNotValidException 异常。默认情况下,这个异常返回的信息不太友好,可能是这样的:

{
  "timestamp": "2024-01-01T12:00:00.000+00:00",
  "status": 400,
  "error": "Bad Request",
  "message": "Validation failed for object='userRegistrationRequest'. Error count: 2",
  "path": "/api/users/register"
}

为了提升用户体验,我们可以用全局异常处理器来统一格式化错误信息:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(error -> {
            errors.put(error.getField(), error.getDefaultMessage());
        });
        return ResponseEntity.badRequest().body(errors);
    }
}

现在,当参数校验失败时,返回的错误信息会变成这样:

{
  "username": "用户名长度必须在3到20之间",
  "password": "密码不能为空"
}

清晰又直观,用户一看就明白自己错在哪儿了。

最近建了一些工作内推群,收集了不少工作岗位,加我微信:su_san_java,备注:博客园+所在城市,即可进群。

三、应对复杂场景的高级技巧

1. 分组校验

有些场景下,不同的接口对参数的校验规则是不一样的,比如:

  • 注册接口要求 usernamepassword 是必填项;
  • 更新接口只需要校验 emailage

这种情况下,可以用 分组校验 来解决。

定义校验分组

public interface RegisterGroup {}
public interface UpdateGroup {}

在字段上指定分组

public class UserRequest {

    @NotNull(groups = RegisterGroup.class, message = "用户名不能为空")
    @Size(min = 3, max = 20, groups = RegisterGroup.class, message = "用户名长度必须在3到20之间")
    private String username;

    @NotNull(groups = RegisterGroup.class, message = "密码不能为空")
    private String password;

    @Email(groups = UpdateGroup.class, message = "邮箱格式不正确")
    private String email;

    @Min(value = 1, groups = UpdateGroup.class, message = "年龄必须是正整数")
    private Integer age;
}

在 Controller 中指定分组

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping("/register")
    public ResponseEntity<String> register(@Validated(RegisterGroup.class) @RequestBody UserRequest request) {
        return ResponseEntity.ok("注册成功!");
    }

    @PutMapping("/update")
    public ResponseEntity<String> update(@Validated(UpdateGroup.class) @RequestBody UserRequest request) {
        return ResponseEntity.ok("更新成功!");
    }
}

2. 自定义校验注解

如果 Hibernate Validator 提供的注解不能满足需求,还可以自定义校验注解。例如,校验手机号格式。

定义注解

@Documented
@Constraint(validatedBy = PhoneValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidPhone {
    String message() default "手机号格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

实现校验逻辑

public class PhoneValidator implements ConstraintValidator<ValidPhone, String> {

    private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && value.matches(PHONE_REGEX);
    }
}

使用自定义注解

public class UserRequest {
    @ValidPhone
    private String phone;
}

四、总结

优雅的参数校验不仅能提高代码的可维护性,还能显著提升用户体验。

在 Spring Boot 中,通过使用 Hibernate Validator 提供的注解,配合分组校验、自定义校验和统一异常处理。

我们可以轻松实现简洁、高效、可扩展的参数校验机制。

优雅的参数校验的秘籍是:

  1. 注解优先:能用注解解决的校验,就不要手写逻辑代码。
  2. 分离校验逻辑:参数校验应该集中在 DTO 层,避免散落在业务代码中。
  3. 全局统一异常处理:确保错误信息规范化、友好化。
  4. 合理使用分组校验:根据接口需求灵活调整校验规则。
  5. 覆盖边界条件:通过单元测试验证校验逻辑,确保没有漏网之鱼。

如果看了这篇文章有些收获,记得给我点赞和转发喔。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。
求一键三连:点赞、转发、在看。
关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

标签:String,校验,class,瞧瞧,ResponseEntity,message,优雅,public
From: https://www.cnblogs.com/12lisu/p/18584709

相关文章

  • 日志注入与日志伪造是高级攻击者用来规避安全监测、隐藏攻击痕迹的常见手段。面对这种
    伪造和间接生成Windows日志的安全案例是存在的,且通常与攻击者利用日志注入或日志伪造技术有关。攻击者可以通过各种方法篡改Windows日志,以掩盖其恶意活动,掩饰入侵行为,或误导安全团队的调查。以下是几种可能的伪造和间接生成Windows日志的安全案例:1. 日志注入与伪造攻击......
  • 指南:如何优雅地写代码
    0xFF前言Talkischeap.Showmethecode.无论是程序员还是OIer,写代码都尤为重要。然而,有些人的代码使人舒适,有些人的代码使人难受。笔者认为其中的原因,主要是“优雅”与否。本文总结了一点关于如何让代码更优雅的建议,供大家参考。0x00控制行数控制行数?压行呗。秒了!#i......
  • uniapp进阶技巧:如何优雅地封装request实例
    在uniapp开发过程中,合理封装网络请求是提高代码质量和开发效率的关键。本文将介绍一种更为优雅的封装方式,通过创建一个request实例来管理不同类型的HTTP请求。一、准备工作在开始封装之前,请确保你的项目中已经安装了uniapp开发环境,并且具备以下基础知识:熟悉JavaScript基本......
  • 请问PbootCMS 登录失败及表单提交校验失败的解决方案
    在使用PbootCMS时,可能会遇到登录失败、表单提交校验失败等问题。以下是针对这些常见问题的详细解决方案。一、登录失败或表单提交校验失败描述:用户尝试登录时失败。表单提交时校验失败。解决方案:检查服务器环境:确保服务器环境满足PbootCMS的最低要求。检查PH......
  • 用VsCode优雅写MarkDown
    插件推荐MarkdownAllinOne快捷键作用Ctrl+B加粗Ctrl+I斜体Alt+S删除线Ctrl+]增加缩进Ctrl+[减少缩进Ctrl+Shift+]标题升级Ctrl+Shift+[标题降级Alt+C勾选/取消勾选任务列表Ctrl+M数学环境Alt+Shif......
  • 【ElementPlus】el-form使用技巧:动态切换校验规则的最佳实践
    喵~今天分享一篇在ElementPlus中使用el-form动态切换校验规则的实用方法。一、问题概述作为前端开发人员,在开发项目中,特别是后台管理系统,表单的使用是必不可少的。当业务需求复杂时,常常需要根据不同的参数动态切换校验规则。当动态切换校验规则时,可能会出现一些意想不......
  • gofiber: 用go-playground/validator校验参数,自定义错误信息
    一,go-playground/validator官方代码地址https://github.com/go-playground/validator二,安装$goget-ugithub.com/go-playground/validator/v10go:downloadinggithub.com/go-playground/validator/v10v10.23.0go:downloadinggithub.com/gabriel-vasile/mimetypev1.4.......
  • JSR303统一校验
    1、简介jsr是JavaSpecificationRequests的缩写,意思是java的请求规范。周志明老师的书上还着重介绍过jsr292(jvm多语言支持包括Kotlin,Clojure,JRuby,Scala等)。JSR303着重参数校验功能,点开javax.validation.constraints,可以看到已经封装好的注解有这些:使用jsr303规范......
  • 记录---前端如何优雅通知用户刷新页面?
    ......
  • [React]antd表单校验函数写法
    来自文心一言通过 rules 属性来定义校验规则,其中可以包含自定义的校验函数 validatorimportReactfrom'react';import{Form,Input,Button}from'antd';constMyForm=()=>{const[form]=Form.useForm();//自定义校验函数constcheckUsername=(......