大化通信

大化物流開発合同会社の社員から技術発信をしていきます

JavaでAzure Functionsの関数を作成する

こんにちは。大化の社員のDKといいます。 とある案件にて、初めてAzure Functionsの開発を行いました。 この際、苦労しましたので、備忘録として残せればと思いこの記事を作成しました。

環境

環境構築

ここでは、環境構築は割愛させていただければと思います。 ですので、参考URLのご紹介とだけさせていただければ幸いです。 また、環境構築の詳細については後日別の記事で投稿させていただければと思います。

learn.microsoft.com

ソースコードのご紹介

ここでは、JavaでAzure Functionsの関数の処理についてソースを踏まえながらご紹介いたします。 また、URLからアクセスし発火させるHttTriggerについてのご紹介です。

トリガークラス

まずは、Azure Functionsからキックされる元のクラスとなるトリガークラスの紹介です。

package jp.co.ht.contract.trigger.test;

import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;

import org.springframework.cloud.function.adapter.azure.FunctionInvoker;

import jp.co.ht.contract.Endpoints;
import jp.co.ht.contract.models.request.test.TestExecRequest;
import jp.co.ht.contract.models.response.common.Error;
import jp.co.ht.contract.models.response.test.TestExecResponse;

/**
 * テスト実行トリガークラス
 */
public class TestExecHttpTrigger extends FunctionInvoker<TestExecRequest, TestExecResponse> {

    @FunctionName("TestExec")
    public HttpResponseMessage execute(@HttpTrigger(name = "request", methods = {HttpMethod.POST}, 
            route = ”test/exec”, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<TestExecRequest> request,
            ExecutionContext context) {
        HttpResponseMessage response = null;

        // リクエスト情報設定
        TestExecRequest param = request.getBody();

        // テスト実行処理
        response = request.createResponseBuilder(HttpStatus.OK).body(handleRequest(param, context))
                .header("Content-Type", "application/json").build();

        return response;
    }

}

public class TestExecHttpTrigger extends FunctionInvoker<TestExecRequest, TestExecResponse> {

クラスを作成する際は、Springのフレームワークの処理を呼び出すために「FunctionInvoker」を継承して作成いたします。 FunctionInvokerに指定しているクラスは下記の様になっています。

  • 第1引数・・・リクエストで使用するパラメータのクラスを指定する
  • 第2引数・・・レスポンスで使用するパラメータのクラスを指定する

@FunctionName("TestExec")

@FunctionNameのアノテーションでFunctionの名前を指定します。 ここでは、「TestExec」という名前で指定しております。

methods = {HttpMethod.POST}

methods のパラメータに、受け取るリクエストのタイプを指定することができます。 ここでは、POSTでのアクセスを受け付けていますが、GETでのアクセスを受け付けたりPOSTとGETなど複数のリクエストを受け付けることをも可能です。

route = ”test/exec”

routeのパラメータに、リクエストする際のドメイン以外のURLを指定します。 ローカルでテスト実行する際に、このトリガーを実行する場合は、下記の様なURLとなります。

実行時のURL:http://localhost:7071/api/test/exec

HttpRequestMessage<TestExecRequest> request

WEB APIを呼び出した際に指定したパラメータを受け取りは、こちらの「request」という変数に格納されます。 ここには、BODY部だけでなくHEADER情報なども含まれて格納されます。 受け取るリクエストの形式としては、「TestExecRequest」として指定した形式で受け取るようになっています。 このプログラムを作成した際は、JSON形式のものをリクエストのBODY部の情報として受け取っていました。 その受け取った文字列をオブジェクトに変換をAzure側でGSONを使用して自動で行ってくれます。 ただ、リクエストの仕様た項目が受け取り時のクラスにない場合、実行エラーとなります。 また、数値で受け取るようにしていて、文字列で渡した場合これもエラーとなります。 文字列で数値だけの場合は、自動で変換されるのですが、英字などの文字が含むとタイプエラーとなります。 バリデーションなどで、数値のみ指定されているかどうかチェックする場合は、一旦文字で受け取らないとバリデーション自体が行えませんでした。

        // リクエスト情報設定
        TestExecRequest param = request.getBody();

        // テスト実行処理
        response = request.createResponseBuilder(HttpStatus.OK).body(handleRequest(param, context))
                .header("Content-Type", "application/json").build();

ここでは、リクエストのBODY部の情報を変数に格納し、「handleRequest」でSpringの処理を実行します。 その結果を、HTTPステータスがOK(200)として返却と、返却時のBODYの内容の設定および返却時の形式をJSON形式で返却するように指定しております。

これを、returnすれば、処理結果が返却される形となっています。

ファンクションクラス

次に、トリガークラスからキックされる元のクラスとなるファンクションクラスの紹介です。

package jp.co.ht.contract.functions.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.function.Function;

import jp.co.ht.contract.models.request.test.TestExecRequest;
import jp.co.ht.contract.models.response.test.TestExecResponse;
import jp.co.ht.contract.service.test.TestExecService;

/**
 * テスト実行ファンクションクラス
 */
@Component("TestExec")
public class TestExec implements Function<TestExecRequest, TestExecResponse>{

    /**
     * テスト実行サービスクラス
     */
    @Autowired
    TestExecService testExecService;

    /**
     * テスト実行処理
     * @param request リクエスト情報
     * @return レスポンス情報
     */
     public TestExecResponse apply(TestExecRequest request) {
        return testExecService.execApi(request);
    }
}
@Component("TestExec")
public class TestExec implements Function<TestExecRequest, TestExecResponse>{

トリガーから呼び出されるクラスと分かるように「@Component」のアノテーションをつける必要があります。 また、クラス名はFunciton名と同じする必要があり、同じ名前にするとトリガークラスに記載した「handleRequest」から自動的に呼び出されます。 ただ、名前解決がうまくいかないことがあり、「@Component」に引数としてFunctionの名前を指定しております。 こうしておけば、名前解決に失敗することはありませんでした。 それと、「Function」には下記の様にクラスを指定します。

  • 第1引数・・・リクエストで使用するパラメータのクラスを指定する
  • 第2引数・・・レスポンスで使用するパラメータのクラスを指定する
    /**
     * テスト実行サービスクラス
     */
    @Autowired
    TestExecService testExecService;

ここでは、サービスクラスを自動生成しております。 ここは、Springと同じような処理を記載することが可能となっております。

    /**
     * テスト実行処理
     * @param request リクエスト情報
     * @return レスポンス情報
     */
     public TestExecResponse apply(TestExecRequest request) {
        return testExecService.execApi(request);
    }

「apply」というメソッドを準備し、この中でトリガーから呼び出される処理を記載いたします。 このプログラムを作成したときには、バリデーションの処理を呼び出すような処理を行い、問題なければサービスクラスの処理を行うような処理を行っておりました。 呼び出しているサービスクラス内の処理は、Springでの記載と同じため個々では割愛いたします。

リクエストクラス

次に、リクエスト時に私パラメータの形式を指定するクラスを参考として紹介します。 詳細については、特に記載の必要がないと思われるため、割愛いたします。

package jp.co.ht.contract.models.request.test;

import java.io.Serializable;

import lombok.Data;

/**
 * テスト実行リクエストクラス
 */
@Data
public class TestExecRequest implements Serializable {

    /** 関数名 */
    private String functionName;

}

レスポンスクラス

次に、レスポンス時に私パラメータの形式を指定するクラスを参考として紹介します。 詳細については、特に記載の必要がないと思われるため、割愛いたします。

package jp.co.ht.contract.models.response.test;

import java.io.Serializable;

import lombok.Data;

/**
 * テスト実行レスポンスクラス
 */
@Data
public class TestExecResponse implements Serializable {

    /** コンテント */
    private String content;

}

今回は、以上となります。 Azure Functionsを使用してアプリ開発を行う方の力に少しでもなればと思います。