背景
JavaアプリをLambdaでホストしているが、API Gateway経由でビジネスロジック側のエラー処理した際、ステータス上200で返ってきてしまい、困ったので調べた
TL;DR
Javaだと例外処理の返却値が標準エラーの形式と合ってないので、レスポンス内容にステータスコード(5XX系)を埋め込む
200系のレスポンスマッピングテンプレート側でステータスコードを条件分岐させてマッチすればオーバーライドするように処理させると500エラーにできる。
サンプルコード(Github)
確認手順
以下は、検証のために確認した手順を記載しておく
前提条件
以下構成なっていることを前提とする。
Lambda側設定
LamdaHanderの作成
検証のため例外を強制発生するコードを作る。
フレームワーク依存な情報を排除したかったので、AWS公式ドキュメントのプレーンな実装とした。
package org.example;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import java.util.Map;
// Handler value: org.example.exampleHandler
public class exampleHandler implements RequestHandler<Map<String,String>, APIGatewayProxyResponseEvent>{
@Override
public APIGatewayProxyResponseEvent handleRequest(Map<String,String> event, Context context)
{
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
try{
// エラーの検証のためRuntimeExceptionを発生させる。
throw new RuntimeException();
}catch (RuntimeException e){
response.setStatusCode(500);
response.setBody("Error: SampleHandler is failed");
return response;
}
}
}
gradleの設定
AWS公式からLambdaで必要なライブラリの指定があるのでdependenciesに追加する。
plugins {
id 'java'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
implementation(platform("software.amazon.awssdk:bom:2.21.1"))
// 以下3つが必要
implementation 'com.amazonaws:aws-lambda-java-core:1.1.0'
implementation 'com.amazonaws:aws-lambda-java-events:3.11.1'
runtimeOnly 'com.amazonaws:aws-lambda-java-log4j2:1.5.1'
// https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-core
implementation 'com.amazonaws:aws-java-sdk-core:1.12.694'
}
tasks.register('buildZip', Zip) {
from compileJava
from processResources
into('lib') {
from configurations.runtimeClasspath
}
}
test {
useJUnitPlatform()
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
build.dependsOn buildZip
Lambdaに載せるファイルの生成
Lambdaに載せるファイルは./gradlew build
でzipファイルとして出力できる。
(何も考えないでBuildしたらbuild/distributions配下にZipが生成された)
API Gatewayの設定
API Gatewayで任意のリソースを作成する。
統合レスポンスのdefaultを編集
Content Typeをapplication/json
を指定
マッピングテンプレートを以下のような形にする
#set($inputRoot = $input.path('$'))
$input.json("$")
#if($inputRoot.statusCode.equal(500))
#set($context.responseOverride.status = 500)
#end
API を任意のステージにデプロイして外部からリクエストできるようにする。
動作確認
Curlコマンドなどを利用してHTTPプロトコルのステータスが500となっているかを確認
simple-lambda-java on master [!+] via 🅶 v8.2 via ☕ v19.0.1 on ☁️ Tokyo
❯ curl -v https://1pkko26y4k.execute-api.ap-northeast-1.amazonaws.com/dev/example
<長いので省略>
> GET /dev/example HTTP/2
> Host: 1pkko26y4k.execute-api.ap-northeast-1.amazonaws.com
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/2 500 ← ここが500エラーになっていればOK
< date: Sat, 06 Apr 2024 04:13:05 GMT
< content-type: application/json
< content-length: 59
< x-amzn-requestid: cce8041d-b308-470f-b61c-5ec7e2c417f0
< x-amz-apigw-id: VyTIuFOLNjMEFmw=
< x-amzn-trace-id: Root=1-6610cbd1-01d4efd83c4734467fbbd06a;Parent=456b75f92567940f;Sampled=0;lineage=9d43bcc4:0
<
{"statusCode":500,"body":"Error: SampleHandler is failed"}
* Connection #0 to host 1pkko26y4k.execute-api.ap-northeast-1.amazonaws.com left intact
ビジュアルで見る場合はPostmanなどを使うとステータスがわかりやすいので便利
その他参考資料
JavaでLambdaを使うユーザが少ないせいか、新しいバージョンに対応してないドキュメントが多い