GNU/Linux >> Belajar Linux >  >> Cent OS

Bagaimana Menerapkan Validasi untuk Layanan RESTful dengan Spring

Validasi Kacang Musim Semi

Validasi data bukanlah topik baru dalam pengembangan aplikasi web.

Kami melihat sekilas validasi data di ekosistem Java secara umum dan Spring Framework secara khusus. Platform Java telah menjadi standar de facto untuk mengimplementasikan validasi data yaitu spesifikasi Bean Validation. Spesifikasi Bean Validation memiliki beberapa versi:

  • 1.0 (JSR-303),
  • 1.1 (JSR-349),
  • 2.0 (JSR 380) – versi terbaru

Spesifikasi ini mendefinisikan satu set komponen, antarmuka, dan anotasi. Ini menyediakan cara standar untuk menempatkan batasan pada parameter dan mengembalikan nilai metode dan parameter konstruktor, menyediakan API untuk memvalidasi objek dan grafik objek.

Model Deklaratif digunakan untuk memberikan batasan berupa anotasi pada objek dan bidangnya. Ada anotasi standar seperti @NotNull , @Digits , @Pattern , @Email , @CreditCard . Ada kemampuan untuk membuat batasan kustom baru.

Validasi dapat berjalan secara manual atau lebih alami, ketika spesifikasi dan kerangka kerja lain memvalidasi data pada waktu yang tepat, misalnya, pengguna memasukkan, menyisipkan, atau memperbarui di JPA.

Validasi dalam Contoh Java

Mari kita lihat bagaimana hal itu dapat dilakukan dalam praktik dalam contoh Validasi Kacang sederhana ini di dalam aplikasi Java biasa.

Kami memiliki objek yang ingin kami validasi dengan semua bidang yang dianotasi dengan batasan.

public class SimpleDto {

  @Min(value = 1, message = "Id can't be less than 1 or bigger than 999999")
  @Max(999999)
  private int id;

  @Size(max = 100)
  private String name;

  @NotNull
  private Boolean active;

  @NotNull
  private Date createdDatetime;

  @Pattern(regexp = "^asc|desc$")
  private String order = "asc";

  @ValidCategory(categoryType="simpleDto")
  private String category;
  …
  Constructor, getters and setters

Sekarang kita dapat menggunakannya dalam aplikasi Java sederhana dan memvalidasi objek secara manual.

public class SimpleApplication {

  public static void main(String[] args) {

    final SimpleDto simpleDto = new SimpleDto();
    simpleDto.setId(-1);
    simpleDto.setName("Test Name");
    simpleDto.setCategory("simple");
    simpleDto.setActive(true);
    simpleDto.setOrder("asc");
    simpleDto.setCreatedDatetime(new Date());

    ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
    Validator validator = validatorFactory.usingContext().getValidator();

    Set constrains = validator.validate(simpleDto);
    for (ConstraintViolation constrain : constrains) {

       System.out.println(
      "[" + constrain.getPropertyPath() + "][" + constrain.getMessage() + "]"
      );

    }

  }

}

Dan hasil di Console akan menjadi:

“[id] [Id can't be less than 1 or bigger than 999999]”

Batasan Validasi dan Anotasi

Buat batasan dan anotasi validasi khusus.


 @Retention(RUNTIME)
   @Target(FIELD)
   @Constraint(validatedBy = {ValidCategoryValidator.class})
   public @interface ValidCategory {

      String categoryType();

      String message() default "Category is not valid";

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

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

    }

And constraint validation implementation:

public class ValidCategoryValidator implements ConstraintValidator<ValidCategory, String> {

    private static final Map<String, List> availableCategories;

    static {

      availableCategories = new HashMap<>();
      availableCategories.put("simpleDto", Arrays.asList("simple", "advanced"));

    }

    private String categoryType;

    @Override
    public void initialize(ValidCategory constraintAnnotation) {

      this.setCategoryType(constraintAnnotation.categoryType());

    }

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

      List categories = ValidCategoryValidator.availableCategories.get(categoryType);
      if (categories == null || categories.isEmpty()) {

         return false;

      }

      for (String category : categories) {

         if (category.equals(value)) {

             return true;

      }

    }

    return false;

  }

}

Dalam contoh di atas, kategori yang tersedia berasal dari peta hash sederhana. Dalam kasus penggunaan aplikasi nyata, mereka dapat diambil dari database atau layanan lainnya.

Harap dicatat bahwa batasan dan validasi dapat ditentukan dan dilakukan tidak hanya pada tingkat bidang, tetapi juga pada seluruh objek.

Saat kita perlu memvalidasi berbagai dependensi bidang, misalnya, tanggal mulai, itu tidak boleh setelah tanggal akhir.

Implementasi Validasi Kacang yang paling banyak digunakan spesifikasinya adalah Validator Hibernasi dan Apache BVal .

Validasi dengan Spring

Kerangka kerja Spring menyediakan beberapa fitur untuk validasi.

