You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使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.

Core Concepts to Master First

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):

  • @Constraint meta-annotation: Marks your custom annotation as a validation constraint, and links it to its validator class.
  • ConstraintValidator interface: The workhorse where you write your validation logic. It has two key methods: initialize() (to grab annotation parameters) and isValid() (the actual validation check).
  • Annotation attributes: Every custom constraint needs message, groups, and payload (required by Bean Validation) plus any custom parameters you want to add.
1. Extending Existing Validation Annotations

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
    }
}
2. Building a Completely New Validation Annotation

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;
}
Your Specific Requirement: Optional @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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 15:02:58