ノードのエラー処理.JSエクスプレス-パート2



Github repository
なぜエラー処理が必要ですか?
クライアントアプリケーション(Web、モバイル…)を想像してくださいはサーバの使用です.時々、アプリケーションで表示される例外を処理する必要があります、そして、我々は何が間違っているかについて、明確なメッセージをユーザーに送る必要があります.より良いユーザーエクスペリエンスを達成するために、また、他の多くの理由のために、アプリケーション内で働くエラーハンドラを持つことは非常に重要です.ユーザーエクスペリエンスの横には、1つの場所でエラーをキャッチするのは良い習慣です(すべてのエラーがハンドラを通過)ので、開発者は、バグ/例外をより簡単に追跡できます.

例外の作成


例外はthrow アプリケーション内のキーワード.
throw Error('Error');
アプリケーションがこの行を実行すると、通常のフローが停止し、コントロールが最寄りの例外ハンドラーに切り替えられます.いくつかの他の環境では、ノード内の文字列、オブジェクトなどをスローすることができます.ジェイスローerror objects . エラーオブジェクトはError またはError 自身.
からエラーをスローするError オブジェクトは以下のようになります:
class SomethingIsWrongError extends Error {
  constructor() {
    super('Something is wrong!');
  }
}
throw new SomethingIsWrongError();
エラーハンドラを作成する前に、正しい方法を決める必要があります.私のアプリケーションのほとんどは、メッセージがユーザが選択した言語に翻訳される必要があることを意味する複数の言語をサポート/サポートしています.エラーメッセージがどこかに翻訳される必要があることを意味する日本語を選択した場合、英語でエラーを表示できません.いずれかのサーバー側またはクライアント側のメッセージを翻訳します.
  • サーバ側の翻訳
    サーバー側でメッセージを翻訳するために、我々はユーザーから選択された言語を得るために、我々が例外を送っているのを知っていなければなりません.このアプローチの課題は、エラーメッセージがクライアント側に送信される必要があるときはいつでも、開発者がユーザの選択言語を常に必要とすることです.
  • クライアント側の翻訳
    例外の翻訳は、コードに基づいてクライアント側で行う必要がありますし、これは私が好むソリューションです.
  • クライアント側は知る必要があります.
  • ステータスコード.
  • ユニークなエラーコード.すべてのエラーは独自のコードがあります.
  • メタデータがあれば.何か追加のダイナミックなデータが最大許容できる入力番号などのようなメッセージを翻訳するために送られる必要があるならば.
  • より簡単にすべてのエラーを追跡するために、我々は我々が我々が知っているすべての可能な誤りを格納するクラスをつくる必要があります.例外をスローすると、そのクラスで見つかったコードの1つを参照します.
    フォルダを作成するerror-handler ルートディレクトリでこれはエラーハンドラロジック用のファイルを作成する場所になります.ファイルを作成するerror-code.ts 次のコードを使用します.
    export class ErrorCode {
      public static readonly Unauthenticated = 'Unauthenticated';
      public static readonly NotFound = 'NotFound';
      public static readonly MaximumAllowedGrade = 'MaximumAllowedGrade';
      public static readonly AsyncError = 'AsyncError';
      public static readonly UnknownError = 'UnknownError';
    }
    
    また、クライアントに戻るモデルも必要です.ファイルを作成するerror-model.ts 内部error-handler 次のコードを含むフォルダ
    export class ErrorModel {
      /**
       * Unique error code which identifies the error.
       */
      public code: string;
      /**
       * Status code of the error.
       */
      public status: number;
      /**
       * Any additional data that is required for translation.
       */
      public metaData?: any;
    }
    
    また、実際のエラー例外オブジェクトを作成する必要があります.ファイルを作成するerror-exception.ts 内部error-handler 次のコードを含むフォルダ
    import { ErrorCode } from './error-code';
    
    export class ErrorException extends Error {
      public status: number = null;
      public metaData: any = null;
      constructor(code: string = ErrorCode.UnknownError, metaData: any = null) {
        super(code);
        Object.setPrototypeOf(this, new.target.prototype);
        this.name = code;
        this.status = 500;
        this.metaData = metaData;
        switch (code) {
          case ErrorCode.Unauthenticated:
            this.status = 401;
            break;
          case ErrorCode.MaximumAllowedGrade:
            this.status = 400;
            break;
          case ErrorCode.AsyncError:
            this.status = 400;
            break;
          case ErrorCode.NotFound:
            this.status = 404;
            break;
          default:
            this.status = 500;
            break;
        }
      }
    }
    
    我々は我々のアプリケーションからエラーをスローするときに我々は正確にコードを使用可能なリストから作成したクラスと1つのコードを使用します.以下のようなエラーをスローします.
    throw new ErrorException(ErrorCode.MaximumAllowedGrade, { max: 100 }); // object is optional
    

    エラーハンドラ


    エラーハンドラーは、ノードの特別なミドルウェアです.4つのパラメータを取るjs.定期的なルートミドルウェアは3パラメータを取ります:req、resおよびnext.エラーハンドラーもこれらの3つのパラメータと1つの追加パラメータをとります.これらの4つのパラメータは(遡及的に)
  • ERR
  • レーク
  • レス
  • ファイル名error-handler.ts 内部error-handler フォルダ.次のハンドラは、アプリケーションで発生するすべてのエラーを傍受します.それが我々自身によって投げられる例外であると認めるために、我々はインスタンスのタイプによってそれを認識することができますif (err instanceof ErrorException)
    import { Request, Response, NextFunction } from 'express';
    import { ErrorCode } from './error-code';
    import { ErrorException } from './error-exception';
    import { ErrorModel } from './error-model';
    
    export const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
      console.log('Error handling middleware called.');
      console.log('Path:', req.path);
      console.error('Error occured:', err);
      if (err instanceof ErrorException) {
        console.log('Error is known.');
        res.status(err.status).send(err);
      } else {
        // For unhandled errors.
        res.status(500).send({ code: ErrorCode.UnknownError, status: 500 } as ErrorModel);
      }
    };
    
    さて、このハンドラを登録する必要があります.ハンドラは、すべてのルートと他のミドルウェアとハンドラーの後、アプリケーションで可能な限り「低下します」ことを必要とします.登録の後、ルートまたはミドルウェアを指定するならばerrorHandler エラーハンドラはこれらのルートやミドルウェアに現れる例外をキャッチしません.
    app.use(errorHandler); // registration of handler
    
    app.listen(3000, () => {
      console.log('Application started on port 3000!');
    });
    
    これでエラーが発生します.
    app.get('/throw-unauthenticated', (req: Request, res: Response, next: NextFunction) => {
      throw new ErrorException(ErrorCode.Unauthenticated);
      // or
      // next(new ErrorException(ErrorCode.Unauthenticated))
    });
    app.get('/throw-maximum-allowed-grade', (req: Request, res: Response, next: NextFunction) => {
      throw new ErrorException(ErrorCode.MaximumAllowedGrade, { grade: Math.random() });
      // or
      // next(new ErrorException(ErrorCode.MaximumAllowedGrade, { grade: Math.random() }))
    });
    app.get('/throw-unknown-error', (req: Request, res: Response, next: NextFunction) => {
      const num: any = null;
      // Node.js will throw an error because there is no length property inside num variable
      console.log(num.length);
    });
    
    上記のコードを見ると、2を持つことがわかりますknown 例外と1unknown . 我々がルートから例外を投げるとき、我々はそれをすることができますthrow キーワードまたはnext 関数は、実際の例外です.エラーハンドラは両方の例外をキャッチします.しかし、それが非同期論理になるとき、それは我々が次にカバーするもう一つの方法で解決されます.

    約束の例外


    コメントによって
    同期コードの処理Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it. For example:
    app.get('/', function (req, res) {
      throw new Error('BROKEN'); // Express will catch this on its own.
    });
    
    非同期コードの処理For errors returned from asynchronous functions invoked by route handlers and middleware, you must pass them to the next() function, where Express will catch and process them. For example:
    app.get('/', function (req, res, next) {
      fs.readFile('/file-does-not-exist', function (err, data) {
        if (err) {
          next(err); // Pass errors to Express.
        } else {
          res.send(data);
        }
      });
    });
    
    Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error. For example:
    app.get('/user/:id', async function (req, res, next) {
      // if error appears in getUserById, express will automatically throw an error
      const user = await getUserById(req.params.id);
      res.send(user);
    });
    
    非同期コードのアプリケーションにコードを追加しましょう.コードは常にエラーをスローし、我々はExpress 4またはExpress 5を使用している場合は、アプリケーションがエラーをキャッチします.
    const someOtherFunction = () => {
      const myPromise = new Promise((resolve, reject) => {
        setTimeout(() => {
          reject(new ErrorException(ErrorCode.AsyncError));
        }, 1000);
      });
      return myPromise;
    };
    app.get('/throw-async-await-error', async (req: Request, res: Response, next: NextFunction) => {
      // express 4
      try {
        await someOtherFunction();
      } catch (err) {
        next(err);
        // next line will not work as expected
        // throw err
      }
      // express 5
      // await someOtherFunction();
    });
    

    ラッピング


    このチュートリアルでは、例外とは、アプリケーションで例外をスローする方法を取り上げました.私たちは、多言語アプリケーションで例外を扱うとき、我々が考慮する必要があることを学びました.我々は、ノードに必要なすべてをすることを学びました.JSアプリケーションを正常にハンドラを作成し、それを登録するために必要なクラスを作成する例外を管理する.最後に、例外をスローする方法と、asyncやsyncブロックの例外をスローするときの注意点を学びました.
    JWTとの認証