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


235

হাইবারনেট ভ্যালিডেটর ৪.x এ কি ক্রস ফিল্ড বৈধতার (বা তৃতীয় পক্ষের প্রয়োগের) কোনও বাস্তবায়ন রয়েছে? যদি তা না হয় তবে ক্রস ফিল্ড বৈধকারক কার্যকর করার সবচেয়ে সহজ উপায় কী?

উদাহরণস্বরূপ, আপনি দুটি শিমের বৈশিষ্ট্য যাচাই করতে API কীভাবে ব্যবহার করতে পারেন তা সমান (যেমন একটি পাসওয়ার্ড ক্ষেত্র যাচাইকরণ পাসওয়ার্ড যাচাইয়ের ক্ষেত্রের সাথে মেলে)।

টীকাগুলিতে, আমি এমন কিছু আশা করব:

public class MyBean {
  @Size(min=6, max=50)
  private String pass;

  @Equals(property="pass")
  private String passVerify;
}

1
শ্রেণি স্তরের টাইপ-সেফ এবং রিফ্লেকশন এপিআই-মুক্ত (ইমো আরও মার্জিত) সমাধানের জন্য স্ট্যাকওভারফ্লো.com/ প্রশ্নগুলি / ২7878১177171/২ দেখুন ।
কার্ল রিখটার

উত্তর:


282

প্রতিটি ক্ষেত্রের প্রতিবন্ধকতা একটি স্বতন্ত্র বৈধীকরণ টীকা দ্বারা পরিচালনা করা উচিত, বা অন্য কথায় এটি অন্য ক্ষেত্রের বিরুদ্ধে এক ক্ষেত্রের বৈধতা টীকা পরীক্ষা করার পরামর্শ দেওয়া হয়নি; ক্রস-ফিল্ড বৈধতা ক্লাস পর্যায়ে করা উচিত। অতিরিক্তভাবে, জেএসআর -303 বিভাগ 2.2 একই ধরণের একাধিক বৈধতা প্রকাশ করার পছন্দের উপায়টি টীকাগুলির তালিকার মাধ্যমে। এটি ত্রুটি বার্তাকে প্রতি ম্যাচে নির্দিষ্ট করার অনুমতি দেয়।

উদাহরণস্বরূপ, একটি সাধারণ ফর্মটি বৈধকরণ:

@FieldMatch.List({
        @FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match"),
        @FieldMatch(first = "email", second = "confirmEmail", message = "The email fields must match")
})
public class UserRegistrationForm  {
    @NotNull
    @Size(min=8, max=25)
    private String password;

    @NotNull
    @Size(min=8, max=25)
    private String confirmPassword;

    @NotNull
    @Email
    private String email;

    @NotNull
    @Email
    private String confirmEmail;
}

টিকা:

package constraints;

import constraints.impl.FieldMatchValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;

/**
 * Validation annotation to validate that 2 fields have the same value.
 * An array of fields and their matching confirmation fields can be supplied.
 *
 * Example, compare 1 pair of fields:
 * @FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match")
 * 
 * Example, compare more than 1 pair of fields:
 * @FieldMatch.List({
 *   @FieldMatch(first = "password", second = "confirmPassword", message = "The password fields must match"),
 *   @FieldMatch(first = "email", second = "confirmEmail", message = "The email fields must match")})
 */
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
public @interface FieldMatch
{
    String message() default "{constraints.fieldmatch}";

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

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

    /**
     * @return The first field
     */
    String first();

    /**
     * @return The second field
     */
    String second();

    /**
     * Defines several <code>@FieldMatch</code> annotations on the same element
     *
     * @see FieldMatch
     */
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Documented
            @interface List
    {
        FieldMatch[] value();
    }
}

যাচাইকারী:

package constraints.impl;

import constraints.FieldMatch;
import org.apache.commons.beanutils.BeanUtils;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object>
{
    private String firstFieldName;
    private String secondFieldName;

    @Override
    public void initialize(final FieldMatch constraintAnnotation)
    {
        firstFieldName = constraintAnnotation.first();
        secondFieldName = constraintAnnotation.second();
    }

    @Override
    public boolean isValid(final Object value, final ConstraintValidatorContext context)
    {
        try
        {
            final Object firstObj = BeanUtils.getProperty(value, firstFieldName);
            final Object secondObj = BeanUtils.getProperty(value, secondFieldName);

            return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
        }
        catch (final Exception ignore)
        {
            // ignore
        }
        return true;
    }
}

8
@ অ্যান্ডটি: অ্যাপাচি কমন্স বিয়ান ইউটিলেসের একটি বাহ্যিক নির্ভরতা রয়েছে।
গ্যারিএফ

7
@ স্ক্রিপ্টআসর্ট আপনাকে কাস্টমাইজড পাথ দিয়ে একটি বৈধতা বার্তা তৈরি করতে দেয় না। context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addNode(secondFieldName).addConstraintViolation().disableDefaultConstraintViolation(); সঠিক ক্ষেত্রটি হাইলাইট করার সম্ভাবনা দেয় (যদি কেবল জেএসএফ সমর্থন করে)।
পিটার ডেভিস

