Spring Cloud FunctionでAmazon API Gatewayのプロキシ統合なLambda関数をサクッと書いてみる
はじめに
Lambda関数をJavaで実装するのは面倒くさい。
Spring Cloud Functionというのを使うと、その辺の煩雑さを良い感じにフレームワークが吸収してくれるらしい。
ということで、本当に良い感じに吸収されてサクッと書けるのかを試してみた。
ちなみに、Spring Cloud Functionはプロキシ統合していないサンプルは多くあれど、統合版はなかなか見つからなかったので、その検討の一助にもなれば。
構成
以下のようなMavenプロジェクトを作成する。
SpringCloudFunctionTest
├── pom.xml
├── serverless.yml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── springcloudfunctiontest
│ │ ├── AWSLambdaHandler.java
│ │ ├── Hello.java
│ │ └── SpringCloudFunctionExampleApplication.java
│ └── resources
│ └── application.properties
└── test
└── java
└── com
└── springcloudfunctiontest
└── SpringCloudFunctionExampleApplicationTests.java
pom.xmlは↓こんな感じにしておく。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.techprimers.serverless</groupId>
<artifactId>spring-cloud-function-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-function-test</name>
<description>Demo project for Spring Boot with Spring Cloud Function</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>aws</shadedClassifierName>
</configuration>
</plugin>
</plugins>
</build>
</project>
ハンドラのコード
通常のSpring Bootなアプリ同様、SpringApplication.run
するクラスを作る。
package com.springcloudfunctiontest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringCloudFunctionExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFunctionExampleApplication.class, args);
}
}
で、Lambdaのイベントハンドラを作る。
正直、このクラスが空っぽで良い理由がよく分からない……。
API Gatewayのバックエンドに置くので、InputとOutputにはそれぞれAPIGatewayProxyRequestEvent
とAPIGatewayProxyResponseEvent
を設定する。
package com.springcloudfunctiontest;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler;
public class AWSLambdaHandler extends SpringBootRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
}
最後に、ビジネスロジックのクラスを作成する。
APIGatewayProxyRequestEvent
と
APIGatewayProxyResponseEvent
`の仕様はjavadocを確認しよう。
しかし、Spring Cloud Functionはマルチクラウドでの互換性がウリなのに、こんなインタフェースにしたら完全にAWS特化な実装になっちゃうよなぁ……。
最後のsetBodyのJSONの扱いが雑なのは気にしないように。
package com.springcloudfunctiontest;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import org.springframework.stereotype.Component;
import java.util.function.Function;
import java.util.Map;
@Component
public class Hello implements Function<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
@Override
public APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent input) {
Map<String, String> queryStringParameter = input.getQueryStringParameters();
String id = queryStringParameter.get("id");
String name = null;
if( id.equals("11111") ) {
name = "\"Taro\"";
} else {
name = "\"Nanashi\"";
}
APIGatewayProxyResponseEvent responseEvent = new APIGatewayProxyResponseEvent();
responseEvent.setStatusCode(200);
responseEvent.setBody("{\"name\":" + name + "}");
return responseEvent;
}
}
テストコード
テストコードも普通に書ける。
package com.springcloudfunctiontest;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringCloudFunctionExampleApplicationTests {
@Test
public void HelloTest1() {
Hello hello = new Hello();
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
Map<String,String> queryStringParameter = new HashMap<>();
queryStringParameter.put("id", "11111");
request.setQueryStringParameters(queryStringParameter);
APIGatewayProxyResponseEvent response = hello.apply(request);
assertThat(response.getBody()).isEqualTo("{\"name\":\"Taro\"}");
}
@Test
public void HelloTest2() {
Hello hello = new Hello();
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
Map<String,String> queryStringParameter = new HashMap<>();
queryStringParameter.put("id", "22222");
request.setQueryStringParameters(queryStringParameter);
APIGatewayProxyResponseEvent response = hello.apply(request);
assertThat(response.getBody()).isEqualTo("{\"name\":\"Nanashi\"}");
}
}
デプロイ
手でいちいちS3にアップロードしたり、パイプラインのビルド待ちしたりするのも面倒臭いので、正式版のアプリでないからServerless Frameworkでテキトーにデプロイしよう。
service: test
provider:
name: aws
runtime: java8
timeout: 30
region: ap-northeast-1
package:
artifact: target/spring-cloud-function-test-0.0.1-SNAPSHOT-aws.jar
functions:
hello:
handler: org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler
events:
- http:
path: testapi
method: get
integration: lambda-proxy
request:
template:
application/json: '$input.json("$")'
これでデプロイすると、こんな感じでちゃんとRESTAPIが返されるぞ!
$ curl -X GET https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/testapi?id=11111; echo
{"name":"Taro"}
結論
うーん、Springフレームワークの恩恵を全然預かっていないので、普通のLambdaのイベントハンドラを作った時と手間としては変わらなかった気分。もっとフレームワークの機能をふんだんに使うようになったらありがたみがわかるのだろうか。
今回は、JavaでLambda関数を書くならやっぱりAPIGatewayProxyRequestEventを使うと実装が楽だね、ということと、Serverless Frameworkのデプロイがお手軽だね、と言うことは理解できた。
Author And Source
この問題について(Spring Cloud FunctionでAmazon API Gatewayのプロキシ統合なLambda関数をサクッと書いてみる), 我々は、より多くの情報をここで見つけました https://qiita.com/neruneruo/items/710a981c0ad3877e1988著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .