🔥🔥🔥参数校验与国际化:提高代码稳定性和可维护性的重要方法

在我们日常的工作中,参数校验是一项非常重要的任务。它能够帮助我们确保代码的稳定性和可靠性,避免因为参数错误而导致的意外情况发生。参数校验通常包括对输入参数的合法性、格式的检查等。而为了提高代码的可维护性和可重用性,我们可以使用一些常见的参数校验方法。

另外,国际化也是一项非常重要的功能,尤其是在跨国项目中。通过国际化的配置,我们可以根据用户的语言环境,自动切换显示的语言,提供更好的用户体验。在参数校验中,我们也可以使用国际化配置来返回相应的提示信息,使得提示信息能够适应不同的语言环境。

  • RequestParam参数校验
  • NotEmpty等注解参数校验
  • 实体类增加参数校验
  • CustomException,自定义异常对参数异常进行国际化定制
  • NotEmptyField,自定义注解实现对参数异常进行国际化定制

好的,让我们一步一步来介绍参数校验的方法。除了上述提到的方法,如果还有一些其他的优秀方法可以用来进行参数校验,也可以提醒我一下!

RequestParam参数校验

RequestParam本身就有对参数进行的基础校验,比如required表示是否参数必填。可以通过在方法参数上使用@RequestParam注解来实现参数校验。以下是示例代码:

java 复制代码
@GetMapping("/hello-not-required")
public String sayHelloByNotRequired(@RequestParam(required = false, name = "who") String who) {
    if (StrUtil.isBlank(who)) {
        who = "World";
    }
    return StrUtil.format("Hello, {}!", who);
}

如果不传递参数,也不会有任何问题,这里不做演示就可以了,非常简单。

NotEmpty等注解参数校验

默认message

然而,虽然RequestParam只能指示参数是否被携带,但它并不负责检验参数值是否为空。因此,我们可以通过与NotEmpty注解结合使用来进行判断。

java 复制代码
@GetMapping("/hello-required")
  public String sayHelloByRequired(@RequestParam @NotEmpty String who) {
    return StrUtil.format("Hello, {}!", who);
  }

当访问http://localhost:8080/demo/hello-required?who=时,我们的程序是报错的,因为who字段不能为空

自定义message

如果你仔细阅读了NotEmpty注解的文档,你会发现它允许自定义报错提示信息。因此,我们可以尝试自定义提示信息,以满足我们的需求。

java 复制代码
@GetMapping("/hello-required-message")
public String sayHelloByRequiredWithMessage(@RequestParam @NotEmpty(message = "who must be not null") String who) {
  return StrUtil.format("Hello, {}!", who);
}

我再来为你演示一下,除了NotEmpty注解,还有许多其他已经内置的注解可供使用。我就不一一列举了,你可以自行探索更多的注解。

实体类增加参数校验

除了上面提到的简单校验方式,我们在实际开发中更常使用的是通过JSON传递参数,并使用实体类接收参数。下面是相关代码示例:

java 复制代码
@PostMapping("/hello-required-message-object")
public String sayHelloByRequiredWithMessageForObject(@RequestBody @Valid MyTest test) {
  return StrUtil.format("Hello, {}!", test);
}
java 复制代码
public class MyTest {

  @NotEmpty(message = "NotEmpty.message")
  private String test;

  public String getTest() {
    return test;
  }

  public void setTest(String test) {
    this.test = test;
  }
}

加长优化语句:为了方便演示,我特意更换了一些提示信息,以便更清晰地展示给大家。接下来,我们再仔细观察一下。

自定义异常

其实在工作中,校验参数是一个常见的任务。我们通常会使用代码进行各种校验,因为我们对参数的要求远远超出了简单的非空检查。除了非空检查,我们还需要进行其他等值校验等。因此,如果在代码中进行校验,我们可以选择抛出异常或直接返回错误信息。举个例子,我可以使用自定义异常来说明这一点:

java 复制代码
@PostMapping("/hello-required-message-i18n")
public String sayHelloByRequiredWithMessageFori18n() {
  throw new CustomException("notBlank.message");
}

代码经过优化,已经去除了多余的逻辑,直接通过抛出异常来处理。让我来演示一下。

