REST API を提供するサービスの 1 つへのアクセスを簡素化するために、Java SDK を構築しています。この SDK は、サードパーティの開発者が使用するものです。Java 言語により適した SDK でエラー処理を実装するための最適なパターンを見つけるのに苦労しています。
残りのエンドポイントがあるとしましょう: GET /photos/{photoId}
. これにより、次の HTTP ステータス コードが返される場合があります。
- 401 : ユーザーは認証されていません
- 403 : ユーザーにはこの写真にアクセスする権限がありません
- 404 : その ID の写真はありません
サービスは次のようになります。
interface RestService {
public Photo getPhoto(String photoID);
}
上記のコードでは、まだエラー処理に対処していません。私は明らかに、sdk のクライアントがどのエラーが発生したかを知り、それから回復できるようにする方法を提供したいと考えています。Java でのエラー処理は Exceptions を使用して行われるので、それで行きましょう。ただし、例外を使用してこれを行う最善の方法は何ですか?
1. エラーに関する情報を含む 1 つの例外を設定します。
public Photo getPhoto(String photoID) throws RestServiceException;
public class RestServiceException extends Exception {
int statusCode;
...
}
SDK のクライアントは、次のようなことができます。
try {
Photo photo = getPhoto("photo1");
}
catch(RestServiceException e) {
swtich(e.getStatusCode()) {
case 401 : handleUnauthenticated(); break;
case 403 : handleUnauthorized(); break;
case 404 : handleNotFound(); break;
}
}
ただし、主に2つの理由から、このソリューションはあまり好きではありません。
- メソッドのシグネチャを見ると、開発者はどのようなエラー状況を処理する必要があるかわかりません。
- 開発者は、HTTP ステータス コードを直接処理し、このメソッドのコンテキストでの意味を知る必要があります (明らかに、正しく使用されている場合、多くの場合、意味はわかっていますが、常にそうであるとは限りません)。
2. エラーのクラス階層を持つ
メソッドのシグネチャはそのままです。
public Photo getPhoto(String photoID) throws RestServiceException;
ただし、エラーの種類ごとに例外を作成します。
public class UnauthenticatedException extends RestServiceException;
public class UnauthorizedException extends RestServiceException;
public class NotFoundException extends RestServiceException;
SDK のクライアントは、次のようなことを行うことができます。
try {
Photo photo = getPhoto("photo1");
}
catch(UnauthenticatedException e) {
handleUnauthorized();
}
catch(UnauthorizedException e) {
handleUnauthenticated();
}
catch(NotFoundException e) {
handleNotFound();
}
このアプローチでは、開発者はエラーを生成した HTTP ステータス コードについて知る必要がなく、Java 例外を処理するだけで済みます。もう 1 つの利点は、開発者が処理したい例外のみをキャッチできることです (単一の Exception ( RestServiceException
) をキャッチしてから、それを処理するかどうかを決定する必要がある以前の状況とは異なります)。
ただし、まだ 1 つの問題があります。メソッドのシグネチャを見ると、開発者は、メソッドのシグネチャにスーパークラスしかないため、処理する必要があるエラーの種類についてまだわかりません。
3.エラーのクラス階層を作成し、メソッドのシグネチャにリストします
さて、ここで頭に浮かぶのは、メソッドのシグネチャを次のように変更することです。
public Photo getPhoto(String photoID) throws UnauthenticatedException, UnauthorizedException, NotFoundException;
ただし、将来、この残りのエンドポイントに新しいエラー状況が追加される可能性があります。これは、メソッドのシグネチャに新しい Exception を追加することを意味し、Java API への重大な変更になります。説明されている状況で API への重大な変更が発生しない、より堅牢なソリューションが必要です。
4.エラーのクラス階層を作成します(未チェックの例外を使用)+メソッドのシグネチャにそれらをリストします
では、チェックされていない例外についてはどうでしょうか。RestServiceException を変更して RuntimeException を拡張すると、次のようになります。
public class RestServiceException extends RuntimeException
そして、メソッドの署名を保持します:
public Photo getPhoto(String photoID) throws UnauthenticatedException, UnauthorizedException, NotFoundException;
このようにして、既存のコードを壊すことなく、メソッドのシグネチャに新しい例外を追加できます。ただし、このソリューションでは、開発者は例外をキャッチする必要がなく、ドキュメントを注意深く読むか (そうです!)、メソッドのシグネチャにある例外に気付くまで、処理する必要があるエラー状況があることに気付かないでしょう。 .
この種の状況でのエラー処理のベスト プラクティスは何ですか?
私が言及したものに代わる他の(より良い)代替手段はありますか?