以前、Azure API Managementで、一定時間内に一定回数以上のアクセスが来たときにHTTP 429(Too Many Requests)を返却するような流量制限を設定したことがあったが、この流量制限エラーになった場合は、HTTPClientErrorExceptionという例外が発生する。
例えば、以下のソースコードのような、@ExceptionHandlerアノテーションでException例外をキャッチするメソッドがある場合、このメソッド内で、流量制限エラーになった場合の処理が行われる。
package com.example.demo;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class DemoControllerAdvice {
@ExceptionHandler(Exception.class)
public String occurException(Exception exception, Model model) {
model.addAttribute("errMsg", exception);
return "error";
}
}今回は、流量制限を設定したプログラム内に、@ExceptionHandlerアノテーションで例外をキャッチするメソッドを追加して、そのメソッド内で流量制限エラーの場合のみ独自のエラー画面に遷移するプログラムを作成してみたので、共有する。
前提条件
下記記事の流量制限の設定と、その前提条件となる実装が完了していること。
修正したサンプルプログラム(App Service側)の内容
修正したサンプルプログラム(App Service側)の構成は以下の通り。

なお、上記の赤枠は、前提条件のプログラムから変更したプログラムである。また、Azure Functions側のソースコードは修正していない。
@ExceptionHandlerアノテーションで例外をキャッチするメソッドを定義しているクラスは以下の通りで、HTTP 429(Too Many Requests)エラーの場合はerror429.html画面に、それ以外の場合はerror.html画面に遷移するようにしている。
package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpClientErrorException;
@ControllerAdvice
public class DemoControllerAdvice {
/**
* Exceptionクラスのサブクラスの例外が発生した場合の処理.
* @param exception Exceptionクラスのサブクラスの例外
* @param model Modelオブジェクト
* @return 画面遷移先
*/
@ExceptionHandler(Exception.class)
public String occurException(Exception exception, Model model) {
// HTTP 429(Too Many Requests)エラーが発生した場合は、error429.htmlに遷移する
if (exception instanceof HttpClientErrorException) {
HttpClientErrorException e = (HttpClientErrorException) exception;
if (HttpStatus.TOO_MANY_REQUESTS.equals(e.getStatusCode())) {
return "error429";
}
}
// 上記以外の場合は、error.htmlに遷移し、発生したエラーメッセージを表示する
model.addAttribute("errMsg", exception);
return "error";
}
}また、error.htmlの内容は以下の通りで、発生した例外の内容を表示するようにしている。
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>エラー画面</title>
</head>
<body>
<form method="post" th:action="@{/backToMain}">
エラーが発生しました。<br/>
発生したエラー: <span th:text="${errMsg}"></span>
<br/><br/>
<input type="submit" value="戻る" />
</form>
</body>
</html>さらに、error429.htmlの内容は以下の通りで、HTTP 429(Too Many Requests)エラーが発生した旨を表示している。
<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>エラー(429)画面</title>
</head>
<body>
<form method="post" th:action="@{/backToMain}">
エラー(HTTP 429 TOO_MANY_REQUESTS)が発生しました。
<br/><br/>
<input type="submit" value="戻る" />
</form>
</body>
</html>その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/flow-rate-limit-exception-handler/demoAzureApp
サンプルプログラムの実行結果
以下のように、DemoControllerAdvice.java内の、HTTP 429(Too Many Requests)エラーの場合をコメントアウトして、前提条件の記事の流量制限を設定した結果は、以下の通り。

1) Azure App ServiceのURL「https://azureappdemoservice.azurewebsites.net/」とアクセスすると以下の画面が表示されるため、「ファンクション呼び出し」ボタンを押下する。

なお、上記URLは、下記Azure App ServiceのURLから確認できる。

2) callFunctionApi関数が呼び出され、以下の画面に遷移する。その後「戻る」ボタンを押下する。

3) 以下のように、初期表示画面に戻ることが確認できる。ここで再度「ファンクション呼び出し」ボタンを押下する。

4) 遷移先画面に再度に遷移するので、「戻る」ボタンを押下する。

5) 初期表示画面に再度戻るので、「ファンクション呼び出し」ボタンを押下する。

6) 流量制限エラーとなり、error.htmlに遷移し、HTTPClientErrorExceptionという例外が発生したことが確認できる。

また、以下のように、DemoControllerAdvice.java内の、HTTP 429(Too Many Requests)エラーの場合のコメントアウトを外した状態で、前提条件の記事の流量制限を設定した結果は、以下の通り。

1) Azure App ServiceのURL「https://azureappdemoservice.azurewebsites.net/」とアクセスすると以下の画面が表示されるため、「ファンクション呼び出し」ボタンを押下する。

2) callFunctionApi関数が呼び出され、以下の画面に遷移する。その後「戻る」ボタンを押下する。

3) 以下のように、初期表示画面に戻ることが確認できる。ここで再度「ファンクション呼び出し」ボタンを押下する。

4) 遷移先画面に再度に遷移するので、「戻る」ボタンを押下する。

5) 初期表示画面に再度戻るので、「ファンクション呼び出し」ボタンを押下する。

6) 流量制限エラーとなり、独自の流量制限エラー画面に遷移することが確認できる。

要点まとめ
- Azure API Managementで流量制限エラーになった場合は、HTTPClientErrorExceptionという例外が発生する。
- @ExceptionHandlerアノテーションを付与したメソッド内で任意例外をキャッチする処理が入っている場合は、そのメソッド内で、流量制限エラーの場合のみ独自のエラー画面に遷移する処理を実装する。





