这是一个对您有用的社区
一对一的交流/采访手册/简历优化/求职和回答问题,欢迎加入“”知识星球。以下是地球提供的一些信息:
这是一个可能对您有用的开源项目
国内恒星拥有一个10W +开源项目,包括管理后端 +微信小程序,而后端则支持单体和微服务体系结构。
功能涵盖RBAC权限,SaaS多租户,数据权限,购物中心,付款,工作流程,大屏幕报告,微信公共帐户,CRM和其他功能:
[中国的第一批批次]支持JDK 21+ 3.2.2,JDK 8+靴子2.7.18双版本
参数验证非常重要,并且是开发过程中必不可少的部分。但是,我认为许多后端同事会很懒惰,最好做好,这可能会对系统的稳定性和安全造成严重伤害。那么如何在引导应用程序中的参数验证方面做得好?本文提供了10个提示。你知道多少?
后端管理系统 +基于启动 + plus + vue&支持RBAC动态权限,多租户,数据权限,工作流程,三方登录,付款,SMS,购物中心和其他功能的后端管理系统 +用户
BOOD提供内置验证注释,可以帮助简单,快速地验证输入字段,例如检查空或空字段,使用正则表达式验证模式以及验证电子邮件地址。
一些最常用的验证注释包括:
对于特定用法,请参阅以下示例:
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 List hobbies;
@Pattern(regexp = "[A-Z]{2}\d{4}")
private String employeeId;
后端管理系统 +基于 + + + + vue&支持RBAC动态权限,多租户,数据权限,工作流程,三方登录,付款,SMS,购物中心和其他功能的后端管理系统 +用户
尽管Boot的内置验证注释很有用,但可能无法涵盖所有情况。如果有特殊的参数验证方案,则可以使用JSR 303验证框架创建自定义验证注释。自定义注释可以使您的验证逻辑更加重复和可维护。
假设我们有一个应用程序,用户可以创建帖子。每个帖子都应具有标题和一个主体,并且标题在所有帖子中都应唯一。尽管Boot提供了内置验证注释,用于检查字段是否为空,但它没有提供内置的验证注释来检查唯一性。在这种情况下,我们可以创建一个自定义验证注释来处理这种情况。
首先,我们创建自定义约束注释:
@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 {};
}
接下来,我们创建一个接口,旨在从数据库中检索帖子:
public interface PostRepository extends JpaRepository<Post, Long> {
Post findByTitle(String title);
}
然后,我们需要按以下方式定义验证器类:
@Component
public class UniqueTitleValidator implements ConstraintValidator<UniqueTitle, String> {
@Autowired
private PostRepository postRepository;
@Override
public boolean isValid(String title, ConstraintValidatorContext context) {
if (title == null) {
return true;
}
return Objects.isNull(postRepository.findByTitle(title));
}
}
实现了具有两种通用类型的接口:第一个是自定义注释,第二个是要验证的字段类型(在这种情况下)。我们还自动组装了类以从数据库中检索帖子。
()方法检查它是零还是通过查询是唯一的。如果无效或唯一,则验证是成功的,并返回真实。
定义自定义验证注释和验证器类后,我们现在可以使用它来验证启动应用程序中的帖子标题:
public class Post {
@UniqueTitle
private String title;
@NotNull
private String body;
}
我们已将 @注释应用于邮政类中的变量。验证此字段时,这会触发类中定义的验证逻辑。
除了前端或客户端的验证事故外,验证在服务器端的输入至关重要。它确保在处理或存储任何恶意数据之前捕获任何恶意或畸形的数据,这对于应用程序的安全性和稳定性至关重要。
假设我们有一个休息端点,允许用户创建新帐户。端点需要一个包含用户用户名和密码的JSON请求主体。为了确保输入有效,我们可以创建DTO(数据传输对象)类,并将验证注释应用于其字段:
public class UserDTO {
@NotBlank
private String username;
@NotBlank
private String password;
}
我们使用 @注释来确保和字段不是空的或空的。
接下来,我们可以创建一个控制器方法来处理HTTP POST请求并在创建新用户之前验证输入:
@RestController
@RequestMapping("/users")
@Validated
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity createUser(@Valid @RequestBody UserDTO userDto) {
userService.createUser(userDto);
return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
}
}
我们使用 @注释来启用方法级验证,并且还将 @注释应用于参数以触发验证过程。
当验证失败时,必须提供清晰简洁的错误消息,以描述错误以及如何修复它。
如果我们有一个允许用户创建新用户的API,则是一个示例。我们要确保名称和电子邮件地址字段不是空的,年龄在18至99岁之间,除了这些字段外,如果用户试图创建具有重复的“用户名”的帐户,我们还提供明确的错误消息或“电子邮件”。
为此,我们可以使用必要的验证注释来定义模型类用户,如下所示:
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;
}
接下来,在我们的控制器中,我们可以处理表单提交和使用 @ 验证用户输入:
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity createUser(@Valid @RequestBody User user, BindingResult result) {
if (result.hasErrors()) {
List errorMessages = result.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errorMessages.toString());
}
// save the user to the database using UserService
userService.saveUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
}
}
如果您的应用程序支持多种语言,则必须使用国际化(I18N)以用户的首选语言显示错误消息。
这是使用I18N在引导应用程序中处理错误消息的示例
1。首先,创建一个在资源目录中包含默认错误消息的文件。
# messages.properties
user.name.required=Name is required.
user.email.invalid=Invalid email format.
user.age.invalid=Age must be a number between 18 and 99.
2。接下来,创建一个。为每种受支持的语言申请,例如中文。
user.name.required=名称不能为空.
user.email.invalid=无效的email格式.
user.age.invalid=年龄必须在18到99岁之间.
3。然后,更新您的验证注释以使用局部错误消息
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;
}
4。最后,在配置文件中配置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;
}
}
5。现在,当发生验证错误时,根据请求发送的“ - ”标头,将以用户的首选语言显示错误消息。
验证组是引导验证框架的强大功能,可让您根据其他输入值或应用程序状态应用条件验证规则。
现在有一个带有三个字段的用户类:和。我们要确保如果字段为空,则或字段必须是非空的。否则,应正常验证所有三个字段。
为此,我们将定义两个验证组:和。当字段不为空时,该组将包含验证规则,而组将包含所有三个字段的正常验证规则。
1。创建一个用验证组创建用户类
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 {}
}
2。接下来,我们使用这些验证组更新
@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
}
}
3。进行这些更改后,现在根据电子邮件字段是否为空的用户类验证。如果为空,则或字段必须是非空的。否则,所有三个字段将正常验证。
如果您需要验证多个字段的复杂输入规则,则可以使用跨场验证来保持验证逻辑组织和可维护。跨场验证可确保所有输入值均有效且彼此一致,从而防止意外行为。
假设我们有一个表格,用户可以输入任务的开始日期和结束日期,并且我们希望确保结束日期不早于开始日期。我们可以使用跨域验证来做到这一点。
1。首先,我们定义自定义验证注释e:
@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 {};
}
2。然后,我们创建一个验证器:
public class EndDateAfterStartDateValidator implements ConstraintValidator<EndDateAfterStartDate, TaskForm> {
@Override
public boolean isValid(TaskForm taskForm, ConstraintValidatorContext context) {
if (taskForm.getStartDate() == null || taskForm.getEndDate() == null) {
return true;
}
return taskForm.getEndDate().isAfter(taskForm.getStartDate());
}
}
3。最后,我们将E注释应用于我们的形式对象:
@EndDateAfterStartDate
public class TaskForm {
@NotNull
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;
@NotNull
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;
}
现在,当用户提交表单时,验证框架将自动检查结束日期是否比开始日期晚,如果不是开始日期,则提供有意义的错误消息。
例外处理可用于统一捕获和处理验证错误。
这是如何使用启动中的异常处理来处理验证错误的示例:
@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status,
WebRequest request) {
Map body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", status.value());
// Get all errors
List errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toList());
body.put("errors", errors);
return new ResponseEntity<>(body, headers, status);
}
}
在这里,我们创建一个带有 @的课程,以处理REST API抛出的异常。然后,我们使用 @注释创建一种方法,以处理验证失败时处理抛出的方法。
在处理程序方法中,我们创建一个地图对象,以保存错误响应的详细信息,包括时间戳,HTTP状态代码和错误消息列表。我们使用对象的()方法来获取所有验证错误,并将它们添加到错误消息列表中。
最后,我们返回一个包含错误响应详细信息的对象,包括错误消息列表为响应主体,HTTP标头和HTTP状态代码。
除了此例外处理代码外,我们的REST API列出的任何验证错误都将被捕获并以结构化且有意义的格式返回给用户,从而更容易理解和解决问题。
需要为您的验证逻辑编写单元测试,以帮助确保其正常工作。
@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");
Set> violations = validator.validate(user);
assertEquals(1, violations.size());
assertEquals("must be a well-formed email address", violations.iterator().next().getMessage());
}
}
我们使用5编写测试,以使用无效的电子邮件地址验证“用户”对象。然后,我们使用接口验证用户对象,并检查是否返回了预期的验证错误。
客户端身份验证可以通过向用户提供即时反馈并减少对服务器的请求数来改善用户体验。但是,不应将其作为验证输入的唯一方法。客户验证很容易绕过或操纵,因此必须在服务器端验证输入,以确保安全性和数据完整性。
有效验证对于任何Web应用程序的稳定性和安全性至关重要。 Boot提供了一套工具和库,以简化验证逻辑并使其更容易维护。通过遵循本文讨论的最佳实践,您可以确保验证组件有效并提供出色的用户体验。
欢迎加入我的知识星球,以全面提高我的技术能力。
还要补充,“长按”或“扫描”以下QR码:
地球的内容包括:项目实践,面试招聘,源代码分析和学习路线。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)