8
আমি উপরে নমুনা ব্যবহার করেছি তবে এটি ত্রুটি বার্তা প্রদর্শন করে না, জেএসপিতে বাইন্ডিংটি কী হওয়া উচিত? আমার কাছে পাসওয়ার্ডের জন্য বাঁধাই রয়েছে এবং কেবলমাত্র নিশ্চিত করুন, অন্য কিছুর দরকার আছে? <ফর্ম: পাসওয়ার্ডের পথ = "পাসওয়ার্ড" /> <ফর্ম: ত্রুটিগুলি পথ = "পাসওয়ার্ড" সিএসএসক্লাস = "ত্রুটি" /> <ফর্ম: পাসওয়ার্ডের পথ = "কনফিগারেশন পাসওয়ার্ড" /> <ফর্ম: ত্রুটির পথ = "কনফিগারেশন পাসওয়ার্ড" সিএসএস ক্লাস = " ত্রুটি "/>
মাহমুদ সালেহ

7
BeanUtils.getPropertyএকটি স্ট্রিং প্রদান করে। উদাহরণ সম্ভবত সম্ভবত ব্যবহার করা PropertyUtils.getPropertyযা কোন বস্তুকে রিটার্ন দেয়।
সিঙ্গলশট

2
নিস উত্তর, কিন্তু আমি এটা এই প্রশ্নের উত্তর দিয়ে সম্পন্ন করেছি: stackoverflow.com/questions/11890334/...
maxivis

164

আমি আপনাকে আর একটি সম্ভাব্য সমাধানের পরামর্শ দিচ্ছি। সম্ভবত কম মার্জিত, কিন্তু সহজ!

public class MyBean {
  @Size(min=6, max=50)
  private String pass;

  private String passVerify;

  @AssertTrue(message="passVerify field should be equal than pass field")
  private boolean isValid() {
    return this.pass.equals(this.passVerify);
  }
}

isValidপদ্ধতি স্বয়ংক্রিয়ভাবে যাচাইকারী দ্বারা প্রার্থনা করা হয়।


12
আমি মনে করি এটি আবার উদ্বেগের মিশ্রণ। বিন বিন্যাসের পুরো পয়েন্টটি কনস্ট্রেন্টভালিডেটরে বৈধকরণকে বহিরাগত করা ize এক্ষেত্রে আপনার মটরশুটিতে বৈধতা যুক্তির কিছু অংশ রয়েছে এবং ভ্যালিডেটর কাঠামোর অংশ রয়েছে। যাওয়ার উপায়টি একটি শ্রেণির স্তরের প্রতিবন্ধকতা। হাইবারনেট ভ্যালিডেটর এখন একটি @ScriptAssert সরবরাহ করে যা শিমের অভ্যন্তরীণ নির্ভরতাগুলি কার্যকর করা সহজ করে তোলে।
হার্ডি

10
আমি বলব এটি আরও মার্জিত, কম নয়!
নিক

8
আমার মতামত এখনও পর্যন্ত যে শিমের বৈধতা জেএসআর উদ্বেগের মিশ্রণ।
দিমিত্রি মিনকভস্কি

3
@ গণেশকৃষ্ণান যদি আমরা এই জাতীয় বেশ কয়েকটি @AssertTrueপদ্ধতি ব্যবহার করতে চাই তবে কী হবে ? কিছু নামকরণ কনভেনশন ধরে?
স্টিফেন

3
কেন এটি সেরা উত্তর নয়
মজাদার-এনডি

32

আমি অবাক হলাম এটি বাক্সের বাইরে পাওয়া যায় না। যাইহোক, এখানে একটি সম্ভাব্য সমাধান।

আমি একটি শ্রেণি স্তরের বৈধকরণকারী তৈরি করেছি, ক্ষেত্রের স্তরটি মূল প্রশ্নে বর্ণিত হিসাবে নয়।

এখানে এনোটেশন কোডটি দেওয়া হয়েছে:

package com.moa.podium.util.constraints;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

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

  String message() default "{com.moa.podium.util.constraints.matches}";

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

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

  String field();

  String verifyField();
}

এবং যাচাইকারী নিজেই:

package com.moa.podium.util.constraints;

import org.mvel2.MVEL;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MatchesValidator implements ConstraintValidator<Matches, Object> {

  private String field;
  private String verifyField;


  public void initialize(Matches constraintAnnotation) {
    this.field = constraintAnnotation.field();
    this.verifyField = constraintAnnotation.verifyField();
  }

  public boolean isValid(Object value, ConstraintValidatorContext context) {
    Object fieldObj = MVEL.getProperty(field, value);
    Object verifyFieldObj = MVEL.getProperty(verifyField, value);

    boolean neitherSet = (fieldObj == null) && (verifyFieldObj == null);

    if (neitherSet) {
      return true;
    }

    boolean matches = (fieldObj != null) && fieldObj.equals(verifyFieldObj);

    if (!matches) {
      context.disableDefaultConstraintViolation();
      context.buildConstraintViolationWithTemplate("message")
          .addNode(verifyField)
          .addConstraintViolation();
    }

    return matches;
  }
}

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

