バリデーターとアスペクト:検証のカスタマイズ

こんにちは、Habr!



しばらくして、ここにもう一度書いて、私の経験を共有することにしました。 今回は、標準バリデーターをカスタマイズし、Springアスペクトを使用する必要がある場所でそれらを呼び出す方法について説明します。 まあ、それは私に書くことを促しました-そのような情報の欠如、特にロシア語で。



問題



そのため、アプリケーションの本質はほぼ次のとおりです。ゲートウェイがあります-要求を受け入れ、さらにそれを修正して適切な銀行にリダイレクトするapiです。 しかし、各銀行への要求は異なっていました-検証パラメーターも同様です。 したがって、最初の要求を検証することはできませんでした。 2つの方法がありました。javax.validationの注釈を使用するか、独自の検証レイヤーを作成します。 最初のケースでは、キャッチがありました-デフォルトでは、オブジェクトはコントローラーでのみ検証できます。 2番目のケースではマイナスもありました-これは余分なレイヤー、大量のコードであり、モデルが変更された場合でも、バリデーターを変更する必要があります。



そのため、コントローラーだけでなく、必要に応じて標準バリデーターをプルする方法を見つけることが決定されました。



バリデーターをプルします



Googleで数時間掘り下げた後、いくつかの解決策が見つかりましたが、その中で最も適切なのはjavax.validation.Validatorにあり、そのメソッドでvalidateメソッドを呼び出すことでした。



解決策が見つかったように見えますが、バリデーターがあらゆる場所で検証されるのは良い考えではないように思えるので、よりエレガントな解決策が必要でした。



AOPを追加



考え直すことなく、私は自分の好きな側面をこのソリューションに適応させることにしました。



ロジックはほぼ次のとおりでした。注釈を作成し、あるオブジェクトを別のオブジェクトに変換するメソッドにそれを掛けます。 さらにアスペクトでは、このアノテーションでマークされたすべてのメソッドをインターセプトし、返される値に対してvalidateメソッドを呼び出します。 利益



注釈:



//      @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Validate {}
      
      







クエリを変換する1つの方法は次のとおりです。



 @Validate public SomeBankRequest requestToBankRequest(Request<T> request) { SomeBankRequest bankRequest = ...; ... //        ... return bankRequest; }
      
      





まあ、実際にはアスペクト自体:



 @Aspect @Component public class ValidationAspect { private final Validator validator; //    public ValidationAspect(Validator validator) { this.validator = validator; } //       // @Validate       @AfterReturning(pointcut = "@annotation(api.annotations.Validate)", returning = "result") public void validate(JoinPoint joinPoint, Object result) { //     Set<ConstraintViolation<Object>> violations = validator.validate(result); //    ,    ,     //        if (!violations.isEmpty()) { StringBuilder builder = new StringBuilder(); //          ,   //  violations.forEach(violation -> builder .append(violation.getPropertyPath()) .append("[" + violation.getMessage() + "],")); throw new IllegalArgumentException("Invalid values for fields: " + builder.toString()); } } }
      
      





作業面について簡単に説明します。



Validateアノテーションでマークされたメソッドによって返されたオブジェクトをインターセプトし、それをValidatorメソッドに渡します。それはSet<ConstraintViolation<Object>>



-要するに-検証済みフィールドとエラーに関するさまざまな情報を持つクラスのSet<ConstraintViolation<Object>>



を返します。 エラーがない場合、セットは空になります。 次に、セットを調べて、すべてのフィールドが検証されていないエラーメッセージを作成し、実行をスローします。



 violation.getPropertyPath() -    violation.getMessage() -  ,      
      
      





おわりに



したがって、アプリケーションの任意の時点で必要なオブジェクトの検証を呼び出すことができ、必要に応じて、オブジェクトを返すメソッドだけでなく、メソッドのフィールドとパラメーターにも検証が合格するように、注釈とアスペクトを補足できます。



PS



また、 Validateとマークされたメソッドを同じクラスの別のメソッドから呼び出す場合、 aopとproxyの間の接続を覚えておいてください



All Articles