জেএসআর 303 বৈধকরণ, যদি একটি ক্ষেত্র "কিছু" এর সমান হয়, তবে এই অন্যান্য ক্ষেত্রগুলি শূন্য হওয়া উচিত নয়


92

আমি জেএসআর -303 এর সাথে কিছুটা কাস্টম বৈধতা যাচ্ছি javax.validation

আমার একটা মাঠ আছে এবং যদি এই ক্ষেত্রের মধ্যে একটি নির্দিষ্ট মান প্রবেশ করা হয় তবে আমি এটি চাই যে কয়েকটি অন্যান্য ক্ষেত্রটি নয় null

আমি এটি বের করার চেষ্টা করছি। ব্যাখ্যা খুঁজে পেতে সহায়তা করার জন্য আমি ঠিক এটি কী বলব তা নিশ্চিত নয়।

কোন সাহায্য প্রশংসা করা হবে। আমি এ থেকে বেশ নতুন।

এই মুহুর্তে আমি একটি কাস্টম সীমাবদ্ধতা ভাবছি। তবে আমি কী জানি টীকাটির মধ্যে থেকে নির্ভরশীল ক্ষেত্রের মান কীভাবে পরীক্ষা করা যায়। মূলত আমি টিকা থেকে প্যানেল অবজেক্টটি কীভাবে অ্যাক্সেস করব তা নিশ্চিত নই।

public class StatusValidator implements ConstraintValidator<NotNull, String> {

    @Override
    public void initialize(NotNull constraintAnnotation) {}

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if ("Canceled".equals(panel.status.getValue())) {
            if (value != null) {
                return true;
            }
        } else {
            return false;
        }
    }
}

এটি panel.status.getValue();আমাকে কষ্ট দিচ্ছে .. কীভাবে এটি সম্পাদন করা যায় তা নিশ্চিত নয়।

উত্তর:


107

এই ক্ষেত্রে আমি একটি কাস্টম ভ্যালিডিটার লিখতে পরামর্শ দিচ্ছি, যা শ্রেণি পর্যায়ে যাচাই হবে (আমাদের বস্তুর ক্ষেত্রগুলিতে অ্যাক্সেস পেতে দিতে) যে অন্য ক্ষেত্রের নির্দিষ্ট মান থাকলে কেবল একটি ক্ষেত্রের প্রয়োজন। নোট করুন যে আপনার জেনেরিক ভ্যালিডেটার লিখতে হবে যা 2 টি ফিল্ডের নাম পায় এবং কেবল এই 2 টি ক্ষেত্রের সাথে কাজ করে। একাধিক ক্ষেত্রের জন্য আপনার প্রতিটি ক্ষেত্রের জন্য এই বৈধকারীটি যুক্ত করা উচিত।