@ ম্যাচ টীকাটি এরপরে শিমের উপরে নিম্নরূপ ব্যবহার করা যেতে পারে:

@Matches(field="pass", verifyField="passRepeat")
public class AccountCreateForm {

  @Size(min=6, max=50)
  private String pass;
  private String passRepeat;

  ...
}

অস্বীকৃতি হিসাবে, আমি এটি গত 5 মিনিটে লিখেছিলাম, তাই আমি সম্ভবত এখনও সমস্ত বাগ বের করে ফেলিনি। কিছু ভুল হলে আমি উত্তর আপডেট করব।


1
এটি দুর্দান্ত এবং এটি আমার পক্ষে কাজ করছে, অ্যাডনোট ব্যতীত বাদ দেওয়া হয়েছে এবং আমি যদি এর পরিবর্তে অ্যাডপ্রোপার্টিনোড ব্যবহার করি তবে আমি অ্যাবস্ট্রাকথ মেথোডর পেতে পারি। গুগল এখানে আমাকে সাহায্য করছে না। কী সমাধান? কোথাও কোনও নির্ভরতা অনুপস্থিত?
পল গ্রেনিয়ার 15

29

হাইবারনেট ভ্যালিডেটর ৪.১.০. সহ ফাইনাল আমি @ স্ক্রিপ্টএসার্ট ব্যবহার করার পরামর্শ দিচ্ছি । এর জাভাডক থেকে ব্যতীত:

স্ক্রিপ্ট এক্সপ্রেশন যেকোন স্ক্রিপ্টিং বা এক্সপ্রেশন ভাষায় লেখা যেতে পারে, যার জন্য একটি জেএসআর 223 ("জাভাটিএম প্ল্যাটফর্মের জন্য স্ক্রিপ্টিং") সামঞ্জস্যপূর্ণ ইঞ্জিনটি ক্লাসপথে পাওয়া যাবে।

নোট: মূল্যায়ন একটি স্ক্রিপ্টিং "দ্বারা সঞ্চালিত হচ্ছে ইঞ্জিন , তাই জাভা" সার্ভার প্রান্তের "এ," জাভা ভার্চুয়াল মেশিনের চলমান না এ "ক্লায়েন্ট সাইড" হিসাবে কিছু মন্তব্য বিবৃত।

উদাহরণ:

@ScriptAssert(lang = "javascript", script = "_this.passVerify.equals(_this.pass)")
public class MyBean {
  @Size(min=6, max=50)
  private String pass;

  private String passVerify;
}

বা সংক্ষিপ্ত নাম এবং নাল-নিরাপদ সহ:

@ScriptAssert(lang = "javascript", alias = "_",
    script = "_.passVerify != null && _.passVerify.equals(_.pass)")
public class MyBean {
  @Size(min=6, max=50)
  private String pass;

  private String passVerify;
}

বা জাভা 7+ নাল-নিরাপদ সহ Objects.equals():

@ScriptAssert(lang = "javascript", script = "Objects.equals(_this.passVerify, _this.pass)")
public class MyBean {
  @Size(min=6, max=50)
  private String pass;

  private String passVerify;
}

তবুও, কাস্টম শ্রেণীর স্তরের যাচাইকারী @ ম্যাচ সমাধানে কোনও সমস্যা নেই।


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

2
আমি সম্মত হই যে নিকোর বাস্তবায়ন দুর্দান্ত, তবে জেএসকে প্রকাশের ভাষা হিসাবে ব্যবহার করার বিষয়ে আপত্তিজনক কিছু দেখছি না। জাভা 6 ঠিক একই ধরনের অ্যাপ্লিকেশনগুলির জন্য রাইনো অন্তর্ভুক্ত করে। আমি @ স্ক্রিপ্টএরসেটটি পছন্দ করি কারণ প্রতিবার আমার কাছে একটি অভিনব টাইপ পরীক্ষা করার জন্য আমার কাছে কোনও টিকা এবং কোনও বৈধতা তৈরি না করে ঠিক এটি কাজ করে।

4
যেমনটি বলা হয়েছে, শ্রেণি স্তর বৈধকারীর সাথে কিছুই ভুল হয় না। স্ক্রিপ্টএসর্ট হ'ল একটি বিকল্প যা আপনাকে কাস্টম কোড লেখার প্রয়োজন হয় না। আমি বলিনি যে এটি পছন্দসই সমাধান ;-)
হার্ডি

দুর্দান্ত উত্তর কারণ পাসওয়ার্ড নিশ্চিতকরণ গুরুত্বপূর্ণ বৈধতা নয় তাই এটি ক্লায়েন্টের পক্ষে করা যেতে পারে
পিটারচাউলা

19

ক্রস ফিল্ডগুলির বৈধতাগুলি কাস্টম সীমাবদ্ধতা তৈরি করে করা যেতে পারে।

