Spring Data RESTバリデーターガイド
データ]
-
リンク:/tag/spring-data-rest/[春のデータREST]
1概要
この記事では、Spring Data REST Validatorの基本的な紹介を扱います。
もしあなたが最初にSpring Data RESTの基礎を学ぶ必要があるなら、間違いなく
この記事
にアクセスして基礎を磨いてください。
簡単に言うと、Spring Data RESTでは、REST APIを通じてデータベースに新しいエントリを追加するだけですが、もちろん実際にデータを永続化する前にデータが有効であることを確認する必要もあります。
この記事はリンクに続けてあります:/spring-data-rest-intro[既存の記事]そして我々はそこで設定した既存のプロジェクトを再利用します。
2
Validators
を使う
Spring 3以降、このフレームワークは
Validator
インタフェースを備えています。これはオブジェクトの検証に使用できます。
** 2.1. 動機
**
前回の記事では、
name
と
email
という2つのプロパティを持つエンティティを定義しました。
したがって、新しいリソースを作成するには、単に実行する必要があります。
curl -i -X POST -H "Content-Type:application/json" -d
'{ "name" : "Test", "email" : "[email protected]" }'
http://localhost:8080/users
このPOSTリクエストは提供されたJSONオブジェクトを私たちのデータベースに保存し、オペレーションは戻ります:
{
"name" : "Test",
"email" : "[email protected]",
"__links" : {
"self" : {
"href" : "http://localhost:8080/users/1"
},
"websiteUser" : {
"href" : "http://localhost:8080/users/1"
}
}
}
我々が有効なデータを提供したので、良い結果が期待された。しかし、プロパティ
name
を削除するか、単に値を空の
String
に設定するとどうなりますか?
最初のシナリオをテストするには、空の文字列をプロパティ
name
の値として設定する前から、変更したコマンドを実行します。
curl -i -X POST -H "Content-Type:application/json" -d
'{ "name" : "", "email" : "Baggins" }' http://localhost:8080/users
そのコマンドにより、次のような応答が得られます。
{
"name" : "",
"email" : "Baggins",
"__links" : {
"self" : {
"href" : "http://localhost:8080/users/1"
},
"websiteUser" : {
"href" : "http://localhost:8080/users/1"
}
}
}
2番目のシナリオでは、リクエストからプロパティ
name
を削除します。
curl -i -X POST -H "Content-Type:application/json" -d
'{ "email" : "Baggins" }' http://localhost:8080/users
そのコマンドに対して、次のような応答が得られます。
{
"name" : null,
"email" : "Baggins",
"__links" : {
"self" : {
"href" : "http://localhost:8080/users/2"
},
"websiteUser" : {
"href" : "http://localhost:8080/users/2"
}
}
}
ご覧のとおり、どちらの要求もOKで、201のステータスコードとAPIを使用してオブジェクトにリンクしていることを確認できます
。
データベースに部分的なデータを挿入するのを避けたいので、この動作は許容できません。
2.2. Spring Data RESTイベント
Spring Data REST APIを呼び出すたびに、Spring Data RESTエクスポーターは以下にリストされているさまざまなイベントを生成します。
-
BeforeCreateEvent
-
AfterCreateEvent
-
BeforeSaveEvent
-
AfterSaveEvent
-
BeforeLinkSaveEvent
-
AfterLinkSaveEvent
-
BeforeDeleteEvent
-
AfterDeleteEvent
すべてのイベントは同様の方法で処理されるため、新しいオブジェクトがデータベースに保存される前に生成される
beforeCreateEvent
の処理方法についてのみ説明します。
2.3.
Validator
を定義する
独自のバリデータを作成するには、
supports
および
validate
メソッドを使って
org.springframework.validation.Validator
インターフェースを実装する必要があります。
Supports
はバリデータが提供されたリクエストをサポートしているかどうかをチェックし、
validate
メソッドはリクエストで提供されたデータを検証します。
WebsiteUserValidator
クラスを定義しましょう。
public class WebsiteUserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return WebsiteUser.class.equals(clazz);
}
@Override
public void validate(Object obj, Errors errors) {
WebsiteUser user = (WebsiteUser) obj;
if (checkInputString(user.getName())) {
errors.rejectValue("name", "name.empty");
}
if (checkInputString(user.getEmail())) {
errors.rejectValue("email", "email.empty");
}
}
private boolean checkInputString(String input) {
return (input == null || input.trim().length() == 0);
}
}
Errors
オブジェクトは、
validate
メソッドで提供されるすべてのエラーを含むように設計された特別なクラスです。この記事の後半では、
Errors
オブジェクトに含まれる提供されたメッセージを使用する方法を説明します。新しいエラーメッセージを追加するには、
errors.rejectValue(nameOfField、errorMessage)
を呼び出す必要があります。
バリデータを定義したら、リクエストを受け付けた後に生成される特定のイベントにマッピングする必要があります。
たとえば、私たちの場合、データベースに新しいオブジェクトを挿入したいので、
beforeCreateEvent
が生成されます。しかし、リクエスト内のオブジェクトを検証したいので、まずバリデータを定義する必要があります。
これには3つの方法があります。
-
名前で
Component
アノテーションを追加する
“
beforeCreateWebsiteUserValidator
”。 Spring Bootはキャッチしたいイベントを決定する接頭辞
beforeCreate
を認識し、また
Component
nameから
WebsiteUser
クラスを認識します。
@Component("beforeCreateWebsiteUserValidator") public class WebsiteUserValidator implements Validator { ... }
-
@ Bean
アノテーションを使用してアプリケーションコンテキストに
Bean
を作成します。
@Bean public WebsiteUserValidator beforeCreateWebsiteUserValidator() { return new WebsiteUserValidator(); }
-
手動登録:
@SpringBootApplication public class SpringDataRestApplication extends RepositoryRestConfigurerAdapter { public static void main(String[]args) { SpringApplication.run(SpringDataRestApplication.class, args); } @Override public void configureValidatingRepositoryEventListener( ValidatingRepositoryEventListener v) { v.addValidator("beforeCreate", new WebsiteUserValidator()); } }
-
** この場合は、に注釈は必要ありません。
WebsiteUserValidator
クラス。
2.4. イベント発見のバグ
現時点では、https://jira.spring.io/browse/DATAREST-524[バグデータがSpring Data RESTに存在します] – これはイベントの発見に影響します。
beforeCreate
イベントを生成するPOSTリクエストを呼び出した場合、このバグのためにイベントが検出されないため、アプリケーションはバリデータを呼び出しません。
この問題に対する簡単な回避策は、Spring Data RESTの
ValidatingRepositoryEventListener
クラスにすべてのイベントを挿入することです。
@Configuration
public class ValidatorEventRegister implements InitializingBean {
@Autowired
ValidatingRepositoryEventListener validatingRepositoryEventListener;
@Autowired
private Map<String, Validator> validators;
@Override
public void afterPropertiesSet() throws Exception {
List<String> events = Arrays.asList("beforeCreate");
for (Map.Entry<String, Validator> entry : validators.entrySet()) {
events.stream()
.filter(p -> entry.getKey().startsWith(p))
.findFirst()
.ifPresent(
p -> validatingRepositoryEventListener
.addValidator(p, entry.getValue()));
}
}
}
3テスト中
-
2.1節** では、バリデータがなくてもnameプロパティを持たないオブジェクトをデータベースに追加できることを示しました。これはデータの整合性をチェックしないため、望ましい動作ではありません。
name
プロパティを使わずにバリデータを指定して同じオブジェクトを追加したい場合、このエラーが発生します。
curl -i -X POST -H "Content-Type:application/json" -d
'{ "email" : "[email protected]" }' http://localhost:8080/users
{
"timestamp":1472510818701,
"status":406,
"error":"Not Acceptable",
"exception":"org.springframework.data.rest.core.
RepositoryConstraintViolationException",
"message":"Validation failed",
"path":"/users"
}
ご覧のとおり、リクエストから欠落データが検出され、オブジェクトはデータベースに保存されませんでした。私たちの要求は500のHTTPコードと内部エラーのメッセージで返されました。
エラーメッセージは、私たちの要求の問題については何も言いません。
もっと情報を提供したいのであれば、レスポンスオブジェクトを修正する必要があります。
/Springでの例外処理[Springでの例外処理]のリンクで、フレームワークによって生成された例外を処理する方法を示したので、この時点では間違いなく優れた記事です。
私たちのアプリケーションは
RepositoryConstraintViolationException
例外を生成するので、応答メッセージを修正するこの特定の例外のためのハンドラを作成します。
これが私たちの
__RestResponseEntityExceptionHandle
__rクラスです。
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends
ResponseEntityExceptionHandler {
@ExceptionHandler({ RepositoryConstraintViolationException.class })
public ResponseEntity<Object> handleAccessDeniedException(
Exception ex, WebRequest request) {
RepositoryConstraintViolationException nevEx =
(RepositoryConstraintViolationException) ex;
String errors = nevEx.getErrors().getAllErrors().stream()
.map(p -> p.toString()).collect(Collectors.joining("\n"));
return new ResponseEntity<Object>(errors, new HttpHeaders(),
HttpStatus.PARTIAL__CONTENT);
}
}
このカスタムハンドラでは、私たちのreturnオブジェクトはすべての検出されたエラーに関する情報を持ちます。
4結論
この記事では、バリデーターがデータ挿入のための追加のセキュリティー層を提供するすべてのSpring Data REST APIに不可欠であることを示しました。
また、アノテーションを使用して新しいバリデータを作成するのがいかに簡単かを説明しました。
いつものように、このアプリケーションのコードはhttps://github.com/eugenp/tutorials/tree/master/spring-data-rest[GitHubプロジェクト]にあります。