  • Dukungan untuk API Validasi Kacang versi 1.0, 1.1 (JSR-303, JSR-349) diperkenalkan di Kerangka Musim Semi yang dimulai dengan versi 3.
  • Spring memiliki antarmuka Validatornya sendiri yang sangat mendasar dan dapat diatur dalam instance DataBinder tertentu. Ini bisa berguna untuk menerapkan logika validasi tanpa anotasi.

Validasi Kacang dengan Spring

Spring Boot menyediakan validasi yang dimulai yang dapat disertakan dalam proyek:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
 </dependency>

Starter ini menyediakan versi Validator Hibernasi kompatibel dengan Spring Boot saat ini.

Menggunakan Validasi Kacang, kami dapat memvalidasi badan permintaan, parameter kueri, variabel di dalam jalur (mis. / /simpledto/{id} ), atau metode atau parameter konstruktor apa pun.

Permintaan POST atau PUT

Dalam permintaan POST atau PUT, misalnya, kami melewati payload JSON, Spring secara otomatis mengubahnya menjadi objek Java dan sekarang kami ingin memvalidasi objek yang dihasilkan. Mari gunakan SimpleDto objek dari 1 contoh:

@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {

    @Autowired
    private SimpleDtoService simpleDtoService;

    @RequestMapping(path = "", method = RequestMethod.POST, produces = 
    "application/json")
    public SimpleDto createSimpleDto(

        @Valid @RequestBody SimpleDto simpleDto) {

      SimpleDto result = simpleDtoService.save(simpleDto);

      return result;

    }

}

Kami baru saja menambahkan @Valid anotasi ke SimpleDto parameter yang dianotasi dengan @RequestBody . Ini akan memberi tahu Spring untuk memproses validasi sebelum melakukan pemanggilan metode yang sebenarnya. Jika validasi gagal, Spring akan menampilkan MethodArgument NotValidException yang, secara default, akan mengembalikan respons 400 (Permintaan Buruk).

Validasi Variabel Jalur

Memvalidasi Variabel Jalur bekerja sedikit berbeda. Masalahnya adalah sekarang kita harus menambahkan anotasi batasan langsung ke parameter metode, bukan di dalam objek.

Untuk membuatnya berfungsi, ada 2 opsi yang memungkinkan:

Opsi 1:@Anotasi yang Divalidasi

Tambahkan @Validated anotasi ke pengontrol di tingkat kelas untuk mengevaluasi anotasi kendala pada parameter metode.

Opsi 2:Variabel Jalur

Gunakan objek yang mewakili variabel jalur seperti yang terlihat pada contoh di bawah ini:

@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {

    @Autowired
    private SimpleDtoService simpleDtoService;

    @RequestMapping(path = "/{simpleDtoId}", method = RequestMethod.GET, produces = 
    "application/json")
    public SimpleDto getSimpleDto(

      @Valid SimpleDtoIdParam simpleDtoIdParam) {

    SimpleDto result = simpleDtoService.findById(simpleDtoIdParam.getSimpleDtoId());

    if (result == null) {

      throw new NotFoundException();

    }

    return result;

  }

}

Dalam hal ini, kami memiliki SimpleDtoIdParam kelas yang berisi simpleDtoId bidang yang akan divalidasi terhadap anotasi batasan Bean standar atau kustom. Nama Variabel Jalur (/{simpleDtoId} ) harus sama dengan nama bidang (sehingga Spring akan dapat menemukan penyetel untuk bidang ini).

private static final long serialVersionUID = -8165488655725668928L;

