Spring BootでのJSONエラー処理

多くの場合、 Spring Bootテクノロジを使用して構築されたマイクロサービスを使用する場合、次のような標準エラー出力を確認できます。



{ "timestamp": 1510417124782, "status": 500, "error": "Internal Server Error", "exception": "com.netflix.hystrix.exception.HystrixRuntimeException", "message": "ApplicationRepository#save(Application) failed and no fallback available.", "path": "/application" }
      
      





このような結論は、サービスの不必要な顧客である可能性があります。 エラーが発生した場合にサードパーティのサービスの寿命を短縮したい場合は、この投稿で説明します。



1つのコントローラーで小さなサービスを構築することから始めます。 当社のサービスはユーザーのリクエストを受け入れ、成功した場合はユーザーにデータを提供します。 失敗した場合、エラーが返されます。 簡単なことから始めましょう。記事ではプロジェクトを改善します。



したがって、最初に必要なのはユーザーです。



 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class User { private int id; private String firstName; private String lastName; }
      
      





ここでは、 ロンボクライブラリを使用しました 。 抽象データは、クラス内のゲッターとセッターを置き換えます。 残りの注釈は、空のコンストラクターとパラメーター付きのコンストラクターを追加します。 IntelliJ Ideaでこの例を繰り返したい場合は、注釈処理を有効にするボックスをチェックするか、すべてを手書きで書く必要があります。



次に、サービスが必要です(簡潔にするため、リポジトリを作成しません)。



 import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; @Service public class UserService { private static final Map<Integer, User> userStorage = new HashMap<>(); static { userStorage.put(1, new User(1, "Petr", "Petrov")); userStorage.put(2, new User(2, "Ivan", "Ivanov")); userStorage.put(3, new User(3, "Sergei", "Sidorov")); } public User get(int id) { return userStorage.get(id); } }
      
      





そして、もちろん、コントローラー自体:



 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("user") public class UserController { private UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } @GetMapping("{id}") public User get(@PathVariable(name = "id") int id) { return userService.get(id); } }
      
      





そのため、ユーザー向けのほぼ完全なサービスを提供しています。 立ち上げて見ます。



localhost :8080 / user / 1 URLをリクエストすると、 jsonが次の形式で取得されます。



 { "id": 1, "firstName": "Petr", "lastName": "Petrov" }
      
      





すべてが素晴らしいです。 しかし、 localhost URLにリクエストを送信するとどうなりますか:8080 /ユーザー/ 4(ユーザーは3人のみ) 正解:ステータスに200が返され、回答には何も表示されません。 状況はあまり快適ではありません。 エラーはありませんが、要求されたオブジェクトもありません。



サービスを改善し、失敗した場合にエラーをスローするサービスを追加しましょう。 まず、例外を作成します。



 public class ThereIsNoSuchUserException extends RuntimeException { }
      
      





次に、サービスにスローするエラーを追加します。



 public User get(int id) { User user = userStorage.get(id); if (user == null) { throw new ThereIsNoSuchUserException(); } return user; }
      
      





サービスを再起動し、存在しないユーザーが要求されたときに何が起こるかをもう一度確認します。



 { "timestamp": 1510479979781, "status": 500, "error": "Internal Server Error", "exception": "org.faoxis.habrexception.ThereIsNoSuchUserException", "message": "No message available", "path": "/user/4" }
      
      





これはすでに優れています。 それははるかに有益であり、ステータスコードは200ではありません。クライアントは、このような状況をうまく簡単に処理できます。 しかし、彼らが言うように、1つの警告があります。 エラーは完全に異なる可能性があり、サービスのクライアントは多数の条件ステートメントを配置し、問題点とその修正方法を調査する必要があります。 それは私たちの側に少し失礼なことが判明しました。



そのような場合のために、ResponseStatusアノテーションが考案されました。 除外の代わりに置換し、実際にどのように機能するかを確認します。



 @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "There is no such user") public class ThereIsNoSuchUserException extends RuntimeException { }
      
      





クエリを繰り返して、結果を確認します。



 { "timestamp": 1510480307384, "status": 404, "error": "Not Found", "exception": "org.faoxis.habrexception.ThereIsNoSuchUserException", "message": "There is no such user", "path": "/user/4" }
      
      





いいね! ステータスコードとメッセージが変更されました。 これで、クライアントは、エラーの理由を応答コードで判断し、メッセージフィールドでそれを明確にすることもできます。 しかし、まだ問題があります。 クライアントは、ほとんどのフィールドを単に必要としない場合があります。 たとえば、別のフィールドとしての応答コードは、とにかく応答コードで取得されるため、冗長になる場合があります。 これで何かする必要があります。



幸いなことに、スプリングブートでは、エラー通知を成功させるための最後の一歩を踏み出すことはそれほど難しくありません。



必要なのは、いくつかの注釈と1つのクラスを解析することだけです。





これをすべて統合して、独自のエラーメッセージを作成する方法を見てみましょう。



 import lombok.AllArgsConstructor; import lombok.Data; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice public class AwesomeExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(ThereIsNoSuchUserException.class) protected ResponseEntity<AwesomeException> handleThereIsNoSuchUserException() { return new ResponseEntity<>(new AwesomeException("There is no such user"), HttpStatus.NOT_FOUND); } @Data @AllArgsConstructor private static class AwesomeException { private String message; } }
      
      







同じリクエストを行い、404レスポンスのステータスと単一のフィールドを持つメッセージを見てみましょう:



 { "message": "There is no such user" }
      
      





例外に対するResponseStatusアノテーションは安全に削除できます。



その結果、エラー処理が可能な限り柔軟かつ簡単に構成されたアプリケーションができました。 完全なプロジェクトは、 githubリポジトリにあります。 すべてがシンプルで明確であったことを願っています。 ありがとう、コメントを書いてください! あなたのコメントと説明に喜んでいます!



All Articles