উদাহরণ: - ব্যবহারকারীর উদাহরণের পাসওয়ার্ড এবং কনফার্মপ্যাসওয়ার্ড ক্ষেত্রগুলি তুলনা করুন।

CompareStrings

@Target({TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy=CompareStringsValidator.class)
@Documented
public @interface CompareStrings {
    String[] propertyNames();
    StringComparisonMode matchMode() default EQUAL;
    boolean allowNull() default false;
    String message() default "";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

StringComparisonMode

public enum StringComparisonMode {
    EQUAL, EQUAL_IGNORE_CASE, NOT_EQUAL, NOT_EQUAL_IGNORE_CASE
}

CompareStringsValidator

public class CompareStringsValidator implements ConstraintValidator<CompareStrings, Object> {

    private String[] propertyNames;
    private StringComparisonMode comparisonMode;
    private boolean allowNull;

    @Override
    public void initialize(CompareStrings constraintAnnotation) {
        this.propertyNames = constraintAnnotation.propertyNames();
        this.comparisonMode = constraintAnnotation.matchMode();
        this.allowNull = constraintAnnotation.allowNull();
    }

    @Override
    public boolean isValid(Object target, ConstraintValidatorContext context) {
        boolean isValid = true;
        List<String> propertyValues = new ArrayList<String> (propertyNames.length);
        for(int i=0; i<propertyNames.length; i++) {
            String propertyValue = ConstraintValidatorHelper.getPropertyValue(String.class, propertyNames[i], target);
            if(propertyValue == null) {
                if(!allowNull) {
                    isValid = false;
                    break;
                }
            } else {
                propertyValues.add(propertyValue);
            }
        }

        if(isValid) {
            isValid = ConstraintValidatorHelper.isValid(propertyValues, comparisonMode);
        }

        if (!isValid) {
          /*
           * if custom message was provided, don't touch it, otherwise build the
           * default message
           */
          String message = context.getDefaultConstraintMessageTemplate();
          message = (message.isEmpty()) ?  ConstraintValidatorHelper.resolveMessage(propertyNames, comparisonMode) : message;

          context.disableDefaultConstraintViolation();
          ConstraintViolationBuilder violationBuilder = context.buildConstraintViolationWithTemplate(message);
          for (String propertyName : propertyNames) {
            NodeBuilderDefinedContext nbdc = violationBuilder.addNode(propertyName);
            nbdc.addConstraintViolation();
          }
        }    

        return isValid;
    }
}

ConstraintValidatorHelper

public abstract class ConstraintValidatorHelper {

public static <T> T getPropertyValue(Class<T> requiredType, String propertyName, Object instance) {
        if(requiredType == null) {
            throw new IllegalArgumentException("Invalid argument. requiredType must NOT be null!");
        }
        if(propertyName == null) {
            throw new IllegalArgumentException("Invalid argument. PropertyName must NOT be null!");
        }
        if(instance == null) {
            throw new IllegalArgumentException("Invalid argument. Object instance must NOT be null!");
        }
        T returnValue = null;
        try {
            PropertyDescriptor descriptor = new PropertyDescriptor(propertyName, instance.getClass());
            Method readMethod = descriptor.getReadMethod();
            if(readMethod == null) {
                throw new IllegalStateException("Property '" + propertyName + "' of " + instance.getClass().getName() + " is NOT readable!");
            }
            if(requiredType.isAssignableFrom(readMethod.getReturnType())) {
                try {
                    Object propertyValue = readMethod.invoke(instance);
                    returnValue = requiredType.cast(propertyValue);
                } catch (Exception e) {
                    e.printStackTrace(); // unable to invoke readMethod
                }
            }
        } catch (IntrospectionException e) {
            throw new IllegalArgumentException("Property '" + propertyName + "' is NOT defined in " + instance.getClass().getName() + "!", e);
        }
        return returnValue; 
    }

    public static boolean isValid(Collection<String> propertyValues, StringComparisonMode comparisonMode) {
        boolean ignoreCase = false;
        switch (comparisonMode) {
        case EQUAL_IGNORE_CASE:
        case NOT_EQUAL_IGNORE_CASE:
            ignoreCase = true;
        }

        List<String> values = new ArrayList<String> (propertyValues.size());
        for(String propertyValue : propertyValues) {
            if(ignoreCase) {
                values.add(propertyValue.toLowerCase());
            } else {
                values.add(propertyValue);
            }
        }

        switch (comparisonMode) {
        case EQUAL:
        case EQUAL_IGNORE_CASE:
            Set<String> uniqueValues = new HashSet<String> (values);
            return uniqueValues.size() == 1 ? true : false;
        case NOT_EQUAL:
        case NOT_EQUAL_IGNORE_CASE:
            Set<String> allValues = new HashSet<String> (values);
            return allValues.size() == values.size() ? true : false;
        }

        return true;
    }

    public static String resolveMessage(String[] propertyNames, StringComparisonMode comparisonMode) {
        StringBuffer buffer = concatPropertyNames(propertyNames);
        buffer.append(" must");
        switch(comparisonMode) {
        case EQUAL:
        case EQUAL_IGNORE_CASE:
            buffer.append(" be equal");
            break;
        case NOT_EQUAL:
        case NOT_EQUAL_IGNORE_CASE:
            buffer.append(" not be equal");
            break;
        }
        buffer.append('.');
        return buffer.toString();
    }

    private static StringBuffer concatPropertyNames(String[] propertyNames) {
        //TODO improve concating algorithm
        StringBuffer buffer = new StringBuffer();
        buffer.append('[');
        for(String propertyName : propertyNames) {
            char firstChar = Character.toUpperCase(propertyName.charAt(0));
            buffer.append(firstChar);
            buffer.append(propertyName.substring(1));
            buffer.append(", ");
        }
        buffer.delete(buffer.length()-2, buffer.length());
        buffer.append("]");
        return buffer;
    }
}

ব্যবহারকারী

@CompareStrings(propertyNames={"password", "confirmPassword"})
public class User {
    private String password;
    private String confirmPassword;

    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    public String getConfirmPassword() { return confirmPassword; }
    public void setConfirmPassword(String confirmPassword) { this.confirmPassword =  confirmPassword; }
}

পরীক্ষা

    public void test() {
        User user = new User();
        user.setPassword("password");
        user.setConfirmPassword("paSSword");
        Set<ConstraintViolation<User>> violations = beanValidator.validate(user);
        for(ConstraintViolation<User> violation : violations) {
            logger.debug("Message:- " + violation.getMessage());
        }
        Assert.assertEquals(violations.size(), 1);
    }

আউটপুট Message:- [Password, ConfirmPassword] must be equal.

CompareStrings বৈধতা সীমাবদ্ধতা ব্যবহার করে, আমরা দুটিরও বেশি বৈশিষ্ট্য তুলনা করতে পারি এবং আমরা চারটি স্ট্রিং তুলনা পদ্ধতিতে যে কোনও মিশ্রণ করতে পারি।

ColorChoice

@CompareStrings(propertyNames={"color1", "color2", "color3"}, matchMode=StringComparisonMode.NOT_EQUAL, message="Please choose three different colors.")
public class ColorChoice {

    private String color1;
    private String color2;
    private String color3;
        ......
}

পরীক্ষা

ColorChoice colorChoice = new ColorChoice();
        colorChoice.setColor1("black");
        colorChoice.setColor2("white");
        colorChoice.setColor3("white");
        Set<ConstraintViolation<ColorChoice>> colorChoiceviolations = beanValidator.validate(colorChoice);
        for(ConstraintViolation<ColorChoice> violation : colorChoiceviolations) {
            logger.debug("Message:- " + violation.getMessage());
        }

আউটপুট Message:- Please choose three different colors.

একইভাবে, আমাদের তুলনা নম্বর, তুলনা তারিখ, ইত্যাদি ক্রস ফিল্ডগুলির বৈধতা সীমাবদ্ধতা থাকতে পারে।

PS আমি এই কোডটি উত্পাদন পরিবেশের অধীনে পরীক্ষা করি নি (যদিও আমি এটি পরিবেশের অধীনে পরীক্ষা করেছি), তাই এই কোডটিকে মাইলস্টোন রিলিজ হিসাবে বিবেচনা করুন। আপনি যদি কোনও বাগ খুঁজে পান তবে একটি ভাল মন্তব্য লিখুন। :)


আমি এই পদ্ধতির পছন্দ করি, কারণ এটি অন্যদের তুলনায় আরও নমনীয়। এটি আমাকে সমতার জন্য 2 টিরও বেশি ক্ষেত্রকে বৈধতা দেয়। চমৎকার কাজ!
টৌরেন

9

আমি অ্যালবার্থোভেনের উদাহরণটি (হাইবারনেট-ভ্যালিডেটর ৩.০.২.জিএ) চেষ্টা করেছি এবং আমি একটি বৈধকরণের ধারণা পেয়েছি: not টীকাগুলি অবশ্যই জাভাবিয়ান নামকরণের কনভেনশন অনুসরণ করবে। ম্যাচ () না। আমি "ম্যাচ" থেকে "আইজভালিড" থেকে এই পদ্ধতির নাম পরিবর্তন করার পরে এটি কাজ করে।

public class Password {

    private String password;

    private String retypedPassword;

    public Password(String password, String retypedPassword) {
        super();
        this.password = password;
        this.retypedPassword = retypedPassword;
    }

    @AssertTrue(message="password should match retyped password")
    private boolean isValid(){
        if (password == null) {
            return retypedPassword == null;
        } else {
            return password.equals(retypedPassword);
        }
    }

    public String getPassword() {
        return password;
    }

    public String getRetypedPassword() {
        return retypedPassword;
    }

}

এটি আমার পক্ষে সঠিকভাবে কাজ করেছে তবে ত্রুটি বার্তাটি প্রদর্শন করে না। এটি কাজ করে এবং আপনার জন্য ত্রুটি বার্তা প্রদর্শন করে? কিভাবে?
ক্ষুদ্র

1
@ টিনি: বার্তাটি বৈধকারীর দ্বারা ফিরে আসা লঙ্ঘনের মধ্যে থাকা উচিত। (একটি ইউনিট পরীক্ষা লিখুন: stackoverflow.com/questions/5704743/… )। তবে বৈধতা বার্তাটি "isvalid" সম্পত্তির অন্তর্গত। সুতরাং জিইআইআই যদি টাইপডপ্যাসওয়ার্ড এবং ইজভালিড (পুনরায় টাইপ করা পাসওয়ার্ডের পাশের) জন্য সমস্যাগুলি দেখায় তবে কেবল বার্তাটি জিইউআইতে প্রদর্শিত হবে।
রাল্ফ

8

আপনি যদি স্প্রিং ফ্রেমওয়ার্ক ব্যবহার করেন তবে তার জন্য আপনি স্প্রিং এক্সপ্রেশন ল্যাঙ্গুয়েজ (স্পেল) ব্যবহার করতে পারেন। আমি একটি ছোট লাইব্রেরি লিখেছি যা স্পেলের উপর ভিত্তি করে জেএসআর -303 যাচাইকারী সরবরাহ করে - এটি ক্রস-ফিল্ডের বৈধতাগুলিকে বাতাস তৈরি করে! Https://github.com/jirutka/ লাডিয়েটার- স্প্রিং এ একবার দেখুন ।

এটি পাসওয়ার্ডের ক্ষেত্রগুলির দৈর্ঘ্য এবং সাম্যকে বৈধতা দেবে।

@SpELAssert(value = "pass.equals(passVerify)",
            message = "{validator.passwords_not_same}")
public class MyBean {

    @Size(min = 6, max = 50)
    private String pass;

    private String passVerify;
}

আপনি উভয় খালি না থাকলেই পাসওয়ার্ড ক্ষেত্রগুলি বৈধ করতে সহজেই এটিকে পরিবর্তন করতে পারেন।

@SpELAssert(value = "pass.equals(passVerify)",
            applyIf = "pass || passVerify",
            message = "{validator.passwords_not_same}")
public class MyBean {

    @Size(min = 6, max = 50)
    private String pass;

    private String passVerify;
}

4

আমি জাকুব জিরুতকার কাছ থেকে ধারণাটি পছন্দ করিস্প্রিং এক্সপ্রেশন ল্যাঙ্গুয়েজটি ব্যবহার করতে । আপনি যদি অন্য কোনও গ্রন্থাগার / নির্ভরতা যুক্ত করতে না চান (ধরে নিলেন আপনি ইতিমধ্যে বসন্ত ব্যবহার করেছেন), তার ধারণার একটি সরলিকৃত বাস্তবায়ন এখানে।

বাধা:

@Constraint(validatedBy=ExpressionAssertValidator.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExpressionAssert {
    String message() default "expression must evaluate to true";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String value();
}

বৈধতা প্রদানকারী:

public class ExpressionAssertValidator implements ConstraintValidator<ExpressionAssert, Object> {
    private Expression exp;

    public void initialize(ExpressionAssert annotation) {
        ExpressionParser parser = new SpelExpressionParser();
        exp = parser.parseExpression(annotation.value());
    }

    public boolean isValid(Object value, ConstraintValidatorContext context) {
        return exp.getValue(value, Boolean.class);
    }
}

এর মতো প্রয়োগ করুন:

@ExpressionAssert(value="pass == passVerify", message="passwords must be same")
public class MyBean {
    @Size(min=6, max=50)
    private String pass;
    private String passVerify;
}

3

প্রথম উত্তরে মন্তব্য করার মতো খ্যাতি আমার নেই তবে আমি যুক্ত করতে চেয়েছিলাম যে আমি বিজয়ী উত্তরের জন্য ইউনিট পরীক্ষা যুক্ত করেছি এবং নিম্নলিখিত পর্যবেক্ষণগুলি পেয়েছি:

  • যদি আপনি প্রথম বা মাঠের নামগুলি ভুল করে থাকেন তবে মানগুলির সাথে মেলে না এমনভাবে আপনি একটি বৈধতা ত্রুটি পাবেন। উদাহরণস্বরূপ বানান ভুল দ্বারা বিভক্ত করবেন না

@ ফিল্ডম্যাচ (প্রথম = " অবৈধ ফিল্ডনাম 1", দ্বিতীয় = "বৈধফিল্ডনেম 2")

  • বৈধকরণকারী সমতুল্য ডেটা ধরণের গ্রহণ করবে যেমন এইগুলি ফিল্ডম্যাচের সাথে পাস করবে:

বেসরকারী স্ট্রিংফিল্ড = "1";

ব্যক্তিগত পূর্ণসংখ্যা পূর্ণসংখ্যা = নতুন পূর্ণসংখ্যা (1)

প্রাইভেট ইন্টিফিল্ড = 1;

  • ক্ষেত্রগুলি যদি কোনও অবজেক্ট টাইপের হয় যা সমান প্রয়োগ করে না, বৈধতা ব্যর্থ হবে।

2

খুব সুন্দর সমাধান ব্র্যাডহাউস। @ ম্যাচ টীকাগুলি একাধিক ক্ষেত্রে প্রয়োগ করার কোনও উপায় আছে কি?

সম্পাদনা: এই প্রশ্নের উত্তর দেওয়ার জন্য আমি এখানে যে সমাধানটি নিয়ে এসেছি, আমি একক মানের পরিবর্তে অ্যারে গ্রহণের জন্য নিয়ন্ত্রণকে সংশোধন করেছি:

@Matches(fields={"password", "email"}, verifyFields={"confirmPassword", "confirmEmail"})
public class UserRegistrationForm  {

    @NotNull
    @Size(min=8, max=25)
    private String password;

    @NotNull
    @Size(min=8, max=25)
    private String confirmPassword;


    @NotNull
    @Email
    private String email;

    @NotNull
    @Email
    private String confirmEmail;
}

টীকা দেওয়ার কোড:

package springapp.util.constraints;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

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

  String message() default "{springapp.util.constraints.matches}";

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

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

  String[] fields();

  String[] verifyFields();
}

এবং বাস্তবায়ন:

package springapp.util.constraints;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.apache.commons.beanutils.BeanUtils;

public class MatchesValidator implements ConstraintValidator<Matches, Object> {

    private String[] fields;
    private String[] verifyFields;

    public void initialize(Matches constraintAnnotation) {
        fields = constraintAnnotation.fields();
        verifyFields = constraintAnnotation.verifyFields();
    }

    public boolean isValid(Object value, ConstraintValidatorContext context) {

        boolean matches = true;

        for (int i=0; i<fields.length; i++) {
            Object fieldObj, verifyFieldObj;
            try {
                fieldObj = BeanUtils.getProperty(value, fields[i]);
                verifyFieldObj = BeanUtils.getProperty(value, verifyFields[i]);
            } catch (Exception e) {
                //ignore
                continue;
            }
            boolean neitherSet = (fieldObj == null) && (verifyFieldObj == null);
            if (neitherSet) {
                continue;
            }

            boolean tempMatches = (fieldObj != null) && fieldObj.equals(verifyFieldObj);

            if (!tempMatches) {
                addConstraintViolation(context, fields[i]+ " fields do not match", verifyFields[i]);
            }

            matches = matches?tempMatches:matches;
        }
        return matches;
    }

    private void addConstraintViolation(ConstraintValidatorContext context, String message, String field) {
        context.disableDefaultConstraintViolation();
        context.buildConstraintViolationWithTemplate(message).addNode(field).addConstraintViolation();
    }
}

হুম। নিশ্চিত না. আপনি প্রতিটি নিশ্চিতকরণ ক্ষেত্রের জন্য নির্দিষ্ট বৈধতা তৈরির চেষ্টা করতে পারেন (যাতে তাদের বিভিন্ন টিকা থাকে), বা একাধিক ক্ষেত্রের ক্ষেত্র গ্রহণ করার জন্য @ ম্যাচ টীকাগুলি আপডেট করে।
ব্র্যাডহাউস

ধন্যবাদ ব্র্যাডহাউস, একটি সমাধান নিয়ে এসেছেন এবং উপরে পোস্ট করেছেন। বিবিধ সংখ্যক তর্কগুলি পাস করার জন্য এটির জন্য একটু কাজ করা দরকার যাতে আপনি সূচিপত্র-অফফাউন্ডস এক্সেক্সেশন না পান, তবে বেসিকগুলি সেখানে রয়েছে।
ম্যাকগিন 22'10

1

আপনাকে এটিকে স্পষ্টভাবে কল করতে হবে। উপরের উদাহরণে ব্র্যাডহাউস আপনাকে একটি কাস্টম সীমাবদ্ধতা লেখার জন্য সমস্ত পদক্ষেপ দিয়েছে।

আপনার কলার শ্রেণিতে এই কোডটি যুক্ত করুন।

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();

Set<ConstraintViolation<yourObjectClass>> constraintViolations = validator.validate(yourObject);

উপরের ক্ষেত্রে এটি হবে

Set<ConstraintViolation<AccountCreateForm>> constraintViolations = validator.validate(objAccountCreateForm);

1

ওভাল কেন চেষ্টা করবেন না: http://oval.sourceforge.net/

দেখে মনে হচ্ছে এটি ওজিএনএলকে সমর্থন করে যাতে আপনি এটি আরও প্রাকৃতিকভাবে করতে পারেন

@Assert(expr = "_value ==_this.pass").

1

আপনি যদি না হয় সন্ত্রস্ত. সত্যিই আশ্চর্যজনক ধারণা। আমি আলবার্থোভেন এবং ম্যাকগিনের সর্বাধিক পছন্দ করি, তাই আমি উভয় ধারণা একত্রিত করার সিদ্ধান্ত নিয়েছি। এবং সমস্ত ক্ষেত্রে পূরণের জন্য কিছু জেনেরিক সমাধান বিকাশ করুন। এখানে আমার প্রস্তাবিত সমাধান।

@Documented
@Constraint(validatedBy = NotFalseValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotFalse {


    String message() default "NotFalse";
    String[] messages();
    String[] properties();
    String[] verifiers();

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

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

}

public class NotFalseValidator implements ConstraintValidator<NotFalse, Object> {
    private String[] properties;
    private String[] messages;
    private String[] verifiers;
    @Override
    public void initialize(NotFalse flag) {
        properties = flag.properties();
        messages = flag.messages();
        verifiers = flag.verifiers();
    }

    @Override
    public boolean isValid(Object bean, ConstraintValidatorContext cxt) {
        if(bean == null) {
            return true;
        }

        boolean valid = true;
        BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);

        for(int i = 0; i< properties.length; i++) {
           Boolean verified = (Boolean) beanWrapper.getPropertyValue(verifiers[i]);
           valid &= isValidProperty(verified,messages[i],properties[i],cxt);
        }

        return valid;
    }

    boolean isValidProperty(Boolean flag,String message, String property, ConstraintValidatorContext cxt) {
        if(flag == null || flag) {
            return true;
        } else {
            cxt.disableDefaultConstraintViolation();
            cxt.buildConstraintViolationWithTemplate(message)
                    .addPropertyNode(property)
                    .addConstraintViolation();
            return false;
        }

    }



}

@NotFalse(
        messages = {"End Date Before Start Date" , "Start Date Before End Date" } ,
        properties={"endDateTime" , "startDateTime"},
        verifiers = {"validDateRange" , "validDateRange"})
public class SyncSessionDTO implements ControllableNode {
    @NotEmpty @NotPastDate
    private Date startDateTime;

    @NotEmpty
    private Date endDateTime;



    public Date getStartDateTime() {
        return startDateTime;
    }

    public void setStartDateTime(Date startDateTime) {
        this.startDateTime = startDateTime;
    }

    public Date getEndDateTime() {
        return endDateTime;
    }

    public void setEndDateTime(Date endDateTime) {
        this.endDateTime = endDateTime;
    }


    public Boolean getValidDateRange(){
        if(startDateTime != null && endDateTime != null) {
            return startDateTime.getTime() <= endDateTime.getTime();
        }

        return null;
    }

}

0

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

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {

    private String firstFieldName;
    private String secondFieldName;

    @Override
    public void initialize(final FieldMatch constraintAnnotation) {
        firstFieldName = constraintAnnotation.first();
        secondFieldName = constraintAnnotation.second();
    }

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

        BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(object);
        final Object firstObj = beanWrapper.getPropertyValue(firstFieldName);
        final Object secondObj = beanWrapper.getPropertyValue(secondFieldName);

        boolean isValid = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);

        if (!isValid) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
                .addPropertyNode(firstFieldName)
                .addConstraintViolation();
        }

        return isValid;

    }
}

