ジャージーでのBean検証

ジャージーでの豆検証

1. 概要

このチュートリアルでは、オープンソースフレームワークJerseyを使用したBeanValidationを見ていきます。

以前の記事ですでに見たように、Jersey is an open source framework for developing RESTful Web Services.create an API with Jersey and Spring.の方法の紹介で、ジャージーの詳細を確認できます。

2. ジャージーでの豆検証

Validation is the process of verifying that some data obeys one or more pre-defined constraints。 もちろん、ほとんどのアプリケーションで非常に一般的な使用例です。

Java BeanValidationフレームワーク(JSR-380)は、Javaでこの種の操作を処理するための事実上の標準になりました。 Java Bean Validationの基本を要約するには、以前のtutorialを参照してください。

Jersey contains an extension module to support Bean Validation。 アプリケーションでこの機能を使用するには、まず設定する必要があります。 次のセクションでは、アプリケーションを構成する方法を説明します。

3. アプリケーションのセットアップ

それでは、優れたJersey MVC Supportの記事の簡単なFruitAPIの例に基づいて構築しましょう。

3.1. Mavenの依存関係

まず、Bean Validationの依存関係をpom.xmlに追加しましょう。


    org.glassfish.jersey.ext
    jersey-bean-validation
    2.27

Maven Centralから最新バージョンを取得できます。

3.2. サーバーの構成

ジャージーでは、通常、カスタムリソース構成クラスで使用する拡張機能を登録します。

ただし、Bean検証拡張機能の場合、この登録を行う必要はありません。 Fortunately, this is one of the few extensions that the Jersey framework registers automatically.

最後に、検証エラーをクライアントwe’ll add a server property to our a custom resource configurationに送信するには:

public ViewApplicationConfig() {
    packages("com.example.jersey.server");
    property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}

4. JAX-RSリソースメソッドの検証

このセクションでは、制約アノテーションを使用して入力パラメーターを検証する2つの異なる方法を説明します。

  • 組み込みのBean Validation API制約の使用

  • カスタム制約とバリデーターの作成

4.1. 組み込みの制約アノテーションの使用

まず、組み込みの制約アノテーションを見てみましょう。

@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void createFruit(
    @NotNull(message = "Fruit name must not be null") @FormParam("name") String name,
    @NotNull(message = "Fruit colour must not be null") @FormParam("colour") String colour) {

    Fruit fruit = new Fruit(name, colour);
    SimpleStorageService.storeFruit(fruit);
}

この例では、2つのフォームパラメータnamecolourを使用して新しいFruitを作成します。 Bean Validation APIの一部である@NotNullアノテーションを使用します。

これにより、フォームパラメータに単純な非null制約が課せられます。 In case one of the parameters is null, the message declared within the annotation will be returned

当然、単体テストでこれを示します。

@Test
public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() {
    Form form = new Form();
    form.param("name", "apple");
    form.param("colour", null);
    Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED)
        .post(Entity.form(form));

    assertEquals("Http Response should be 400 ", 400, response.getStatus());
    assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null"));
}

上記の例では、we use the JerseyTest support class to test our fruit resourceです。 nullcolourでPOSTリクエストを送信し、応答に予期されたメッセージが含まれていることを確認します。

組み込みの検証制約のリストについては、at the docsを参照してください。

4.2. カスタム制約アノテーションの定義

より複雑な制約を課す必要がある場合があります。 We can do this by defining our own custom annotation.

簡単なFruitAPIの例を使用して、すべてのフルーツに有効なシリアル番号があることを検証する必要があると想像してみましょう。

@PUT
@Path("/update")
@Consumes("application/x-www-form-urlencoded")
public void updateFruit(@SerialNumber @FormParam("serial") String serial) {
    //...
}

この例では、パラメータserialは、次に定義する@SerialNumberで定義された制約を満たす必要があります。

まず、制約アノテーションを定義します。

@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { SerialNumber.Validator.class })
    public @interface SerialNumber {

    String message()

    default "Fruit serial number is not valid";

    Class[] groups() default {};

    Class[] payload() default {};
}

