LoginSignup
3
4

More than 3 years have passed since last update.

複数のバックエンドのリソースをAmazon API Gatewayで統合する

Posted at

前提

システム開発をしていると「A機能はLambdaで作るべきで、B機能はコンテナで作るべきで、でもそれぞれ実はURIは同じにしておく方がREST的にキレイになるんだよね」なことがある。…いや、あまりないか?ともあれ、そんな時にはnginxとかでプロキシを作って振り分けても良いのだけど、せっかくパブリッククラウドを使っているのだからマネージドサービス使ってみたいよね、と思ってやってみた。

Spring BootなWebアプリの動いているコンテナと、Lambda関数がそれぞれあるような構成。

Lambda関数

この記事のために作り直すのも面倒なので、以下の記事で作ったものを転用する。

Amazon API Gateway/ALBのバックエンドで動くLambda関数をJava(Eclipse+maven)で実装する

Spring BootなWebアプリの動いているコンテナ

あまりこだわりもないので、適当なパラメータを受けてJSONを返すコンテナを作ってみる。
リソースは/person
パラメータは、クエリかリクエストボディ(JSON)でidを読み込んで、それに対応した名前と年齢をJSONで返すような仕様で。

WebController.java
package com.example;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

@RestController
public class WebController {

    @RequestMapping("/person")
    @ResponseBody
    public Person response(
            @RequestBody(required = false) Person person,
            @RequestParam(name="id", required = false) String reqParamId) {
        Person response = null;
        String id = null;

        if( reqParamId != null ){
            id = reqParamId;
        }else{
            id = Integer.toString(person.getId());
        }

        if(id.equals("11111")) {
            response = new Person(Integer.parseInt(id), "Taro", 35);
        }else if(id.equals("22222")) {
            response = new Person(Integer.parseInt(id), "Hanako", 32);
        }

        return response;
    }
}
Person.java
package com.example;

public class Person {
    private int Id;
    private String Name;
    private int Age;

    // 適当な getter, setter
    // 中略

    public Person(int id, String name, int age) {
        Id = id;
        Name = name;
        Age = age;
    }
}

pom.xml は spring-boot-starter, spring-boot-starter-web があればOK。
ビルドは適当にmvn packageしてから適当なDockerfileでdocker buildして、ECRにpushする。
この辺も以下の過去記事を参考に。

AWSでSpringBootベースのWebアプリを起動してみる(Docker on EC2編/ECS+Fargate編)

API Gatewayの設定

↓こんな感じで定義する。
キャプチャ4.PNG

リソース メソッド 転送先
LambdaTest ANY Lambda関数
person GET Spring BootなWebアプリ
person POST Lambda関数
person PUT Spring BootなWebアプリ

手でポチポチ設定するのも面倒なので、以下のSwagger+API Gateway拡張をインポートする。
分かりにくいかもしれないけど、インポートはここから可能。
キャプチャ5.png

---
swagger: "2.0"
info:
  description: "Created by AWS Lambda"
  version: "2020-04-25T10:56:39Z"
  title: "API GatewayのAPI名"
host: "[API Gatewayのドメイン]"
basePath: "/default"
schemes:
- "https"
paths:
  /LambdaTest:
    x-amazon-apigateway-any-method:
      responses:
        200:
          description: "200 response"
      x-amazon-apigateway-integration:
        uri: "[Lambda関数のARN]"
        responses:
          .*:
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        httpMethod: "POST"
        type: "aws_proxy"
  /person:
    get:
      produces:
      - "application/json"
      responses:
        200:
          description: "200 response"
          schema:
            $ref: "#/definitions/Empty"
      x-amazon-apigateway-integration:
        uri: "http://[NLBのドメイン]/person"
        responses:
          default:
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        connectionType: "VPC_LINK"
        connectionId: "[VPCLinkのID]"
        httpMethod: "GET"
        type: "http_proxy"
    post:
      produces:
      - "application/json"
      responses:
        200:
          description: "200 response"
          schema:
            $ref: "#/definitions/Empty"
      x-amazon-apigateway-integration:
        uri: "[Lambda関数のARN]"
        responses:
          default:
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        httpMethod: "POST"
        contentHandling: "CONVERT_TO_TEXT"
        type: "aws_proxy"
    put:
      produces:
      - "application/json"
      responses:
        200:
          description: "200 response"
          schema:
            $ref: "#/definitions/Empty"
      x-amazon-apigateway-integration:
        uri: "http://[NLBのドメイン]/person"
        responses:
          default:
            statusCode: "200"
        passthroughBehavior: "when_no_match"
        connectionType: "VPC_LINK"
        connectionId: "[VPCLinkのID]"
        httpMethod: "GET"
        type: "http_proxy"
definitions:
  Empty:
    type: "object"
    title: "Empty Schema"

コンテナについては、内部向けのHTTPサーバを統合する場合は、VPCLinkで繋いだNLBにぶら下げる必要がある。この辺の記事を参考にしよう。ほぼこの記事とやってることは同じだけど気にしない………。

さて、ここまでやったら、curlで動作確認をしてみると、期待通りの動作になっているのが分かる。

Lambda向けのリクエスト①
$ curl --include -X GET https://[API Gatewayのドメイン]/default/LambdaTest
HTTP/2 200
date: Sat, 25 Apr 2020 12:52:11 GMT
content-type: application/json
content-length: 42
x-amzn-requestid: ********
x-amz-apigw-id: ********
x-amzn-trace-id: ********

{"greetings":"Hello from Lambda Ver 1.11"}
Lambda向けのリクエスト②
$ curl --include -X POST https://[API Gatewayのドメイン]/default/person
HTTP/2 200
date: Sat, 25 Apr 2020 12:54:16 GMT
content-type: application/json
content-length: 42
x-amzn-requestid: ********
x-amz-apigw-id: ********
x-amzn-trace-id: ********

{"greetings":"Hello from Lambda Ver 1.11"}
Spring BootなWebアプリ向けのリクエスト①
$ curl --include -X GET https://[API Gatewayのドメイン]/default/person?id=11111
HTTP/2 200
date: Sat, 25 Apr 2020 12:55:24 GMT
content-type: application/json
content-length: 35
x-amzn-requestid: ********
x-amzn-remapped-connection: keep-alive
x-amz-apigw-id: ********
x-amzn-remapped-date: Sat, 25 Apr 2020 12:55:24 GMT

{"name":"Taro","id":11111,"age":35}[
Spring BootなWebアプリ向けのリクエスト②
$ curl --include -X PUT -H 'Content-Type:application/json' -d '{"id":"11111"}' https://[API Gatewayのドメイン]/default/person?id=11111
HTTP/2 200
date: Sat, 25 Apr 2020 12:56:33 GMT
content-type: application/json
content-length: 35
x-amzn-requestid: ********
x-amzn-remapped-connection: keep-alive
x-amz-apigw-id: ********
x-amzn-remapped-date: Sat, 25 Apr 2020 12:56:32 GMT

{"name":"Taro","id":11111,"age":35}
3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4