-1

সমস্যার সমাধান সম্পর্কিত প্রশ্নের সাথে: কীভাবে এমন ক্ষেত্রটি অ্যাক্সেস করবেন যা টীকা দেওয়ার সম্পত্তিতে বর্ণিত

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Match {

    String field();

    String message() default "";
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MatchValidator.class)
@Documented
public @interface EnableMatchConstraint {

    String message() default "Fields must match!";

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

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

public class MatchValidator implements  ConstraintValidator<EnableMatchConstraint, Object> {

    @Override
    public void initialize(final EnableMatchConstraint constraint) {}

    @Override
    public boolean isValid(final Object o, final ConstraintValidatorContext context) {
        boolean result = true;
        try {
            String mainField, secondField, message;
            Object firstObj, secondObj;

            final Class<?> clazz = o.getClass();
            final Field[] fields = clazz.getDeclaredFields();

            for (Field field : fields) {
                if (field.isAnnotationPresent(Match.class)) {
                    mainField = field.getName();
                    secondField = field.getAnnotation(Match.class).field();
                    message = field.getAnnotation(Match.class).message();

                    if (message == null || "".equals(message))
                        message = "Fields " + mainField + " and " + secondField + " must match!";

                    firstObj = BeanUtils.getProperty(o, mainField);
                    secondObj = BeanUtils.getProperty(o, secondField);

                    result = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
                    if (!result) {
                        context.disableDefaultConstraintViolation();
                        context.buildConstraintViolationWithTemplate(message).addPropertyNode(mainField).addConstraintViolation();
                        break;
                    }
                }
            }
        } catch (final Exception e) {
            // ignore
            //e.printStackTrace();
        }
        return result;
    }
}

এবং কিভাবে এটি ব্যবহার করবেন ...? এটার মত:

@Entity
@EnableMatchConstraint
public class User {

    @NotBlank
    private String password;

    @Match(field = "password")
    private String passwordConfirmation;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.