如何使ASP.NET Core的[Required]注解可选?自定义验证注解资源求助
Hey there! Let me walk you through how to tackle custom validation annotations in Java—both extending existing ones and building new from scratch, plus that specific optional @Required you need. I’ve handled similar scenarios before, so let’s break it down step by step.
You don’t need external links to get started—focus on these foundational ideas from the Bean Validation 2.0 (JSR-380) spec and Spring Validation (if you’re using Spring):
@Constraintmeta-annotation: Marks your custom annotation as a validation constraint, and links it to its validator class.ConstraintValidatorinterface: The workhorse where you write your validation logic. It has two key methods:initialize()(to grab annotation parameters) andisValid()(the actual validation check).- Annotation attributes: Every custom constraint needs
message,groups, andpayload(required by Bean Validation) plus any custom parameters you want to add.
Java annotations can’t inherit directly, so the best way to extend an existing one (like @NotNull or @NotBlank) is to wrap its logic and add your custom parameters. Here’s how to do it:
Example: Extend @NotBlank with an Enabled Toggle
// Custom extended annotation import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = OptionalNotBlankValidator.class) public @interface OptionalNotBlank { String message() default "Field cannot be blank"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; // Your custom toggle parameter boolean enabled() default true; } // Validator that reuses the original @NotBlank logic import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import org.springframework.validation.beanvalidation.SpringValidatorAdapter; import javax.validation.constraints.NotBlank; public class OptionalNotBlankValidator implements ConstraintValidator<OptionalNotBlank, String> { private boolean enabled; private final NotBlankValidator delegate = new NotBlankValidator(); @Override public void initialize(OptionalNotBlank constraintAnnotation) { this.enabled = constraintAnnotation.enabled(); // Pass annotation attributes to the delegate validator delegate.initialize(new NotBlank() { @Override public Class<? extends Annotation> annotationType() { return NotBlank.class; } @Override public String message() { return constraintAnnotation.message(); } @Override public Class<?>[] groups() { return constraintAnnotation.groups(); } @Override public Class<? extends Payload>[] payload() { return constraintAnnotation.payload(); } }); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (!enabled) return true; // Skip validation if disabled return delegate.isValid(value, context); // Reuse original @NotBlank logic } }
For entirely new validation logic (like checking phone number format or password complexity), follow these three steps:
Step 1: Define the Annotation
import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Target({ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = ValidPhoneNumberValidator.class) public @interface ValidPhoneNumber { String message() default "Invalid phone number format"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; // Custom parameter: allowed country codes String[] allowedCountryCodes() default {"+86"}; }
Step 2: Implement the Validator
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.Arrays; import java.util.regex.Pattern; public class ValidPhoneNumberValidator implements ConstraintValidator<ValidPhoneNumber, String> { private String[] allowedCodes; private final Pattern phonePattern = Pattern.compile("^\\+\\d{1,3}-?\\d{10,14}$"); @Override public void initialize(ValidPhoneNumber constraintAnnotation) { this.allowedCodes = constraintAnnotation.allowedCountryCodes(); } @Override public boolean isValid(String phoneNumber, ConstraintValidatorContext context) { if (phoneNumber == null) return false; // Check if the number starts with an allowed country code boolean codeValid = Arrays.stream(allowedCodes).anyMatch(phoneNumber::startsWith); // Check if the format matches the regex boolean formatValid = phonePattern.matcher(phoneNumber).matches(); return codeValid && formatValid; } }
Step 3: Use It in Your Code
public class UserProfile { @ValidPhoneNumber(allowedCountryCodes = {"+86", "+1"}, message = "Phone number must be a valid US or Chinese number") private String phoneNumber; }
@Required Annotation Let’s build the toggleable @Required you need—one that can be turned on/off with a boolean parameter. This example assumes your original @Required checks for non-null and non-empty values:
Step 1: Create the Extended Annotation
import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = OptionalRequiredValidator.class) public @interface OptionalRequired { String message() default "This field is required"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; // Toggle parameter: default to enabled boolean enabled() default true; }
Step 2: Implement the Validator
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class OptionalRequiredValidator implements ConstraintValidator<OptionalRequired, Object> { private boolean isEnabled; @Override public void initialize(OptionalRequired constraintAnnotation) { this.isEnabled = constraintAnnotation.enabled(); } @Override public boolean isValid(Object value, ConstraintValidatorContext context) { // Skip validation entirely if disabled if (!isEnabled) { return true; } // Original @Required logic: adjust based on your needs if (value == null) { return false; } if (value instanceof String) { return !((String) value).trim().isEmpty(); } // For non-string types, just check non-null return true; } }
Step 3: Use It in Your Code
public class OrderForm { // Required by default @OptionalRequired(message = "Order ID cannot be empty") private String orderId; // Only required when the order is a gift @OptionalRequired(enabled = false, message = "Gift message is required for gift orders") private String giftMessage; }
Pro Tip
If you’re using Spring, you can combine this with validation groups to enable/disable constraints across multiple fields for different scenarios (like create vs update operations), but the boolean parameter gives you granular control per field.
内容的提问来源于stack exchange,提问作者user13735770