次に、バリデータークラスSerialNumber.Validatorを定義します。

public class Validator implements ConstraintValidator {
    @Override
    public void initialize(SerialNumber serial) {
    }

    @Override
    public boolean isValid(String serial,
        ConstraintValidatorContext constraintValidatorContext) {

        String serialNumRegex = "^\\d{3}-\\d{3}-\\d{4}$";
        return Pattern.matches(serialNumRegex, serial);
    }
}

The key point here is the Validator class must implement ConstraintValidator where T is the type of value we want to validate, in our case a String

Finally, we then implement our custom validation logic in the isValid method

5. リソースの検証

Furthermore, the Bean Validation API also allows us to validate objects using the @Valid annotation

次のセクションでは、この注釈を使用してリソースクラスを検証する2つの異なる方法について説明します。

  • まず、リクエストのリソース検証

  • 第二に、応答リソースの検証

Fruitオブジェクトに@Minアノテーションを追加することから始めましょう。

@XmlRootElement
public class Fruit {

    @Min(value = 10, message = "Fruit weight must be 10 or greater")
    private Integer weight;
    //...
}

5.1. リソース検証のリクエスト

まず、FruitResourceクラスで@Validを使用して検証を有効にします。

@POST
@Path("/create")
@Consumes("application/json")
public void createFruit(@Valid Fruit fruit) {
    SimpleStorageService.storeFruit(fruit);
}

上記の例では、if we try to create a fruit with a weight less than 10 we will get a validation error.

5.2. 応答リソースの検証

同様に、次の例では、応答リソースを検証する方法を確認します。

@GET
@Valid
@Produces("application/json")
@Path("/search/{name}")
public Fruit findFruitByName(@PathParam("name") String name) {
    return SimpleStorageService.findByName(name);
}

同じ@Validアノテーションをどのように使用するかに注意してください。 But this time we use it at the resource method level to be sure the response is valid.

6. カスタム例外ハンドラ

この最後のパートでは、カスタム例外ハンドラーを作成する方法について簡単に説明します。 This is useful when we want to return a custom response if we violate a particular constraint.

FruitExceptionMapperを定義することから始めましょう:

public class FruitExceptionMapper implements ExceptionMapper {

    @Override
    public Response toResponse(ConstraintViolationException exception) {
        return Response.status(Response.Status.BAD_REQUEST)
            .entity(prepareMessage(exception))
            .type("text/plain")
            .build();
    }

    private String prepareMessage(ConstraintViolationException exception) {
        StringBuilder message = new StringBuilder();
        for (ConstraintViolation cv : exception.getConstraintViolations()) {
            message.append(cv.getPropertyPath() + " " + cv.getMessage() + "\n");
        }
        return message.toString();
    }
}

まず、カスタム例外マッピングプロバイダーを定義します。 これを行うために、ConstraintViolationExceptionを使用してExceptionMapperインターフェースを実装します。

Hence, we’ll see that when this exception is thrown the toResponse method of our custom exception mapper instance will be invoked

また、この単純な例では、すべての違反を反復処理し、応答で返される各プロパティとメッセージを追加します。

Next, in order to use our custom exception mapper we need to register our provider

@Override
protected Application configure() {
    ViewApplicationConfig config = new ViewApplicationConfig();
    config.register(FruitExceptionMapper.class);
    return config;
}

最後に、無効なFruitを返すエンドポイントを追加して、動作中の例外ハンドラーを表示します。

@GET
@Produces(MediaType.TEXT_HTML)
@Path("/exception")
@Valid
public Fruit exception() {
    Fruit fruit = new Fruit();
    fruit.setName("a");
    fruit.setColour("b");
    return fruit;
}

7. 結論

要約すると、このチュートリアルでは、Jersey Bean Validation API拡張機能について説明しました。

最初に、ジャージーでBean Validation APIを使用する方法を紹介することから始めました。 また、サンプルのWebアプリケーションを構成する方法を調べました。

最後に、Jerseyで検証を行ういくつかの方法と、カスタム例外ハンドラーの作成方法を調べました。

いつものように、記事の完全なソースコードはover on GitHubで入手できます。