নিম্নলিখিত কোডটি ধারণা হিসাবে ব্যবহার করুন (আমি এটি পরীক্ষা করে দেখিনি)।

  • ভ্যালিডেটর ইন্টারফেস

    /**
     * Validates that field {@code dependFieldName} is not null if
     * field {@code fieldName} has value {@code fieldValue}.
     **/
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Repeatable(NotNullIfAnotherFieldHasValue.List.class) // only with hibernate-validator >= 6.x
    @Constraint(validatedBy = NotNullIfAnotherFieldHasValueValidator.class)
    @Documented
    public @interface NotNullIfAnotherFieldHasValue {
    
        String fieldName();
        String fieldValue();
        String dependFieldName();
    
        String message() default "{NotNullIfAnotherFieldHasValue.message}";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    
        @Target({TYPE, ANNOTATION_TYPE})
        @Retention(RUNTIME)
        @Documented
        @interface List {
            NotNullIfAnotherFieldHasValue[] value();
        }
    
    }
    
  • বৈধকরণকারী বাস্তবায়ন

    /**
     * Implementation of {@link NotNullIfAnotherFieldHasValue} validator.
     **/
    public class NotNullIfAnotherFieldHasValueValidator
        implements ConstraintValidator<NotNullIfAnotherFieldHasValue, Object> {
    
        private String fieldName;
        private String expectedFieldValue;
        private String dependFieldName;
    
        @Override
        public void initialize(NotNullIfAnotherFieldHasValue annotation) {
            fieldName          = annotation.fieldName();
            expectedFieldValue = annotation.fieldValue();
            dependFieldName    = annotation.dependFieldName();
        }
    
        @Override
        public boolean isValid(Object value, ConstraintValidatorContext ctx) {
    
            if (value == null) {
                return true;
            }
    
            try {
                String fieldValue       = BeanUtils.getProperty(value, fieldName);
                String dependFieldValue = BeanUtils.getProperty(value, dependFieldName);
    
                if (expectedFieldValue.equals(fieldValue) && dependFieldValue == null) {
                    ctx.disableDefaultConstraintViolation();
                    ctx.buildConstraintViolationWithTemplate(ctx.getDefaultConstraintMessageTemplate())
                        .addNode(dependFieldName)
                        .addConstraintViolation();
                        return false;
                }
    
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
                throw new RuntimeException(ex);
            }
    
            return true;
        }
    
    }
    
  • বৈধকরণকারী ব্যবহারের উদাহরণ (জাভা 8+ সহ হাইবারনেট-ভ্যালিডেটর> = 6)

    @NotNullIfAnotherFieldHasValue(
        fieldName = "status",
        fieldValue = "Canceled",
        dependFieldName = "fieldOne")
    @NotNullIfAnotherFieldHasValue(
        fieldName = "status",
        fieldValue = "Canceled",
        dependFieldName = "fieldTwo")
    public class SampleBean {
        private String status;
        private String fieldOne;
        private String fieldTwo;
    
        // getters and setters omitted
    }
    
  • ভ্যালিডেটর ব্যবহারের উদাহরণ (হাইবারনেট-বৈধকরণকারী <6; পুরানো উদাহরণ)

    @NotNullIfAnotherFieldHasValue.List({
        @NotNullIfAnotherFieldHasValue(
            fieldName = "status",
            fieldValue = "Canceled",
            dependFieldName = "fieldOne"),
        @NotNullIfAnotherFieldHasValue(
            fieldName = "status",
            fieldValue = "Canceled",
            dependFieldName = "fieldTwo")
    })
    public class SampleBean {
        private String status;
        private String fieldOne;
        private String fieldTwo;
    
        // getters and setters omitted
    }
    

নোট করুন যে বৈধকরণকারী বাস্তবায়ন লাইব্রেরি BeanUtilsথেকে ক্লাস ব্যবহার করে commons-beanutilsতবে আপনি BeanWrapperImplস্প্রিং ফ্রেমওয়ার্ক থেকেও ব্যবহার করতে পারেন ।

এই দুর্দান্ত উত্তরটিও দেখুন: হাইবারনেট ভ্যালিডেটর (জেএসআর 303) এর সাথে ক্রস ফিল্ডের বৈধতা


4
@ বেনডিক্টাস এই উদাহরণটি কেবল স্ট্রিংয়ের সাথে কাজ করবে তবে আপনি কোনও বস্তুর সাথে কাজ করতে এটি পরিবর্তন করতে পারেন। এখানে 2 টি উপায় রয়েছে: 1) ক্লাসের সাথে প্যারামিট্রাইজ বৈধতা যা আপনি বৈধ করতে চান (পরিবর্তে Object)। এই ক্ষেত্রে, আপনাকে মানগুলি পাওয়ার জন্য প্রতিচ্ছবি ব্যবহার করার প্রয়োজন নেই তবে এই ক্ষেত্রে শংসাপত্রটি BeanWrapperImpস্প্রিং ফ্রেমওয়ার্ক (বা অন্যান্য গ্রন্থাগার) এবং এর getPropertyValue()পদ্ধতি থেকে ব্যবহার করুন less Objectএক্ষেত্রে আপনি যে কোনও ধরণের মান প্রয়োজন এবং আপনার যে কোনও প্রকারে কাস্ট করতে পারবেন ।
স্লাভা সেমুশিন

হ্যাঁ, তবে টীকাগুলির প্যারামিটার হিসাবে আপনার অবজেক্ট থাকতে পারে না, সুতরাং আপনার যাচাই করতে চান এমন প্রতিটি ধরণের জন্য আপনার একগুচ্ছ বিভিন্ন টীকাগুলির প্রয়োজন।
বেন

4
হ্যাঁ, আমি যখন "এই ক্ষেত্রে ভ্যালিডেটরটি কম জেনেরিক হয়ে উঠি" তখন আমি যা বলি তা বোঝায়।
স্লাভা সেমুশিন

