コンテンツにスキップ

メッセージ管理機能の設定⚓︎

バックエンドのメッセージ管理方針に関するアーキテクチャについては、こちら をご確認ください。

設定方法⚓︎

本設定で利用するフォルダーの構成は以下の通りです。

root/ ------------------------------------------- root フォルダー
 ├ application-core/src/main/resource
 │ └ applicationcore ---------------------------- 業務メッセージのプロパティファイルを一元管理するフォルダー
 │    └ messages.properties --------------------- 業務メッセージのプロパティファイル
 └ system-common/src/main/resource
   └ systemcommon ------------------------------- 共通メッセージのプロパティファイルを一元管理するフォルダー
      └ messages.properties --------------------- 共通メッセージのプロパティファイル

プロパティファイルの作成⚓︎

メッセージに関するプロパティファイルは各サブプロジェクトの /src/main/resource/<サブプロジェクト名> フォルダーに集約します。 以下のように、メッセージ本体を格納するプロパティファイルを作成します。

message.properties
1
2
systemError=想定外のシステムエラーが発生しました
businessError=想定外の業務エラーが発生しました

プロパティファイルの読込⚓︎

以下のように、 web プロジェクトなどエントリーポイントとなるサブプロジェクトの application.properties にプロパティファイルを読み込む設定を記載します。

application.properties
1
spring.messages.basename=applicationcore.messages,systemcommon.messages

読み込むプロパティファイルは src/main/resource 配下の <フォルダー名>.<ファイル名> で指定します。 プロパティファイルが複数ある場合は、ファイルの間をカンマで区切ります。

メッセージの取得⚓︎

読み込んだプロパティファイルのメッセージを取得するためには、 MessageSource インターフェースを利用します。

以下は、プロパティファイルからメッセージを取得し、ログに出力するためのエラーメッセージを整形する ErrorMessageBuilder クラスの例です。

サンプルアプリケーションの ErrorMessageBuilder.java
ErrorMessageBuilder.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.dressca.web.log;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Locale;
import org.springframework.context.MessageSource;
import com.dressca.systemcommon.constant.SystemPropertyConstants;
import com.dressca.systemcommon.util.ApplicationContextWrapper;
import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * ログでエラーメッセージを作成するためのクラスです。
 */
@Getter
@AllArgsConstructor
public class ErrorMessageBuilder {

  private static final MessageSource messageSource = (MessageSource) ApplicationContextWrapper
      .getBean(MessageSource.class);

  private Exception ex;
  private String exceptionId;
  private String[] logMessageValue;
  private String[] frontMessageValue;

  /**
   * ProblemDetails の detail 情報に格納するスタックトレースを作成します。
   * 
   * @return スタックトレース。
   */
  public String createLogMessageStackTrace() {
    StringBuilder builder = new StringBuilder();
    String exceptionMessage = messageSource.getMessage(exceptionId, logMessageValue, Locale.getDefault());
    builder.append(exceptionId).append(" ").append(exceptionMessage).append(SystemPropertyConstants.LINE_SEPARATOR);
    StringWriter writer = new StringWriter();
    ex.printStackTrace(new PrintWriter(writer));
    builder.append(writer.getBuffer().toString());
    return builder.toString();
  }
}

また、 @Service@Controller@Component といった Bean 登録されたクラス内で MessageSource を利用する場合は、 @Autowired による DI で実装します。

以下は、プロパティファイルからエラーレスポンスに含めるメッセージを整形する ProblemDetailsFactory.java クラスの例です。

サンプルアプリケーションの ProblemDetailsFactory.java
ProblemDetailsFactory.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.dressca.web.controller.advice;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.stereotype.Component;
import com.dressca.web.constant.WebConstants;
import com.dressca.web.log.ErrorMessageBuilder;

/**
 * エラーレスポンスに含める ProblemDetails を作成するクラスです。
 */
@Component
public class ProblemDetailsFactory {

  @Autowired
  private Environment env;

  @Autowired
  private MessageSource messages;

  /**
   * エラーレスポンスに含める ProblemDetails を作成します。
   *
   * @param errorBuilder 例外ビルダー。
   * @param titleId      タイトルのメッセージ ID 。
   * @param status       ステータスコード。
   * @return エラーレスポンスに格納する ProblemDetails 。
   */
  public ProblemDetail createProblemDetail(ErrorMessageBuilder errorBuilder, String titleId, HttpStatus status) {

    ProblemDetail problemDetail = ProblemDetail.forStatus(status);

    problemDetail.setTitle(messages.getMessage(titleId, new String[] {}, Locale.getDefault()));

    // 開発環境においては、 detail プロパティにスタックトレースを含める
    // 開発環境かどうかの判断は、環境変数の Profile をもとに判断する
    String[] activeProfiles = env.getActiveProfiles();
    if (activeProfiles.length == 0) {
      activeProfiles = env.getDefaultProfiles();
    }

    if (Arrays.stream(activeProfiles).filter(profile -> Objects.equals(profile, "local"))
        .findFirst().isPresent()) {
      problemDetail.setDetail(errorBuilder.createLogMessageStackTrace());
    }

    // 拡張メンバーとして exceptionId と exceptionValues を含める
    Map<String, Object> errorProperty = new LinkedHashMap<String, Object>() {
      {
        put(WebConstants.PROBLEM_DETAILS_EXCEPTION_ID, errorBuilder.getExceptionId());
        put(WebConstants.PROBLEM_DETAILS_EXCEPTION_VALUES, errorBuilder.getFrontMessageValue());
      }
    };

    problemDetail.setProperties(errorProperty);

    return problemDetail;
  }
}