虽然在出错时会有报错信息返回,但是这些错误信息并不完全符合业务返回数据的格式。为了解决这个问题,我们需要定义一个全局异常处理类来统一处理异常情况。

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {

  @Autowired
  private MessageSource messageSource;
  
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<ErrorResponse> handleCustomException(CustomException ex, Locale locale) {
      String errorMessage = messageSource.getMessage(ex.getMessage(), null, locale);
      ErrorResponse errorResponse = new ErrorResponse(500, errorMessage);
      return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

ErrorResponse是我单独定义的错误描述类,我就不写了,因为每个项目都会有自己的返回类。

国际化信息

让我们进一步探讨一下关于国际化信息的定制。首先,为了实现这一目标,你需要在应用程序中进行相关的配置,具体来说,就是在application.yaml文件中进行必要的设置。

java 复制代码
server:
  port: 8080
  servlet:
    context-path: /demo
spring:
  messages:
    baseName: i18n/messages
    encoding: UTF-8
  mvc:
    locale: zh_CN

请注意,baseName在这里表示路径,而最后的messages并不代表包的含义,而是文件的前缀。如果无法找到messages_zh_CN文件,则会尝试查找messages_zh文件,如果还找不到,则会查找messages文件。因此,请不要再创建一个名为messages的包。

当所有准备工作完成后,我们可以检查演示结果是否已经改变了字段信息。

自定义注解

现在基本上已经存在了全局异常处理机制,不过我们还需要进一步优化。现在让我们来讨论一下自定义注解的使用。首先,我们注意到错误信息无法给出具体的字段值。显然,我们不能每次都手动写上每个字段的名称并提示不能为空。为了解决这个问题,我们需要自定义一个注解来实现自动化的校验。

java 复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmptyField {
    String message() default "";
}

有了注解后,下一步就是通过切面来获取并处理这个注解。接下来,我们来编写相应的代码来实现这个功能:

java 复制代码
@Aspect
@Component
public class NotEmptyFieldAspect {
  @Autowired
  private MessageSource messageSource;

    @Around("within(com.xkcoding..*)")
    public Object validateNotEmptyField(ProceedingJoinPoint joinPoint) throws Throwable {
      // 获取目标方法的参数列表
      Object[] args = joinPoint.getArgs();

      // 遍历参数列表
      for (Object arg : args) {
        // 获取参数实体类的属性
        Field[] fields = arg.getClass().getDeclaredFields();

        // 遍历属性列表
        for (Field field : fields) {
          // 判断属性是否被 @NotEmptyField 注解修饰
          if (field.isAnnotationPresent(NotEmptyField.class)) {
            // 获取注解信息
            NotEmptyField annotation = field.getAnnotation(NotEmptyField.class);
            field.setAccessible(true);
            // TODO: 进一步处理逻辑
            if (ObjectUtil.isEmpty(field.get(arg))) {
              String message = messageSource.getMessage(annotation.message(), null, Locale.getDefault());
              throw new CustomException(field.getName() + message);
            }
            // 输出属性和注解信息
            System.out.println("属性:" + field.getName());
            System.out.println("注解信息:" + annotation.message());
            System.out.println("值:" + field.get(arg));
          }
        }
      }

      // 调用目标方法
      Object result = joinPoint.proceed();

      return result;
    }
}

我只是简单地写了一下实现逻辑,并没有进行优化操作,所以以上代码仅供参考。现在让我们来看一下外层代码:

java 复制代码
  @PostMapping("/hello-required-message-i18n-object")
  public String sayHelloByRequiredWithMessageFori18nObject(@RequestBody @Valid MyTestForAspect myTestForAspect) {
    return "";
  }

当我们完成所有的准备工作之后,让我们来观察一下演示情况:

总结

在我们的工作中,参数校验是一项不可或缺的重要任务。因此,本文只是初步探讨了可以进行优化的方面,而并未详细阐述如何完美地进行优化。如果我遗漏了一些解决方案,也欢迎大家提供宝贵的建议和提醒。我的目的只是提供一些思路和引发讨论,以期能够共同进步。

相关推荐
普通网友8 分钟前
KUD#73019
java·php·程序优化
IT_陈寒11 分钟前
Redis 性能翻倍的 5 个隐藏技巧,99% 的开发者都不知道第3点!
前端·人工智能·后端
JaguarJack12 分钟前
PHP 桌面端框架NativePHP for Desktop v2 发布!
后端·php·laravel
番茄Salad12 分钟前
自定义Spring Boot Starter项目并且在其他项目中通过pom引入使用
java·spring boot
程序员三明治24 分钟前
详解Redis锁误删、原子性难题及Redisson加锁底层原理、WatchDog续约机制
java·数据库·redis·分布式锁·redisson·watchdog·看门狗
自由的疯34 分钟前
Java 怎么学习Kubernetes
java·后端·架构
自由的疯34 分钟前
Java kubernetes
java·后端·架构
普通网友1 小时前
IZT#73193
java·php·程序优化
rechol1 小时前
C++ 继承笔记
java·c++·笔记
Han.miracle5 小时前
数据结构——二叉树的从前序与中序遍历序列构造二叉树
java·数据结构·学习·算法·leetcode