22
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

【Dart】例外クラスを自作する

Last updated at Posted at 2023-06-29

FlutterやDartでエラーハンドリングをする際、どのような方針で実装をしていますか?
基本的にはどの言語でも以下のようにtry-catchでハンドリングを行うと思います。

try {
  // 正常処理
} catch(e) {
  // 例外処理
}

しかし、上記のような単調なtry-catchではいかなるエラーもすべて同様の処理をしなくてはなりません。
つまり、アプリの中で想定しうるエラーと想定外エラーはしっかり線引きをしようというのがこの記事の目的です。

実装

下準備1. エラー情報を格納する抽象クラス

エラーメッセージでよくあるエラーコード・タイトル・メッセージを格納するクラスです

abstract class ErrorCode {
  const ErrorCode();

  /// エラーコード
  String get errorCode;

  /// タイトル
  String get errorTitle;

  /// メッセージ
  String get errorMessage;
}

下準備2. 共通のExceptionクラス

このクラスは直接使用しません。あくまで土台となくabstructクラスです

import 'package:custom_exception/exception/error_code.dart';

abstract class CustomException implements Exception {
  const CustomException(
    this.errorCode, {
    this.info,
  });

  final ErrorCode errorCode;
  final dynamic info;

  @override
  String toString() {
    return 'CustomException{errorCode: ${errorCode.errorCode}, title: ${errorCode.errorTitle}, message: ${errorCode.errorMessage}, info: $info}';
  }
}

実装1. サーバー共通エラーの定義

下準備で実装したErrorCodeenumに実装します。

/// サーバー共通エラーコード
enum ServerCommonErrorCode implements ErrorCode {
  systemError(
    'ER001',
    'システムエラー',
    'エラーが発生しました。しばらくしてもう一度お試しください。\n\n何度か試しても改善しない場合はアプリを再起動してください。',
  ),
  // 400エラー
  badRequestError(
    'ER002',
    'リクエストエラー',
    'リクエストが不正です。',
  ),
  // 403エラー
  forbiddenError(
    'ER003',
    'アクセス権限エラー',
    'アクセス権限がありません。',
  ),
  // 500エラー
  internalServerError(
    'ER004',
    'システムエラー',
    'エラーが発生しました。しばらくしてもう一度お試しください。\n\n何度か試しても改善しない場合はアプリを再起動してください。',
  ),
  ;

  const ServerCommonErrorCode(
    this._errorCode,
    this._errorTitle,
    this._errorMessage,
  );

  final String _errorCode;
  final String _errorTitle;
  final String _errorMessage;

  @override
  String get errorCode => _errorCode;

  @override
  String get errorTitle => _errorTitle;

  @override
  String get errorMessage => _errorMessage;

  static ServerCommonErrorCode? fromCode(String errorCode) =>
      values.firstWhereOrNull((element) => element.errorCode == errorCode);
}

実装2. カスタムExceptionクラスの実装

土台となるCustomExceptionクラスと上記で実装したServerCommonErrorCodeを使ってカスタムExceptionクラスを実装します。

class ServerCommonError extends CustomException {
  const ServerCommonError(
    ServerCommonErrorCode errorCode, {
    dynamic info,
  }) : super(errorCode, info: info);

  // factoryでエラーから生成する
  factory ServerCommonError.fromCode(String errorCode) {
    final errorInfo = ServerCommonErrorCode.fromCode(errorCode);
    // 取得に失敗した場合、一律システムエラーとする
    if (errorInfo == null) {
      throw const ServerCommonError(ServerCommonErrorCode.systemError);
    }
    return ServerCommonError(errorInfo);
  }
}

実際に使ってみよう

さて、実際に上記で実装したServerCommonErrorクラスを使用してみます。
今回は同じレイヤーで実装してしまいますが、実際はrepositoryやserviceクラスなどでハンドリングする方針が好ましいかなと思います。

お試して、PokeAPIを用いてAPIリクエストをした場合を想定して実装します。

  Future<void> fetchPikachuInfo() async {
    try {
      final Uri requestUrl =
          Uri.parse("https://pokeapi.co/api/v2/pokemon/pikachu");
      final response = await http.get(requestUrl);
      // ステータスコード別に例外を返す
      switch (response.statusCode) {
        case 200:
          // 200 OK
          debugPrint(response.toString());
          break;
        case 400:
        case 403:
          // 403 Forbidden
          throw const ServerCommonError(ServerCommonErrorCode.forbiddenError);
        case 500:
          // 500 Internal Server Error
          throw const ServerCommonError(
              ServerCommonErrorCode.internalServerError);
        default:
          // その他のステータスコード
          throw const ServerCommonError(ServerCommonErrorCode.systemError);
      }
    } on ServerCommonError catch (errorInfo) {
      // サーバー共通エラーの場合、定義したエラー情報を取得できる
      debugPrint(errorInfo.errorCode.toString());
      debugPrint(errorInfo.info.toString());
    } on Exception catch (_, stack) {
      debugPrint(stack.toString());
    } catch (error) {
      debugPrint(error.toString());
    }
  }

Github

22
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?