আমি এই কৌশলটি প্রোটো বুফার ক্লাসগুলির জন্য ব্যবহার করতে চাই। এটি খুব সহায়ক (:
Saeed

সুন্দর সমাধান। কাস্টম টীকা তৈরিতে খুব সহায়ক!
বিশ্ব

128

এমন পদ্ধতিটি সংজ্ঞায়িত করুন যা অবশ্যই সত্যতে যাচাই করতে হবে এবং @AssertTrueএনেটেশনটিকে তার শীর্ষে রাখতে হবে:

  @AssertTrue
  private boolean isOk() {
    return someField != something || otherField != null;
  }

পদ্ধতিটি অবশ্যই 'ইজ' দিয়ে শুরু করা উচিত।


আমি আপনার পদ্ধতিটি ব্যবহার করেছি এবং এটি কাজ করে, তবে কীভাবে বার্তাটি পাবেন তা আমি বুঝতে পারি না। তুমি কি জানো?
আনাবাদ

12
এটি ছিল বিকল্পগুলির মধ্যে সবচেয়ে দক্ষ। ধন্যবাদ! @ আনাবাদ: অন্যান্য সীমাবদ্ধ টীকাগুলির মতোই অ্যাসেটট্রু টীকাটি একটি কাস্টম বার্তা নিতে পারে।
আর্নেস্ট_ কে

@ আর্নেস্টকুইলে উত্তর দেওয়ার জন্য ধন্যবাদ, তবে আমার সমস্যা বার্তাটি সেট করার সাথে নয় তবে এটি আমার জেএসপিতে পেয়েছে। আমি মডেল নিম্নলিখিত ফাংশন আছে: @AssertTrue(message="La reference doit etre un URL") public boolean isReferenceOk() { return origine!=Origine.Evolution||reference.contains("http://jira.bcaexpertise.org"); } এবং এটি আমার jsp মধ্যে: <th><form:label path="reference"><s:message code="reference"/></form:label></th><td><form:input path="reference" cssErrorClass="errorField"/><br/><form:errors path="isReferenceOk" cssClass="error"/></td> কিন্তু এটি একটি ত্রুটি নিক্ষেপ।
আনাবাদ

@ আর্নেস্টকিওয়েলে কিছু মনে করবেন না আমি এটি বের করে এনেছি, সেট্রেফারেন্স () বলা হওয়ার পরে আমি একটি বুলিয়ান অ্যাট্রিবিউট তৈরি করেছিলাম।
আনাবাদ

4
আমি পদ্ধতি সার্বজনিক করতে ছিল
ত্বীবী

22

আপনার কাস্টম ব্যবহার করা উচিত DefaultGroupSequenceProvider<T>:

শর্তসাপেক্ষ

// Marker interface
public interface ConditionalValidation {}

মাই কাস্টমফর্মসিকোয়েন্সপ্রভাইডার.জভা

public class MyCustomFormSequenceProvider
    implements DefaultGroupSequenceProvider<MyCustomForm> {

    @Override
    public List<Class<?>> getValidationGroups(MyCustomForm myCustomForm) {

        List<Class<?>> sequence = new ArrayList<>();

        // Apply all validation rules from ConditionalValidation group
        // only if someField has given value
        if ("some value".equals(myCustomForm.getSomeField())) {
            sequence.add(ConditionalValidation.class);
        }

        // Apply all validation rules from default group
        sequence.add(MyCustomForm.class);

        return sequence;
    }
}

মাই কাস্টমফর্ম.জভা

@GroupSequenceProvider(MyCustomFormSequenceProvider.class)
public class MyCustomForm {

    private String someField;

    @NotEmpty(groups = ConditionalValidation.class)
    private String fieldTwo;

    @NotEmpty(groups = ConditionalValidation.class)
    private String fieldThree;

    @NotEmpty
    private String fieldAlwaysValidated;


    // getters, setters omitted
}

এই বিষয়ে সম্পর্কিত প্রশ্নটি দেখুন ।


এটি করার আকর্ষণীয় উপায়। উত্তরটি এটি কীভাবে কাজ করে তার আরও ব্যাখ্যা দিয়ে করতে পারে, যদিও কী ঘটছে তা দেখার আগে আমাকে দুবার পড়তে হয়েছিল ...
জুলস

হাই, আমি আপনার সমাধানটি কার্যকর করেছি তবে একটি সমস্যার মুখোমুখি হয়েছি। getValidationGroups(MyCustomForm myCustomForm)পদ্ধতিতে কোনও বস্তু পাস করা হচ্ছে না । আপনি কি এখানে সম্ভবত সাহায্য করতে পারেন? : Stackoverflow.com/questions/44520306/...
user238607

4
@ ব্যবহারকারী 238607 getValidationGroups (মাই কাস্টমফর্ম মাই কাস্টমফর্ম) প্রতি মটর দৃষ্টান্তে অনেক সময় কল করবে এবং এটি কিছুটা সময় অকার্যকর হবে। এটি বাতিল হয়ে গেলে আপনি কেবল তা উপেক্ষা করবেন না।
pramoth

9

এটি আমার গ্রহণ এখানে, যতটা সম্ভব সহজ রাখার চেষ্টা করেছি।

ইন্টারফেস:

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = OneOfValidator.class)
@Documented
public @interface OneOf {

