例外

次の例に示すように、@Controller クラスおよび @ControllerAdvice クラスには、コントローラーメソッドからの例外を処理する @ExceptionHandler メソッドを含めることができます。

  • Java

  • Kotlin

import java.io.IOException;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;

@Controller
public class SimpleController {

	@ExceptionHandler(IOException.class)
	public ResponseEntity<String> handle() {
		return ResponseEntity.internalServerError().body("Could not read file storage");
	}

}
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ExceptionHandler
import java.io.IOException

@Controller
class SimpleController {

	@ExceptionHandler(IOException::class)
	fun handle() : ResponseEntity<String> {
		return ResponseEntity.internalServerError().body("Could not read file storage")
	}
	
}

例外マッピング

例外は、伝播されるトップレベルの例外 (たとえば、直接スローされる IOException ) またはラッパー例外内のネストされた原因 (たとえば、IllegalStateException 内にラップされた IOException ) と一致する可能性があります。5.3 以降では、任意の原因レベルで一致できますが、以前は直接の原因のみが考慮されていました。

例外の種類を一致させるには、前の例で示したように、ターゲットの例外をメソッド引数として宣言することが望ましいです。複数の例外メソッドが一致する場合、通常は原因の例外の一致よりもルートの例外の一致が優先されます。より具体的には、ExceptionDepthComparator は、スローされた例外型からの深さに基づいて例外をソートするために使用されます。

