网站开发设计作业及代码,wordpress模板路径,世赛网站开发与设计,网站建设sem账户搭建这里写目录标题 前言1.使用验证注解2 使用自定义验证注解3 在服务器端验证4 提供有意义的错误信息5 将 i18n 用于错误消息messages.properties6 使用分组验证7 对复杂逻辑使用跨域验证8 对验证错误使用异常处理9 测试你的验证逻辑10 考虑客户端验证总结 前言 参数验证很重要是平时开发环节中不可少的一部分但是我想很多后端同事会偷懒干脆不错这样很可能给系统的稳定性和安全性带来严重的危害。那么在Spring Boot应用中如何做好参数校验工作呢本文提供了10个小技巧你知道几个呢 1.使用验证注解
Spring Boot提供了内置的验证注解可以帮助简单、快速地对输入字段进行验证例如检查 null 或空字段、强制执行长度限制、使用正则表达式验证模式以及验证电子邮件地址。
一些最常用的验证注释包括
NotNull指定字段不能为空。
NotEmpty指定列表字段不能为空。
NotBlank指定字符串字段不得为空或仅包含空格。
Min 和 Max指定数字字段的最小值和最大值。
Pattern指定字符串字段必须匹配的正则表达式模式。
Email指定字符串字段必须是有效的电子邮件地址。 具体用法参考下面例子public class User { NotNull private Long id;
NotBlank
Size(min 2, max 50)
private String firstName;NotBlank
Size(min 2, max 50)
private String lastName;Email
private String email;NotNull
Min(18)
Max(99)
private Integer age;NotEmpty
private ListString hobbies;Pattern(regexp [A-Z]{2}\d{4})
private String employeeId;2 使用自定义验证注解
虽然 Spring Boot 的内置验证注释很有用但它们可能无法涵盖所有情况。如果有特殊参数验证的场景可以使用 Spring 的 JSR 303 验证框架创建自定义验证注释。自定义注解可以让你的的验证逻辑更具可重用性和可维护性。
假设我们有一个应用程序用户可以在其中创建帖子。每个帖子都应该有一个标题和一个正文并且标题在所有帖子中应该是唯一的。虽然 Spring Boot 提供了用于检查字段是否为空的内置验证注释但它没有提供用于检查唯一性的内置验证注释。在这种情况下我们可以创建一个自定义验证注解来处理这种情况。
首先我们创建自定义约束注解UniqueTitle
Target({ElementType.FIELD}) Retention(RetentionPolicy.RUNTIME) Constraint(validatedBy UniqueTitleValidator.class) public interface UniqueTitle { String message() default “Title must be unique”;
Class?[] groups() default {};Class? extends Payload[] payload() default {};} 接下来我们创建一个PostRepository接口目的是从数据库中检索帖子
public interface PostRepository extends JpaRepositoryPost, Long { Post findByTitle(String title); } 然后我们需要定义验证器类 UniqueTitleValidator如下所示
Component public class UniqueTitleValidator implements ConstraintValidatorUniqueTitle, String {
Autowired
private PostRepository postRepository;Override
public boolean isValid(String title, ConstraintValidatorContext context) {if (title null) {return true;}return Objects.isNull(postRepository.findByTitle(title));
}} UniqueTitleValidator实现了ConstraintValidator接口它有两个泛型类型第一个是自定义注解UniqueTitle第二个是正在验证的字段类型在本例中为String). 我们还自动装配了PostRepository 类以从数据库中检索帖子。
isValid()方法通过查询 PostRepository 来检查 title 是否为 null 或者它是否是唯一的。如果 title 为 null 或唯一则验证成功并返回 true。
定义了自定义验证注释和验证器类后我们现在可以使用它来验证 Spring Boot 应用程序中的帖子标题
public class Post { UniqueTitle private String title;
NotNull
private String body;} 我们已将 UniqueTitle 注释应用于 Post 类中的 title 变量。验证此字段时这将触发 UniqueTitleValidator 类中定义的验证逻辑。
3 在服务器端验证
除了前端或者客户端做了验证意外服务器端验证输入是至关重要的。它可以确保在处理或存储任何恶意或格式错误的数据之前将其捕获这对于应用程序的安全性和稳定性至关重要。
假设我们有一个允许用户创建新帐户的 REST 端点。端点需要一个包含用户用户名和密码的 JSON 请求体。为确保输入有效我们可以创建一个 DTO数据传输对象类并将验证注释应用于其字段
public class UserDTO {
NotBlank
private String username;NotBlank
private String password;} 我们使用NotBlank注解来确保username和password字段不为空或 null。
接下来我们可以创建一个控制器方法来处理 HTTP POST 请求并在创建新用户之前验证输入
RestController RequestMapping(“/users”) Validated public class UserController { Autowired private UserService userService;
PostMapping
public ResponseEntityString createUser(Valid RequestBody UserDTO userDto) {userService.createUser(userDto);return ResponseEntity.status(HttpStatus.CREATED).body(User created successfully);
}} 我们使用 Spring 的Validated注解来启用方法级验证我们还将 Valid 注释应用于 userDto 参数以触发验证过程。
4 提供有意义的错误信息
当验证失败时必须提供清晰简洁的错误消息来描述出了什么问题以及如何修复它。
这是一个示例如果我们有一个允许用户创建新用户的 RESTful API。我们要确保姓名和电子邮件地址字段不为空年龄在 18 到 99 岁之间除了这些字段如果用户尝试使用重复的“用户名”创建帐户我们还会提供明确的错误消息或“电子邮件”。
为此我们可以定义一个带有必要验证注释的模型类 User如下所示
public class User {
NotBlank(message 用户名不能为空)
private String name;NotBlank(message Email不能为空)
Email(message 无效的Emaild地址)
private String email;NotNull(message 年龄不能为空)
Min(value 18, message 年龄必须大于18)
Max(value 99, message 年龄必须小于99)
private Integer age;} 我们使用 message属性为每个验证注释提供了自定义错误消息。
接下来在我们的 Spring 控制器中我们可以处理表单提交并使用 Valid 注释验证用户输入
RestController RequestMapping(“/users”) public class UserController { Autowired private UserService userService;
PostMapping
public ResponseEntityString createUser(Valid RequestBody User user, BindingResult result) {if (result.hasErrors()) {ListString errorMessages result.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.toList());return ResponseEntity.badRequest().body(errorMessages.toString());}// save the user to the database using UserServiceuserService.saveUser(user);return ResponseEntity.status(HttpStatus.CREATED).body(User created successfully);
}}
我们使用 Valid 注释来触发 User 对象的验证并使用 BindingResult 对象来捕获任何验证错误。
5 将 i18n 用于错误消息
如果你的应用程序支持多种语言则必须使用国际化 (i18n) 以用户首选语言显示错误消息。
以下是在 Spring Boot 应用程序中使用 i18n 处理错误消息的示例
首先在资源目录下创建一个包含默认错误消息的 messages.properties 文件
messages.properties
user.name.requiredName is required. user.email.invalidInvalid email format. user.age.invalidAge must be a number between 18 and 99. 接下来为每种支持的语言创建一个 messages_xx.properties 文件例如中文的 messages_zh_CN.properties。
user.name.required名称不能为空. user.email.invalid无效的email格式. user.age.invalid年龄必须在18到99岁之间. 然后更新您的验证注释以使用本地化的错误消息
public class User { NotNull(message “{user.id.required}”) private Long id;
NotBlank(message {user.name.required})
private String name;Email(message {user.email.invalid})
private String email;NotNull(message {user.age.required})
Min(value 18, message {user.age.invalid})
Max(value 99, message {user.age.invalid})
private Integer age;}
最后在 Spring 配置文件中配置 MessageSource bean 以加载 i18n 消息文件
Configuration public class AppConfig { Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource new ResourceBundleMessageSource(); messageSource.setBasename(“messages”); messageSource.setDefaultEncoding(“UTF-8”); return messageSource; }
Bean
public LocalValidatorFactoryBean validator() {LocalValidatorFactoryBean validatorFactoryBean new LocalValidatorFactoryBean();validatorFactoryBean.setValidationMessageSource(messageSource());return validatorFactoryBean;
}}
现在当发生验证错误时错误消息将根据随请求发送的“Accept-Language”标头以用户的首选语言显示。
6 使用分组验证
验证组是 Spring Boot 验证框架的一个强大功能允许您根据其他输入值或应用程序状态应用条件验证规则。
现在有一个包含三个字段的User类的情况下firstName、lastName和email。我们要确保如果 email 字段为空则 firstName 或 lastName 字段必须非空。否则所有三个字段都应该正常验证。
为此我们将定义两个验证组EmailNotEmpty 和 Default。EmailNotEmpty 组将包含当 email 字段不为空时的验证规则而 Default 组将包含所有三个字段的正常验证规则。
创建带有验证组的 User 类
public class User { NotBlank(groups Default.class) private String firstName;
NotBlank(groups Default.class)
private String lastName;Email(groups EmailNotEmpty.class)
private String email;// getters and setters omitted for brevity
public interface EmailNotEmpty {}
public interface Default {}} 请注意我们在User类中定义了两个接口EmailNotEmpty和 Default。这些将作为我们的验证组。
接下来我们更新Controller使用这些验证组
RestController RequestMapping(“/users”) Validated public class UserController { public ResponseEntity createUser( Validated({org.example.model.ex6.User.EmailNotEmpty.class}) RequestBody User userWithEmail, Validated({User.Default.class}) RequestBody User userWithoutEmail) { // Create the user and return a success response
}} 我们已将Validated注释添加到我们的控制器表明我们想要使用验证组。我们还更新了 createUser 方法将两个 User 对象作为输入一个在 email 字段不为空时使用另一个在它为空时使用。
Validated 注释用于指定将哪个验证组应用于每个 User 对象。对于 userWithEmail 参数我们指定了 EmailNotEmpty 组而对于 userWithoutEmail 参数我们指定了 Default 组。
进行这些更改后现在将根据“电子邮件”字段是否为空对“用户”类进行不同的验证。如果为空则 firstName 或 lastName 字段必须非空。否则所有三个字段都将正常验证。
7 对复杂逻辑使用跨域验证
如果需要验证跨多个字段的复杂输入规则可以使用跨字段验证来保持验证逻辑的组织性和可维护性。跨字段验证可确保所有输入值均有效且彼此一致从而防止出现意外行为。
假设我们有一个表单用户可以在其中输入任务的开始日期和结束日期并且我们希望确保结束日期不早于开始日期。我们可以使用跨域验证来实现这一点。
首先我们定义一个自定义验证注解EndDateAfterStartDate
Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Constraint(validatedBy EndDateAfterStartDateValidator.class) public interface EndDateAfterStartDate { String message() default “End date must be after start date”; Class?[] groups() default {}; Class? extends Payload[] payload() default {}; } 然后我们创建验证器EndDateAfterStartDateValidator
public class EndDateAfterStartDateValidator implements ConstraintValidatorEndDateAfterStartDate, TaskForm { Override public boolean isValid(TaskForm taskForm, ConstraintValidatorContext context) { if (taskForm.getStartDate() null || taskForm.getEndDate() null) { return true; } return taskForm.getEndDate().isAfter(taskForm.getStartDate());
}} 最后我们将EndDateAfterStartDate注释应用于我们的表单对象TaskForm
EndDateAfterStartDate public class TaskForm { NotNull DateTimeFormat(pattern “yyyy-MM-dd”) private LocalDate startDate;
NotNull
DateTimeFormat(pattern yyyy-MM-dd)
private LocalDate endDate;} 现在当用户提交表单时验证框架将自动检查结束日期是否晚于开始日期如果不是则提供有意义的错误消息。
8 对验证错误使用异常处理
可以使用异常处理ExceptionHandler来统一捕获和处理验证错误。
以下是如何在 Spring Boot 中使用异常处理来处理验证错误的示例
RestControllerAdvice public class RestExceptionHandler extends ResponseEntityExceptionHandler {
ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntityObject handleMethodArgumentNotValid(MethodArgumentNotValidException ex,HttpHeaders headers, HttpStatus status,WebRequest request) {MapString, Object body new LinkedHashMap();body.put(timestamp, LocalDateTime.now());body.put(status, status.value());// Get all errorsListString errors ex.getBindingResult().getFieldErrors().stream().map(x - x.getDefaultMessage()).collect(Collectors.toList());body.put(errors, errors);return new ResponseEntity(body, headers, status);
}} 在这里我们创建了一个用 RestControllerAdvice 注解的 RestExceptionHandler 类来处理我们的 REST API 抛出的异常。然后我们创建一个用ExceptionHandler注解的方法来处理在验证失败时抛出的 MethodArgumentNotValidException。在处理程序方法中我们创建了一个 Map 对象来保存错误响应的详细信息包括时间戳、HTTP 状态代码和错误消息列表。我们使用 MethodArgumentNotValidException 对象的 getBindingResult() 方法获取所有验证错误并将它们添加到错误消息列表中。最后我们返回一个包含错误响应详细信息的ResponseEntity对象包括作为响应主体的错误消息列表、HTTP 标头和 HTTP 状态代码。有了这个异常处理代码我们的 REST API 抛出的任何验证错误都将被捕获并以结构化和有意义的格式返回给用户从而更容易理解和解决问题。9 测试你的验证逻辑
需要为你的验证逻辑编写单元测试以帮助确保它正常工作。
DataJpaTest public class UserValidationTest {
Autowired
private TestEntityManager entityManager;Autowired
private Validator validator;Test
public void testValidation() {User user new User();user.setFirstName(John);user.setLastName(Doe);user.setEmail(invalid email);SetConstraintViolationUser violations validator.validate(user);assertEquals(1, violations.size());assertEquals(must be a well-formed email address, violations.iterator().next().getMessage());
}} 我们使用 JUnit 5 编写一个测试来验证具有无效电子邮件地址的“用户”对象。然后我们使用 Validator 接口来验证 User 对象并检查是否返回了预期的验证错误。10 考虑客户端验证
客户端验证可以通过向用户提供即时反馈并减少对服务器的请求数量来改善用户体验。但是不应依赖它作为验证输入的唯一方法。客户端验证很容易被绕过或操纵因此必须在服务器端验证输入以确保安全性和数据完整性。
总结
有效的验证对于任何 Web 应用程序的稳定性和安全性都是必不可少的。Spring Boot 提供了一套工具和库来简化验证逻辑并使其更易于维护。通过遵循本文中讨论的最佳实践您可以确保您的验证组件有效并提供出色的用户体验。