    String message() default "{one.of.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String[] value();
}

বৈধকরণ বাস্তবায়ন:

public class OneOfValidator implements ConstraintValidator<OneOf, Object> {

    private String[] fields;

    @Override
    public void initialize(OneOf annotation) {
        this.fields = annotation.value();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {

        BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);

        int matches = countNumberOfMatches(wrapper);

        if (matches > 1) {
            setValidationErrorMessage(context, "one.of.too.many.matches.message");
            return false;
        } else if (matches == 0) {
            setValidationErrorMessage(context, "one.of.no.matches.message");
            return false;
        }

        return true;
    }

    private int countNumberOfMatches(BeanWrapper wrapper) {
        int matches = 0;
        for (String field : fields) {
            Object value = wrapper.getPropertyValue(field);
            boolean isPresent = detectOptionalValue(value);

            if (value != null && isPresent) {
                matches++;
            }
        }
        return matches;
    }

    private boolean detectOptionalValue(Object value) {
        if (value instanceof Optional) {
            return ((Optional) value).isPresent();
        }
        return true;
    }

    private void setValidationErrorMessage(ConstraintValidatorContext context, String template) {
        context.disableDefaultConstraintViolation();
        context
            .buildConstraintViolationWithTemplate("{" + template + "}")
            .addConstraintViolation();
    }

}

ব্যবহার:

@OneOf({"stateType", "modeType"})
public class OneOfValidatorTestClass {

    private StateType stateType;

    private ModeType modeType;

}

বার্তা:

one.of.too.many.matches.message=Only one of the following fields can be specified: {value}
one.of.no.matches.message=Exactly one of the following fields must be specified: {value}

3

একটি ভিন্ন পদ্ধতি হ'ল একটি (সুরক্ষিত) গিটার তৈরি করা যা সমস্ত নির্ভর ক্ষেত্র যুক্ত কোনও বস্তু ফেরত দেয়। উদাহরণ:

public class MyBean {
  protected String status;
  protected String name;

  @StatusAndSomethingValidator
  protected StatusAndSomething getStatusAndName() {
    return new StatusAndSomething(status,name);
  }
}

স্ট্যাটাসএন্ডসোমথিংভিডেটর এখন স্ট্যাটাসএন্ডসোমথিং স্ট্যাটাস এবং স্ট্যাটাসঅ্যান্ডসোমথিংস.সামথিং অ্যাক্সেস করতে পারে এবং একটি নির্ভরশীল চেক করতে পারে।


0

নীচে নমুনা:

package io.quee.sample.javax;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validator;
import javax.validation.constraints.Pattern;
import java.util.Set;

/**
 * Created By [**Ibrahim Al-Tamimi **](https://www.linkedin.com/in/iloom/)
 * Created At **Wednesday **23**, September 2020**
 */
@SpringBootApplication
public class SampleJavaXValidation implements CommandLineRunner {
    private final Validator validator;

    public SampleJavaXValidation(Validator validator) {
        this.validator = validator;
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleJavaXValidation.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        Set<ConstraintViolation<SampleDataCls>> validate = validator.validate(new SampleDataCls(SampleTypes.TYPE_A, null, null));
        System.out.println(validate);
    }

    public enum SampleTypes {
        TYPE_A,
        TYPE_B;
    }

    @Valid
    public static class SampleDataCls {
        private final SampleTypes type;
        private final String valueA;
        private final String valueB;

        public SampleDataCls(SampleTypes type, String valueA, String valueB) {
            this.type = type;
            this.valueA = valueA;
            this.valueB = valueB;
        }

        public SampleTypes getType() {
            return type;
        }

        public String getValueA() {
            return valueA;
        }

        public String getValueB() {
            return valueB;
        }

        @Pattern(regexp = "TRUE")
        public String getConditionalValueA() {
            if (type.equals(SampleTypes.TYPE_A)) {
                return valueA != null ? "TRUE" : "";
            }
            return "TRUE";
        }

        @Pattern(regexp = "TRUE")
        public String getConditionalValueB() {
            if (type.equals(SampleTypes.TYPE_B)) {
                return valueB != null ? "TRUE" : "";
            }
            return "TRUE";
        }
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.