Spring FrameworkでDTO検証を構成する

みなさんこんにちは! 今日は、データ転送オブジェクト(DTO)を介して入力されたデータの検証に触れ、必要なものだけを受け取って提供できるように注釈と可視性を構成します。



したがって、対応するフィールドを持つUserDto DTOクラスがあります。



public class UserDto { private Long id; private String name; private String login; private String password; private String email; }
      
      





コンストラクターとゲッターセッターを省略します-それらを作成する方法を知っていると確信していますが、コードが3〜4倍に増加するという感覚はありません-既に存在していると想像してみましょう。



CRUDメソッドを使用して、コントローラーを介してDTOを受け入れます。 繰り返しますが、すべてのCRUDメソッドを書くわけではありません-実験の純度のために、いくつかあります。 createおよびupdateNameにします。



  @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> create(@RequestBody UserDto dto) { return new ResponseEntity<>(service.save(dto), HttpStatus.OK); } @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> updateName(@RequestBody UserDto dto) { return new ResponseEntity<>(service.update(dto), HttpStatus.OK); }
      
      





明確にするために、それらも簡素化する必要がありました。 したがって、UserDtoに変換されるJSONを取得し、JSONに変換されてクライアントに送信されるUserDtoを返します。



ここで、一緒に作業するいくつかの検証アノテーションに精通することを提案します。



  @Null //   null @NotNull //    null @Email //   e-mail
      
      





すべての注釈はjavax.validation.constraintsライブラリで利用可能です。 したがって、検証されたオブジェクトをすぐに受け取り、さらに本質に変換してデータベースに保存するように、DTOを構成します。 入力する必要があるフィールドには、 NotNullをマークし、電子メールにもマークを付けます。



 public class UserDto { @Null //   private Long id; @NotNull private String name; @NotNull private String login; @NotNull private String password; @NotNull @Email private String email; }
      
      





DTOの検証設定を設定します-id以外のすべてのフィールドに入力する必要があります-データベースで生成されます。 検証をコントローラーに追加します。



  @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> create(@Validated @RequestBody UserDto dto) { return new ResponseEntity<>(service.save(dto), HttpStatus.OK); } @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> updateName(@Validated @RequestBody UserDto dto) { return new ResponseEntity<>(service.update(dto), HttpStatus.OK); }
      
      





このように構成された検証は、新しいユーザーの作成には適していますが、既存のユーザーの更新には適していません。このため、id(nullに設定)を取得する必要があります。また、updateNameの名前のみを変更するため、 。 つまり、IDと名前を取得する必要があります。 そして、ここには可視性インターフェースが必要です。



DTOクラスに直接インターフェイスを作成しましょう(わかりやすくするために、このようなものを別のクラスに入れることをお勧めします。たとえば、転送などの別のパッケージに入れることをお勧めします)。 インターフェイスはNewと呼ばれ、2番目はExistと呼ばれ、そこからUpdateNameを継承します(将来、Existから他の可視性インターフェイスを継承できるようになり、複数の名前を変更します)。



 public class User { interface New { } interface Exist { } interface UpdateName extends Exist { } @Null //   private Long id; @NotNull private String name; @NotNull private String login; @NotNull private String password; @NotNull @Email private String email; }
      
      





次に、Newインターフェースで注釈をマークします。



  @Null(groups = {New.class}) private Long id; @NotNull(groups = {New.class}) private String name; @NotNull(groups = {New.class}) private String login; @NotNull(groups = {New.class}) private String password; @NotNull(groups = {New.class}) @Email(groups = {New.class}) private String email;
      
      





現在、これらの注釈は、新規インターフェースを指定する場合にのみ機能します。 名前フィールドを更新する必要がある場合にのみ、ケースに注釈を設定できます(非nullのIDと名前を指定する必要があることを思い出してください、残りはnullです)。 これは次のようなものです。



  @Null(groups = {New.class}) @NotNull(groups = {UpdateName.class}) private Long id; @NotNull(groups = {New.class, UpdateName.class}) private String name; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) private String login; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) private String password; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) @Email(groups = {New.class}) private String email;
      
      





コントローラーで必要な設定を行い、検証を設定するインターフェースを登録するだけです。



  @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> create(@Validated(UserDto.New.class) @RequestBody UserDto dto) { return new ResponseEntity<>(service.save(dto), HttpStatus.OK); } @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> updateName(@Validated(UserDto.UpdateName.class) @RequestBody UserDto dto) { return new ResponseEntity<>(service.update(dto), HttpStatus.OK); }
      
      





これで、各メソッドに対して、独自の設定セットが呼び出されます。



そこで、入力データを検証する方法を考え出しましたが、今では出力を検証する必要があります。 これは@JsonViewアノテーションを使用して行われます。



これで、出力DTOには、すべてのフィールドが含まれます。 しかし、パスワードを与える必要がないと仮定します(例外的な場合を除く)。



出力DTOを検証するために、出力の可視性を担当する2つのインターフェイスを追加します。詳細(ユーザーへの表示用)とAdminDetails(管理者のみへの表示用)です。 インターフェイスは相互に継承できますが、認識を簡単にするために、ここではこれを行いません。この主題に関する入力データの例にすぎません。



  interface New { } interface Exist { } interface UpdateName extends Exist { } interface Details { } interface AdminDetails { }
      
      





これで、必要に応じてフィールドに注釈を付けることができます(パスワード以外はすべて表示されます)。



  @Null(groups = {New.class}) @NotNull(groups = {UpdateName.class}) @JsonView({Details.class}) private Long id; @NotNull(groups = {New.class, UpdateName.class}) @JsonView({Details.class}) private String name; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) @JsonView({Details.class}) private String login; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) @JsonView({AdminDetails.class}) private String password; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) @Email(groups = {New.class}) @JsonView({Details.class}) private String email;
      
      





必要なコントローラーメソッドをマークするために残ります。



  @JsonView(Details.class) @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> create(@Validated(UserDto.New.class) @RequestBody UserDto dto) { return new ResponseEntity<>(service.save(dto), HttpStatus.OK); } @JsonView(Details.class) @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserDto> updateName(@Validated(UserDto.UpdateName.class) @RequestBody UserDto dto) { return new ResponseEntity<>(service.update(dto), HttpStatus.OK); }
      
      





また、別の場合には、@ JsonView(AdminDetails.class)にパスワードのみをプルするメソッドに注釈を付けます。 パスワードだけでなくすべての情報を管理者に送信する場合は、必要なすべてのフィールドに適宜注釈を付けます。



  @Null(groups = {New.class}) @NotNull(groups = {UpdateName.class}) @JsonView({Details.class, AdminDetails.class}) private Long id; @NotNull(groups = {New.class, UpdateName.class}) @JsonView({Details.class, AdminDetails.class}) private String name; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) @JsonView({Details.class, AdminDetails.class}) private String login; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) @JsonView({AdminDetails.class}) private String password; @NotNull(groups = {New.class}) @Null(groups = {UpdateName.class}) @Email(groups = {New.class}) @JsonView({Details.class, AdminDetails.class}) private String email;
      
      





この記事が、入力DTOの検証とこの出力の可視性の理解に役立つことを願っています。



All Articles