Kilka dni temu mój zespół miał za zadanie zintegrować naszą aplikację Java z punktami końcowymi Amazon API Gateway. Jak zapewne wiesz, Amazon API Gateway wymaga podpisywania żądań.

W naszym przypadku uwierzytelnianie opierało się na kluczu dostępu użytkownika IAM i tajnym kluczu dostępu. Podpisanie żądania to kryptograficzna funkcja skrótu zwracająca wartość skrótu na podstawie danych wejściowych. Amazon API Gateway obsługuje uwierzytelnianie przy użyciu AWS Signature Version 4.

wywoływanie aws api gateway z poziomu java

Gotowy do rozpoczęcia? Wywołanie AWS API Gateway z poziomu języka Java

Podczas burzy mózgów rozważaliśmy następujące pomysły:

  1. Wygenerowanie AWS SDK dla konkretnej definicji API Gateway - najbardziej wystarczający sposób zalecany przez Amazon.
  2. Generowanie Klient HTTP z OpenAPI lub Swagger (obie definicje są gotowe do pobrania z API Gateway).
  3. Użyj niestandardowego Klient HTTPnp. RestTemplate i zaimplementować sygnatariusza samodzielnie w oparciu o Przykład w języku Python.
  4. Użyj AWS SDK, AWS4Signer i czystego żądania/odpowiedzi JSON, aby wywołać API Gateway.

Wiedzieliśmy również, że to API nie jest stabilne i dostawca API może je zmienić. W tych okolicznościach zdecydowaliśmy się nie używać wstępnie wygenerowanego AWS SDK lub klienta HTTP z OpenAPI (pamiętasz kod boilerplate wygenerowany z WSDL i mapowań DTO?). Dodatkowo, próba wygenerowania dedykowanego AWS SDK dała nam błąd w AWS Console. Chore!!!

aws api gateway

Naturalną decyzją było wdrożenie trzeciego lub czwartego pomysłu. Zorganizowałem wewnętrzny konkurs, w którym zwycięskie rozwiązanie zostanie wykorzystane w naszym projekcie. Mój kolega z zespołu zaczął wdrażać AWS signer i wywołać API Gateway ręcznie za pomocą RestTemplate. Rozpocząłem badania nad tym, jak to zrobić, używając AWS SDK oraz AWS4Signer wdrożenie. Podczas implementacji obaj narzekaliśmy. Mój kolega miał problem z debugowaniem, dlaczego suma kontrolna (kod skrótu) jest nieprawidłowa. Nie byłem w stanie znaleźć przykładu użycia AWS SDK oraz AWS4Signer w oficjalnej dokumentacji AWS i StackOverflow. Było tylko jedno miejsce w Apache NiFi projekt, w którym podobne implementacja została wykonana.

Ostatecznie najszybszym rozwiązaniem okazała się niestandardowa implementacja wywołania API Gateway oparta na AWS SDK oraz AWS4Signer.

W tym wpisie na blogu chciałbym opisać wymagane kroki, aby zaimplementować wywołanie API Gateway przy użyciu surowego żądania i odpowiedzi JSON. Jeśli jesteś niecierpliwy, możesz przejść bezpośrednio do przykładu GitHub i sprawdzić moją niestandardową implementację opartą na Apache NiFi.

Jak wywołać AWS API Gateway z poziomu kodu Java?

1. Zebranie podstawowych informacji

Przede wszystkim należy zebrać następujące dane od dostawcy API Gateway:

  • AWS_IAM_ACCESS_KEY (użytkownik IAM),
  • AWS_IAM_SECRET_ACCESS_KEY (hasło IAM),
  • AWS_REGION (region, w którym wdrożona jest brama API),
  • AWS_API_GATEWAY_ENDPOINT (adres URL do punktu końcowego API Gateway).

2. Dołącz zestaw AWS SDK do swojego projektu

W moim przypadku dodałem do kodu następującą zależność Maven:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-core</artifactId>
    <version>{version}</version>
</dependency>

3. Utwórz klasę JsonApiGatewayCaller rozszerzającą klasę AmazonWebServiceClient.

public class JsonApiGatewayCaller extends AmazonWebServiceClient

The AmazonWebServiceClient abstrakcyjna klasa bazowa jest odpowiedzialna za podstawowe możliwości klienta, które są takie same dla wszystkich klientów AWS SDK Java, na przykład ustawianie punktu końcowego klienta.

4. Utwórz JsonApiGatewayCaller konstruktor

    public JsonApiGatewayCaller(String accessKey, String secretAccessKey, String apiKey, String region, URI endpoint) {

        super(new ClientConfiguration());

        this.credentials = new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretAccessKey));
        this.apiKey = apiKey;
        this.endpoint = endpoint;

        this.signer = new AWS4Signer();
        this.signer.setServiceName(API_GATEWAY_SERVICE_NAME);
        this.signer.setRegionName(region);

        final JsonOperationMetadata metadata = new JsonOperationMetadata().withHasStreamingSuccessResponse(false).withPayloadJson(false);
        final Unmarshaller<ApiGatewayResponse, JsonUnmarshallerContext> responseUnmarshaller = in -> new ApiGatewayResponse(in.getHttpResponse());
        this.responseHandler = SdkStructuredPlainJsonFactory.SDK_JSON_FACTORY.createResponseHandler(metadata, responseUnmarshaller);

        JsonErrorUnmarshaller defaultErrorUnmarshaller = new JsonErrorUnmarshaller(ApiGatewayException.class, null) {
            @Override
            public AmazonServiceException unmarshall(JsonNode jsonContent) throws Exception {
                return new ApiGatewayException(jsonContent.toString());
            }
        };

        this.errorResponseHandler = SdkStructuredPlainJsonFactory.SDK_JSON_FACTORY.createErrorResponseHandler(
                Collections.singletonList(defaultErrorUnmarshaller), null);
    }
  • The clientConfiguration obiekt zawiera podstawowe opcje HTTP, takie jak ustawienia proxy, ciąg agenta użytkownika, maksymalna liczba ponawianych prób itp.
  • The credentials obiekt reprezentuje AWSCredentialsProvider. Istnieje kilka sposobów dostarczania poświadczeń IAM do kodu, zobacz oficjalną dokumentację. W naszym uproszczonym przykładzie używamy statycznego dostawcy poświadczeń.
  • The apiKey reprezentuje ustawienie autoryzacji w naszej bramie API. Zobacz oficjalną dokumentację.
  • The endpoint obiekt jest reprezentowany przez adres URL do naszej bramy API, na przykład https://234n34k5678k.execute-api.eu-west-1.amazonaws.com/TEST.
  • Następne trzy linie tworzą AWS4Signer obiekt do podpisywania żądań za pomocą AWS4 protokół podpisywania.
  • Ostatnia sekcja konstruktora jest odpowiedzialna za funkcję responseHandler oraz errorResponseHandler creation. Jest to kluczowa część pobierania odpowiedzi w czystej reprezentacji JSON.

5. Utwórz ExecutionContext

    private ExecutionContext createExecutionContext() {
        final ExecutionContext executionContext = ExecutionContext.builder().withSignerProvider(
                new DefaultSignerProvider(this, signer)).build();
        executionContext.setCredentialsProvider(credentials);
        return executionContext;
    }

The ExecutionContext pochodzi z com.amazonws.http i agreguje obsługę żądań, klienta usługi sieciowej, dostawcę sygnatariuszy i AWS Request Metrics. Należy pamiętać, że ta klasa nie jest bezpieczna dla wątków.

6. Utwórz DefaultRequest

    private DefaultRequest prepareRequest(HttpMethodName method, String resourcePath, InputStream content) {
        DefaultRequest request = new DefaultRequest(API_GATEWAY_SERVICE_NAME);
        request.setHttpMethod(method);
        request.setContent(content);
        request.setEndpoint(this.endpoint);
        request.setResourcePath(resourcePath);
        request.setHeaders(Collections.singletonMap("Content-type", "application/json"));
        return request;
    }

Ta metoda tworzy DefaultRequest w dość prosty sposób. Kluczową częścią jest tutaj ustawienie Content-type: application/json aby poprawnie "rozmawiać" w formacie JSON.

7. Wykonanie metody HTTP

    public ApiGatewayResponse execute(HttpMethodName method, String resourcePath, InputStream content) {

        final ExecutionContext executionContext = createExecutionContext();
        DefaultRequest request = prepareRequest(method, resourcePath, content);
        RequestConfig requestConfig = new AmazonWebServiceRequestAdapter(request.getOriginalRequest());
        return this.client.execute(request, responseHandler, errorResponseHandler, executionContext, requestConfig).getAwsResponse();
    }

Ta metoda jest sercem naszej klasy. Jest ona odpowiedzialna za wywołanie punktu końcowego API Gateway poprzez przygotowanie kontekstu wykonania, żądania, konfiguracji żądania i wywołanie funkcji execute metoda z AmazonHttpClient. Można tutaj zauważyć, że metoda ta wymaga przekazania responseHandler oraz errorResponseandler aby poprawnie usunąć wyniki. Implementacja wykorzystuje nasze własne ApiGatewayResponse oraz Wyjątek ApiGatewayException.

8. Końcowe użycie

Załóżmy, że chcemy wysłać metodę POST do punktu końcowego /pets z następującym żądaniem JSON:

{
    "type": "dog",
    "price": 249.99
}

Nasz kod Java może wyglądać jak na poniższym przykładzie:

    JsonApiGatewayCaller caller = new JsonApiGatewayCaller(
                    AWS_IAM_ACCESS_KEY,
                    AWS_IAM_SECRET_ACCESS_KEY,
                    null,
                    AWS_REGION,
                    new URI(AWS_API_GATEWAY_ENPOINT)
            );

    ApiGatewayResponse response = caller.execute(HttpMethodName.POST, "/pets", new ByteArrayInputStream(exampleJsonRequest.getBytes()));

Wnioski

  • Korzystanie z AWS SDK nie jest zbyt trywialne, a czasami nie jest dobrze udokumentowane.
  • Podpisywanie żądań przez AWS4 protokół może powodować problemy.
  • Aby po prostu wywołać API Gateway, trzeba stworzyć wiele standardowego kodu.
  • Pełny Java kod jest dostępny na stronie GitHub.

Potrzebujesz dedykowanego zespołu programistów do wynajęcia?
Chcesz porozmawiać z naszymi ekspertami o niestandardowych rozwiązaniach programistycznych dla Twojej firmy?

P: Jakie są niektóre z korzyści migracji do chmury wspomnianych w tej treści?

Niektóre z korzyści płynących z migracji do chmury wymienionych w tej treści to skalowalność, niezawodność, odzyskiwanie danych po awarii i automatyczne tworzenie kopii zapasowych, bezpieczeństwo, niższe koszty, lepsze monitorowanie i analiza oraz lepsze zarządzanie zasobami IT.

4.1/5 - (20 głosów)