API Gateway + Lambda(Java17)で複数REST APIを作成する – 【aws-serverless-java-container】

API Gateway + Lambda(Java17)で複数REST APIを作成する – 【aws-serverless-java-container】

aws-serverless-java-containerを使用して、1つのLambda(Java17)で複数のREST APIを作成します。

API Gatewayのエンドポイントはすべて同じ1つのLambdaになります。

URI メソッド Lambda
/dev/message GET message-lambda
/dev/message POST message-lambda
/dev/message PUT message-lambda
/dev/message DELETE message-lambda

1つのzipをLambdaにデプロイして複数エンドポイントを作成することが可能です。

項目
Spring Boot 3.1.2
Group Id jp.co.confrage
Artifact Id demo
Packaging Type jar※zip
Java Version 17

依存関係

依存関係にaws-serverless-java-container-springboot3を追加します。これでaws-serverless-java-container-core、aws-lambda-java-coreライブラリも自動的にインポートされます。

implementation 'com.amazonaws.serverless:aws-serverless-java-container-springboot3:2.0.0-M2'

ちなみに今回Tomcatは不要なのでexcludeしておきます。

implementation ('org.springframework.boot:spring-boot-starter-web') {
  exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}

Lambda

RequestStreamHandlerインタフェースをimplementsしたクラスをエントリポイントとして作成します。※Lambdaプロキシ統合を使用する

Handler.java

package jp.co.confrage.demo;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;

import jakarta.ws.rs.core.Application;
public class Handler implements RequestStreamHandler{
    private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
    static {
        try {
            handler = SpringBootLambdaContainerHandler.getAwsProxyHandler(Application.class);
        } catch (ContainerInitializationException e) {
            e.printStackTrace();
            throw new RuntimeException("Could not initialize Spring Boot application", e);
        }
    }

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {
        handler.proxyStream(inputStream, outputStream, context);
    }
}

REST APIを作成します。

DemoController.java

package jp.co.confrage.demo;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {
    @RequestMapping(path = "/message",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> getHello(@RequestHeader HttpHeaders headers) throws Exception {
        return new ResponseEntity<>("GET.", headers, HttpStatus.OK);
    }

    @RequestMapping(path = "/message",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> postHello(@RequestHeader HttpHeaders headers) throws Exception {
        return new ResponseEntity<>("POST.", headers, HttpStatus.OK);
    }

    @RequestMapping(path = "/message",method = RequestMethod.PUT,produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> putHello(@RequestHeader HttpHeaders headers) throws Exception {
        return new ResponseEntity<>("PUT.", headers, HttpStatus.OK);
    }

    @RequestMapping(path = "/message",method = RequestMethod.DELETE,produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<?> putBye(@RequestHeader HttpHeaders headers) throws Exception {
        return new ResponseEntity<>("DELETE.", headers, HttpStatus.OK);
    }
}

zip作成

Lambda関数にアップロードするためのzipを作成しますがコチラを参考にgradle taskを追加します。組み込みTomcatを除外してzip作成します。

task buildZip(type: Zip) {
  from compileJava
  from processResources
  into('lib') {
    from(configurations.compileClasspath) {
      exclude 'tomcat-embed-*'
    }
  }
}

build.dependsOn buildZip

buildZipタスクが追加されます。

buildZipタスクを実行するとzipが作成されます。このzipをlambdaにアップロードしてデプロイします。

ハンドラが「example.Hello::handleRequest」となっているので、「jp.co.confrage.demo.Handler::handleRequest」に変更します。

build.gradle

build.gradle

plugins {
  id 'java'
  id 'org.springframework.boot' version '3.1.2'
  id 'io.spring.dependency-management' version '1.1.2'
}

group = 'jp.co.confrage'
version = '0.0.1-SNAPSHOT'

java {
  sourceCompatibility = '17'
}
repositories {
  mavenCentral()
}
dependencies {
  implementation ('org.springframework.boot:spring-boot-starter-web') {
    exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
  }
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  implementation 'com.amazonaws.serverless:aws-serverless-java-container-springboot3:2.0.0-M2'
  compileOnly 'org.projectlombok:lombok'
  annotationProcessor 'org.projectlombok:lombok'
}

tasks.named('test') {
  useJUnitPlatform()
}

task buildZip(type: Zip) {
  from compileJava
  from processResources
  into('lib') {
    from(configurations.compileClasspath) {
      exclude 'tomcat-embed-*'
    }
  }
}

build.dependsOn buildZip

API Gateway

API Gatewayの設定をします。Lambda Proxy統合を使用します。

テスト

curlでテストします。

$ curl -i -X DELETE \
https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/messages
HTTP/1.1 200 OK
Date: Thu, 24 Aug 2023 12:24:30 GMT
Content-Type: application/json
Content-Length: 28
Connection: keep-alive
x-amzn-RequestId: 12c59df2-effd-43ae-915b-d4c967555879
Accept: */*
x-amzn-Remapped-Content-Length: 28
X-Forwarded-Proto: https
x-amz-apigw-id: KKjPTFoVtjMFbVw=
x-amzn-Remapped-User-Agent: curl/8.0.1
x-amzn-Remapped-Host: xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com
X-Amzn-Trace-Id: Root=9-33e74bfb-57956e6h1afd8ea48c296fh7
x-amzn-Remapped-X-Forwarded-For: 60.100.40.40
X-Forwarded-Port: 443

DELETE.

同様にGET,POST,PUTでもcurlでAPIをたたくことができます。これで1つのLambdaで複数REST APIを実装できます。

HTTP API

API GatewayがHTTP APIの場合はSpringBootLambdaContainerHandler.getAwsProxyHandler()メソッドではなく、SpringBootLambdaContainerHandler.getHttpApiV2ProxyHandler()メソッドを使用します。

戻り値も以下のようにHttpApiV2ProxyRequestに変わります。

private static SpringBootLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
↓
private static SpringBootLambdaContainerHandler<HttpApiV2ProxyRequest, AwsProxyResponse> handler;

ドキュメント

Home
A Java wrapper to run Spring, Jersey, Spark, and other apps inside AWS Lambda. - awslabs/aws-serverless-java-container
Support for Lambda Function URLs · Issue #460 · awslabs/aws-serverless-java-container
Recently Lambda Function URLs were released: Those requests fail wit...

コメント

タイトルとURLをコピーしました