または、次の例に示すように、アノテーション宣言により、一致するように例外型を絞り込むことができます。

  • Java

  • Kotlin

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handleIoException(IOException ex) {
	return ResponseEntity.internalServerError().body(ex.getMessage());
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handleIoException(ex: IOException): ResponseEntity<String> {
	return ResponseEntity.internalServerError().body(ex.message)
}

次の例に示すように、非常に汎用的な引数シグネチャーを持つ特定の例外型のリストを使用することもできます。

  • Java

  • Kotlin

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handleExceptions(Exception ex) {
	return ResponseEntity.internalServerError().body(ex.getMessage());
}
@ExceptionHandler(FileSystemException::class, RemoteException::class)
fun handleExceptions(ex: Exception): ResponseEntity<String> {
	return ResponseEntity.internalServerError().body(ex.message)
}

ルートと原因の例外マッチングの違いは驚くべきことです。

前に示した IOException バリアントでは、メソッドは通常、実際の FileSystemException または RemoteException インスタンスを引数として呼び出されます。どちらも IOException から拡張されているためです。ただし、そのような一致する例外がそれ自体 IOException であるラッパー例外内で伝搬される場合、渡される例外インスタンスはそのラッパー例外です。

handle(Exception) バリアントの動作はさらに単純です。これは、折り返しシナリオでは常にラッパー例外で呼び出され、実際には一致する例外が ex.getCause() を介して検出されます。渡された例外は、実際の FileSystemException または RemoteException インスタンスがトップレベルの例外としてスローされた場合のみです。

一般的に、引数シグネチャーをできるだけ具体的に指定することをお勧めします。これにより、ルートと原因の例外型間の不一致の可能性を減らします。マルチマッチングメソッドを個々の @ExceptionHandler メソッドに分割し、それぞれのシグネチャーで単一の特定の例外型に一致させることを検討してください。

マルチ @ControllerAdvice 配置では、対応する順序で優先順位付けされた @ControllerAdvice でプライマリルート例外マッピングを宣言することをお勧めします。ルート例外の一致は原因よりも優先されますが、これは特定のコントローラーまたは @ControllerAdvice クラスのメソッド間で定義されます。つまり、優先度の高い @ControllerAdvice Bean の原因一致は、優先度の低い @ControllerAdvice Bean の一致(ルートなど)よりも優先されます。

最後になりましたが、@ExceptionHandler メソッドの実装では、元の形式で例外を再スローすることにより、特定の例外インスタンスの処理を取り消すことを選択できます。これは、ルートレベルの一致、または静的に決定できない特定のコンテキスト内の一致のみに関心があるシナリオで役立ちます。指定された @ExceptionHandler メソッドがそもそも一致していないかのように、再スローされた例外は残りの解決チェーンを介して伝搬されます。

Spring MVC での @ExceptionHandler メソッドのサポートは、DispatcherServlet レベルの HandlerExceptionResolver メカニズムに基づいて構築されています。

メディア型のマッピング

例外型に加えて、@ExceptionHandler メソッドは生成可能なメディア型も宣言できます。これにより、通常は "Accept" HTTP リクエストヘッダーで HTTP クライアントによってリクエストされたメディア型に応じてエラーレスポンスを調整できます。

アプリケーションは、同じ例外型に対して、生成可能なメディア型をアノテーション上で直接宣言できます。

  • Java

  • Kotlin

@ExceptionHandler(produces = "application/json")
public ResponseEntity<ErrorMessage> handleJson(IllegalArgumentException exc) {
	return ResponseEntity.badRequest().body(new ErrorMessage(exc.getMessage(), 42));
}

@ExceptionHandler(produces = "text/html")
public String handle(IllegalArgumentException exc, Model model) {
	model.addAttribute("error", new ErrorMessage(exc.getMessage(), 42));
	return "errorView";
}
@ExceptionHandler(produces = ["application/json"])
fun handleJson(exc: IllegalArgumentException): ResponseEntity<ErrorMessage> {
	return ResponseEntity.badRequest().body(ErrorMessage(exc.message, 42))
}

@ExceptionHandler(produces = ["text/html"])
fun handle(exc: IllegalArgumentException, model: Model): String {
	model.addAttribute("error", ErrorMessage(exc.message, 42))
	return "errorView"
}

ここでは、メソッドは同じ例外型を処理しますが、重複として拒否されることはありません。代わりに、"application/json" をリクエストする API クライアントは JSON エラーを受け取り、ブラウザーは HTML エラービューを取得します。各 @ExceptionHandler アノテーションは、生成可能なメディア型をいくつか宣言できます。エラー処理フェーズでのコンテンツネゴシエーションによって、どのコンテンツ型が使用されるかが決定されます。

メソッド引数

@ExceptionHandler メソッドは、次の引数をサポートしています。

メソッド引数 説明

例外型

発生した例外へのアクセス用。

HandlerMethod

例外を発生させたコントローラーメソッドへのアクセス用。

WebRequest, NativeWebRequest

サーブレット API を直接使用せずに、リクエストパラメーターとリクエストおよびセッション属性への汎用アクセス。

jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponse

特定のリクエストまたはレスポンスの型を選択します(例: ServletRequest または HttpServletRequest または Spring の MultipartRequest または MultipartHttpServletRequest)。

jakarta.servlet.http.HttpSession

セッションの存在を強制します。結果として引数は決して null になりません。
セッションアクセスはスレッドセーフではないことに注意してください。複数のリクエストがセッションに同時にアクセスできる場合は、RequestMappingHandlerAdapter インスタンスの synchronizeOnSession フラグを true に設定することを検討してください。

java.security.Principal

現在認証されているユーザー — 既知の場合、特定の Principal 実装クラスである可能性があります。

HttpMethod

リクエストの HTTP メソッド。

java.util.Locale

現在のリクエストロケール。利用可能な最も具体的な LocaleResolver によって決まります。実際には、構成された LocaleResolver または LocaleContextResolver です。

java.util.TimeZone, java.time.ZoneId

LocaleContextResolver によって決定される、現在のリクエストに関連付けられたタイムゾーン。

java.io.OutputStream, java.io.Writer

サーブレット API によって公開されている未加工のレスポンス本文へのアクセス用。

java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap

エラーレスポンスのモデルへのアクセス用。常に空です。

RedirectAttributes

リダイレクト(クエリ文字列に追加される)の場合に使用する属性と、リダイレクト後のリクエストまで一時的に保存されるフラッシュ属性を指定します。リダイレクト属性およびフラッシュ属性を参照してください。

@SessionAttribute

クラスレベルの @SessionAttributes 宣言の結果としてセッションに格納されたモデル属性とは対照的に、任意のセッション属性へのアクセス。詳細については、@SessionAttribute を参照してください。

@RequestAttribute

リクエスト属性へのアクセス用。詳細については、@RequestAttribute を参照してください。

戻り値

@ExceptionHandler メソッドは、次の戻り値をサポートしています。

戻り値 説明

@ResponseBody

戻り値は HttpMessageConverter インスタンスを介して変換され、レスポンスに書き込まれます。@ResponseBody を参照してください。

HttpEntity<B>, ResponseEntity<B>

戻り値は、完全なレスポンス(HTTP ヘッダーと本文を含む)が HttpMessageConverter インスタンスを介して変換され、レスポンスに書き込まれることを指定します。ResponseEntity を参照してください。

ErrorResponse

本文に詳細を含む RFC 9457 エラーレスポンスをレンダリングするには、エラーレスポンスを参照してください。

ProblemDetail

本文に詳細を含む RFC 9457 エラーレスポンスをレンダリングするには、エラーレスポンスを参照してください。

String

ViewResolver 実装で解決され、暗黙モデルと一緒に使用されるビュー名 — コマンドオブジェクトと @ModelAttribute メソッドによって決定されます。ハンドラーメソッドは、Model 引数(前述)を宣言することにより、プログラムでモデルを強化することもできます。

View

暗黙的なモデルと一緒にレンダリングするために使用する View インスタンス— コマンドオブジェクトと @ModelAttribute メソッドによって決定されます。ハンドラーメソッドは、Model 引数(前述)を宣言することにより、プログラムでモデルを強化することもできます。

java.util.Map, org.springframework.ui.Model

RequestToViewNameTranslator を介して暗黙的に決定されたビュー名を持つ暗黙モデルに追加される属性。

@ModelAttribute

RequestToViewNameTranslator を介して暗黙的に決定されたビュー名でモデルに追加される属性。

@ModelAttribute はオプションです。この表の最後にある「その他の戻り値」を参照してください。

ModelAndView object

The view and model attributes to use and, optionally, a response status.

void

void 戻り型(または null 戻り値)を持つメソッドは、ServletResponseOutputStream 引数、@ResponseStatus アノテーションも持っている場合、レスポンスを完全に処理したと見なされます。コントローラーが ETag または lastModified の正のタイムスタンプチェックを行った場合も同様です(詳細についてはコントローラーを参照してください)。

上記のいずれにも当てはまらない場合、void 戻り値の型は、REST コントローラーの「レスポンス本文なし」または HTML コントローラーのデフォルトのビュー名選択を示すこともできます。

その他の戻り値

戻り値が上記のいずれにも一致せず、(BeanUtils#isSimpleProperty (Javadoc) によって決定される)単純型ではない場合、デフォルトでは、モデルに追加されるモデル属性として扱われます。単純型の場合、未解決のままです。