    @Min(value = 1)
    @Max(999999)
    private int simpleDtoId;

    public int getSimpleDtoId() {
    return simpleDtoId;
    }

    public void setSimpleDtoId(int simpleDtoId) {
    this.simpleDtoId = simpleDtoId;
    }

}

Berbeda dengan Badan Permintaan validasi, Variabel Jalur validasi melempar ConstraintViolationException alih-alih MethodArgumentNotValidException . Oleh karena itu, kita perlu membuat penangan pengecualian khusus.

Kerangka kerja Java Spring juga memungkinkan memvalidasi parameter pada tingkat layanan dengan @Validated anotasi (tingkat kelas) dan @Valid (tingkat parameter).

Mengingat Spring JPA menggunakan Hibernate di bawahnya, ia juga mendukung Validasi Bean untuk kelas entitas. Harap dicatat bahwa mungkin bukan ide yang baik dalam banyak kasus mengandalkan tingkat validasi ini karena itu berarti bahwa semua logika sebelumnya berurusan dengan objek yang tidak valid.

Antarmuka Validasi Musim Semi

Spring mendefinisikan antarmukanya sendiri untuk Validator validasi (org.springframework.validation.Validator). Ini dapat disetel untuk instance DataBinder tertentu dan mengimplementasikan validasi tanpa anotasi (pendekatan non-deklaratif).

Untuk menerapkan pendekatan ini, kita perlu:

  1. Menerapkan Antarmuka Validator
  2. Tambahkan Validator

Menerapkan Antarmuka Validator

Implementasikan antarmuka Validator, misalnya mari bekerja dengan SimpleDto . kami kelas:

@Component
public class SpringSimpleDtoValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
    return SimpleDto.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {

      if (errors.getErrorCount() == 0) {

        SimpleDto param = (SimpleDto) target;
        Date now = new Date();
        if (param.getCreatedDatetime() == null) {

          errors.reject("100",

            "Create Date Time can't be null");

        } else if (now.before(param.getCreatedDatetime())) {

          errors.reject("101",

            "Create Date Time can't be after current date time");

        }

      }

    }

}

Periksa di sini apakah Datetime created yang dibuat stempel waktu ada di masa mendatang.

Tambahkan Validator

Tambahkan implementasi Validator ke DataBinder :

@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {

    @Autowired
    private SimpleDtoService simpleDtoService;

    @Autowired
    private SpringSimpleDtoValidator springSimpleDtoValidator;

    @InitBinder("simpleDto")
    public void initMerchantOnlyBinder(WebDataBinder binder) {
    binder.addValidators(springSimpleDtoValidator);
    }

    @RequestMapping(path = "", method = RequestMethod.POST, produces = 
    "application/json")
    public SimpleDto createSimpleDto(

      @Valid @RequestBody SimpleDto simpleDto) {

    SimpleDto result = simpleDtoService.save(simpleDto);
    return result;

  }

}

Sekarang kita memiliki SimpleDto divalidasi menggunakan anotasi batasan dan implementasi Validasi Musim Semi khusus kami.


Cent OS
  1. Cara Mengelola Layanan Systemd dengan Systemctl di Linux

  2. Bagaimana mengelola Layanan Linux dengan perintah systemctl

  3. Cara mengonfigurasi IMAP dengan SSL

  1. Cara memindai server Debian untuk rootkit dengan Rkhunter

  2. Bagaimana Cara Membuat A For Loop Dengan Jumlah Iterasi yang Dapat Diubah?

  3. Cara membuat ulang initramfs dan vmlinuz untuk Kernel Penyelamatan dengan Kernel Saat Ini di CentOS/RHEL 7

  1. Cara Menulis Ulang URL dengan mod_rewrite untuk Apache di Ubuntu 20.04

  2. Cara Mengatur Batas Sumber Daya untuk Proses dengan Systemd di CentOS/RHEL 7 dan 8

  3. Cara mengonfigurasi subdomain untuk layanan email SMTP transaksional