Azure FunctionsとActiveReports for .NETでつくる帳票生成API

.NET帳票コンポーネント「ActiveReports for .NET(アクティブレポート)」の最新バージョン「18.0J」では、クラウド環境のサポートが拡充され、新たに大手クラウドベンダー4社の「FaaS(Function as a Service)」が利用可能となりました。今回は、その中からMicrosoftの「Azure Functions」とActiveReportsを利用して、帳票機能をWeb APIとして作成する方法をご紹介します。

開発環境

ActiveReports for .NETの開発環境にはOSに「Windows」、統合開発環境(IDE)に「Visual Studio」が必要となります。事前に、ActiveReportsの必要システムに記載されている、開発環境をご準備ください。さらに、今回はAzure Functionsのデプロイ先としてAzure環境もご用意ください。

今回の開発環境では、以下を使用します。

  • Azure 環境
  • OS:Windows 11(23H2)
  • IDE:Visual Studio 2022(Version 17.11.5)
  • ActiveReports:18.0J SP1(v18.1.1.0)

製品版の最新バージョンは以下より入手可能です。

トライアル版は無料で以下より入手可能です。

プロジェクトの作成

それでは、プロジェクトの作成を行っていきます。今回は以下のようにVisual Studio標準で用意されている「Azure Functions」プロジェクトテンプレートを利用します。

検索ボックスに「Azure Functions」と入力すると、「Azure Functions」がフィルタリングされますので、こちらを選択して[次へ]を押します。

プロジェクトの作成1

続いて表示されるダイアログでは、プロジェクト名を「FunctionReportsApp」とします。保存場所は任意の場所を設定してください。

プロジェクトの作成2

最後はFunction worker、Functionの選択ダイアログとなります。次のようにFunction workerには「.NET 8.0」、Functionは「Http trigger with OpenAPI」を設定します。

プロジェクトの作成3

NuGetからActiveReportsパッケージをインストール

プロジェクトが作成されると次のような画面が表示されます。まずはActiveReportの関連パッケージをNuGetからインストールしていきます。

図のように、ソリューションエクスプローラー上からプロジェクトを右クリックし「NuGetパッケージの管理」を選択します。

NuGetからのインストール1

続いて、NuGetパッケージマネージャーの表示後、「参照」タブを選択、検索テキストボックスに「MESCIUS.ActiveReports.ja」を入力します。検索結果には次のように「MESCIUS.ActiveReports.ja」が表示されるので、選択し、インストールを行います。

NuGetからのインストール2

この際に、次の画像のように、「Microsoft.CodeAnalysis.VisualBasic」のバージョンの競合に関するエラーが発生する場合があります。これは、プロジェクト作成時に設定した「Http trigger with OpenAPI」のOpenAPIで利用しているパッケージとの競合によるものです。

Microsoft.CodeAnalysis.VisualBasic競合エラー

この場合には次のように、個別に指定のバージョンをインストールします。

NuGetからMicrosoft.CodeAnalysis.VisualBasicをインストール

インストール後に、改めて「MESCIUS.ActiveReports.ja」をインストールします。

NuGetからのインストール2

さらに、今回作成するAPIで利用するPDF出力に関するパッケージ「MESCIUS.ActiveReports.Export.Pdf.ja」もインストールします。

NuGetからのPDFパッケージインストール

帳票レイアウト

パッケージのインストール後、帳票レイアウトの準備を行います。今回は以下のGitHubに公開されているレポートファイル「Invoice_bluegray.rdlx」を利用します。

今回のプログラムは、Azure FunctionsのWebAPIとして実装します。APIのリクエストのタイミングでJSONデータを渡し、そのデータを帳票のデータソースとして利用して帳票を生成する構成として実装を行っていきます。

最初に、ダウンロードしたレポートファイルを次のようにソリューションエクスプローラーから追加します。追加の際、ファイルのビルドアクションを「埋め込みリソース」に変更します。

レポートファイルの追加

続けて、APIのリクエスト時に渡されるJSONデータを帳票のデータソースとして利用するために、データを格納する「レポートパラメータ」を追加していきます。

追加したファイルを選択し、Visual StudioのIDEデザイナで開きます。次に、レポートエクスプローラからパラメータを選択し、「パラメータの追加」を使用してレポートパラメータを追加します。

レポートファイルの編集1

レポートパラメータダイアログが表示後、パラメータ名称など変更可能です。今回は初期値のままにしておきます。

レポートファイルの編集2

続いて、レポートパラメータダイアログの「既定値」タブを選択します。「値を直接入力」を選び、「+アイコン」をクリックして既定値のリストを追加します。追加されたリスト内に、レポート表示データの初期値となるJSONデータを設定します。

レポートファイルの編集3
レポート表示初期値用JSONデータ
設定データ
[
  {
    “ID”: 1,
    “BillNo”: “WS-DF502”,
    “SlipNo”: “GB465”,
    “CustomerID”: 1,
    “CustomerName”: “長崎カントリーフーズ”,
    “Products”: “コーヒー 250 ml”,
    “Number”: 100,
    “UnitPrice”: 100,
    “Date”: “2020-01-05T00:00:00”
  },
  {
    “ID”: 2,
    “BillNo”: “WS-DF502”,
    “SlipNo”: “GB465”,
    “CustomerID”: 1,
    “CustomerName”: “長崎カントリーフーズ”,
    “Products”: “紅茶 350 ml”,
    “Number”: 300,
    “UnitPrice”: 120,
    “Date”: “2020-01-05T00:00:00”
  },
  {
    “ID”: 3,
    “BillNo”: “WS-DF502”,
    “SlipNo”: “DK055”,
    “CustomerID”: 1,
    “CustomerName”: “長崎カントリーフーズ”,
    “Products”: “炭酸飲料 (オレンジ) 350 ml”,
    “Number”: 200,
    “UnitPrice”: 120,
    “Date”: “2020-01-09T00:00:00”
  }
]

つづけて、追加したレポートパラメータを帳票のデータソースとして利用する為、以下のようにレポートエクスプローラの「データソース」から現在設定されている「DataSource1」を編集で開きます。

レポートファイルの編集4

レポートデータソースダイアログの表示後、次の画像のように「コンテンツ」タブ内の「JSONデータの形式の選択」を「式」に変更し、つづけて「式の入力」に="jsondata=" &[@ReportParameter1]を入力します。

レポートファイルの編集5

以上で、帳票レイアウトの設定は終了です。

コード実装

つづいて、コードの実装を行います。プロジェクトの作成時に、次の「Function1.cs」というファイルが追加されています。このファイルに対して、次のコードで強調表示している箇所を実装していきます。

using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using GrapeCity.ActiveReports;
using GrapeCity.ActiveReports.Extensibility.Rendering;
using GrapeCity.ActiveReports.Rendering.IO;
using System.Collections.Specialized;
using System.Globalization;
using System.Threading;
using static GrapeCity.Enterprise.Data.DataEngine.DataProcessing.DataProcessor;
using System.Reflection;
using System;


namespace FunctionReportsApp
{
    public class Function1
    {
        private readonly ILogger<Function1> _logger;

        public Function1(ILogger<Function1> log)
        {
            _logger = log;
        }

        [FunctionName("Function1")]
        [OpenApiOperation(operationId: "Run", tags: new[] { "datajson" })]
        [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
        [OpenApiParameter(name: "datajson", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **DataJson** parameter")]
        [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/pdf", bodyType: typeof(byte[]), Description = "The OK response")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");
            //クエリパラメータを取得
            string datajson = req.Query["datajson"];

            //リクエストボディを取得
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            // クエリパラメータが空の場合、リクエストボディのdatajsonを使用  
            if (datajson == null && data != null)
            {
                // datajsonが存在するか確認し、文字列に変換する  
                datajson = JsonConvert.SerializeObject(data.datajson);
            }

            //ActionResultを設定
            ActionResult result;

            try
            {

                //アセンブリ取得
                var assembly = Assembly.GetExecutingAssembly();

                //レポートファイルのストリームを取得
                var stream = assembly.GetManifestResourceStream("FunctionReportsApp.Invoice_bluegray.rdlx");
                var streamReader = new StreamReader(stream);

                //レポートファイルを読み込み
                GrapeCity.ActiveReports.PageReport pageReport = new PageReport(streamReader);
                //レポートにパラメータを設定
                pageReport.Document.Parameters[0].Values[0].Value = datajson;

                //PDF出力設定
                GrapeCity.ActiveReports.Export.Pdf.Page.Settings pdfSetting = new GrapeCity.ActiveReports.Export.Pdf.Page.Settings();
                GrapeCity.ActiveReports.Export.Pdf.Page.PdfRenderingExtension pdfRenderingExtension = new GrapeCity.ActiveReports.Export.Pdf.Page.PdfRenderingExtension();
                GrapeCity.ActiveReports.Rendering.IO.MemoryStreamProvider outputProvider = new GrapeCity.ActiveReports.Rendering.IO.MemoryStreamProvider();

                //PDFレンダリング
                pageReport.Document.Render(pdfRenderingExtension, outputProvider, pdfSetting);

                //PDFファイルをMemoryStreamに変換
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                outputProvider.GetPrimaryStream().OpenStream().CopyTo(ms);

                //ActionResultにPDFファイルを設定
                result = new FileContentResult(ms.ToArray(), "application/pdf")
                {
                    FileDownloadName = "report.pdf"
                };
                

            }
            catch (Exception ex)    //エラー処理
            {
                //エラー時はステータスコード500を返す
                result = new StatusCodeResult(500);


                //エラーログを出力
                _logger.LogInformation(ex.Message);
            }

            return result;
        }
    }
}

このコードでは、GETメソッドのリクエスト時にクエリパラメータ「datajson」を渡すか、POSTメソッドの場合はリクエストボディで設定されたパラメータ「datajson」を渡します。これを帳票レイアウトで定義したレポートパラメーターを通じて帳票データソースとして利用し、最終的にPDFファイルとして帳票を作成しています。

ライセンスの設定

通常の「.NETアプリケーション」と異なり、「Azure Functionsアプリケーション」にライセンスの付与を行う際は、「C:\ProgramData\GrapeCity\gclm」内の「gclm.exe」を利用してライセンスを付与する必要があります。
※ Azure Functionsを利用する際は通常のコアサーバーライセンスでは利用できません。配布ライセンスの詳細については営業部へお問い合わせください。

まず、以下のように、Windowsのコマンドラインツールで「gclm.exe」が配置されているパスへ移動します。

cd C:\ProgramData\GrapeCity\gclm

続いて、次の形式のコマンドを実行し、任意のフォルダにライセンスファイルを生成します。

gclm.exe "[ActiveReports GUID]" -lc "[output dir]/.gclicx" [DeployTarget].[assemblyName].dll
項目設定内容
[ActiveReports GUID]ActiveReports for .NET 18.0J 製品IDを設定します。
  • 「“8145dedc-e6e6-4af3-917b-c3fb57853f6b”」
[output dir]ライセンスファイルの出力先フォルダを設定します。
[DeployTarget]デプロイの対象先によって、次のように設定が異なります。
  • ローカル(デバック実行用):「func」
  • Azure:「Microsoft.Azure.WebJobs.Script.WebHost」
[assemblyName]アプリケーションのアセンブリ名を指定します。今回は次のアセンブリ名を設定します。
  • 「FunctionReportsApp」

まず、ローカルでデバッグ実行にて動作確認を行うため、ローカル向けのライセンスの付与します。上述したコマンドを利用し、以下のように実行して「.gclicx」ファイルを生成します。

ローカル用ライセンス付与

つづけて、コマンドにより生成されたライセンスファイル「.gclicx」をプロジェクト内に含め、ビルドアクションを「埋め込みリソース」に設定します。

ライセンスファイルの追加

上記に加えて、プロジェクトファイルをエディタで開き、以下のように<PropertyGroup>内に<disablegclm>true</disablegclm>の記述を追加します。この設定は、通常の.NETアプリケーションをビルドする際に自動的に付与されているライセンスを無効化するものです。プロジェクトファイルを編集した後、再度プロジェクトを開き直してください。

自動付与ライセンスを無効化

動作確認

ここまでで、帳票レイアウトの編集、コード実装、ライセンスの設定が一通り終了しましたので、ローカルでデバック実行にて動作の確認を行います。

デバック実行にてAPIが起動された状態となりましたので、以下のようにSwagger UIからAPIに帳票生成のリクエストを行います。

リクエスト時に送信したJSONデータが帳票のデータソースとして設定され、PDF形式で帳票が生成されました。

Azure環境へデプロイ

ローカル環境での動作確認が行えましたので、最後にAzure環境へデプロイしていきます。

ライセンス設定

まずはAzure環境で利用できるライセンスファイルを生成します。ライセンス生成方法は先ほどと同様です。以下のように、対象とするアセンブリ名の前に「Microsoft.Azure.WebJobs.Script.WebHost」を設定します。

Azure用ライセンス付与

フォント設定

今回使用している帳票レイアウトでは、フォントを「IPAゴシック」に設定しています。ローカル環境では該当フォントがインストールされているため、問題なく表示されていましたが、Azure環境上にはこのフォントがインストールされていないため、フォントファイルを含めてデプロイする必要があります。

まず、プロジェクト内に「Fonts」フォルダを作成し、そのフォルダ内にフォントファイルを格納します。※ IPAゴシックファイルはこちらからダウンロード可能です。

フォントファイルの追加

格納したフォントファイルを作成した帳票で利用するため、「EmbeddedFontResolver」クラスを追加します。

using GrapeCity.ActiveReports;
using GrapeCity.Documents.Text;
using System.Reflection;

internal sealed class EmbeddedFontResolver : IFontResolver
{
    internal static readonly IFontResolver Instance = new EmbeddedFontResolver();
    internal static readonly Font Ipag;
    static EmbeddedFontResolver()
    {
        Ipag = Font.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream("FunctionReportsApp.Fonts.ipag.ttf"));
    }
    internal EmbeddedFontResolver() { }
    public FontCollection GetFonts(string familyName, bool isBold, bool isItalic)
    {
        var fonts = new FontCollection();
        fonts.AppendFonts(new Font[] { Ipag }, true);
        return fonts;
    }
}

さらに、実装済みの「Function1」内で、次のコードの強調表示されている箇所において、レポートオブジェクトに追加したクラスを利用してフォントの設定を行います。

using System.IO;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using GrapeCity.ActiveReports;
using GrapeCity.ActiveReports.Extensibility.Rendering;
using GrapeCity.ActiveReports.Rendering.IO;
using System.Collections.Specialized;
using System.Globalization;
using System.Threading;
using static GrapeCity.Enterprise.Data.DataEngine.DataProcessing.DataProcessor;
using System.Reflection;
using System;


namespace FunctionReportsApp
{
    public class Function1
    {
        private readonly ILogger<Function1> _logger;

        public Function1(ILogger<Function1> log)
        {
            _logger = log;
        }

        [FunctionName("Function1")]
        [OpenApiOperation(operationId: "Run", tags: new[] { "datajson" })]
        [OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
        [OpenApiParameter(name: "datajson", In = ParameterLocation.Query, Required = true, Type = typeof(string), Description = "The **DataJson** parameter")]
        [OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/pdf", bodyType: typeof(byte[]), Description = "The OK response")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");
            //クエリパラメータを取得
            string datajson = req.Query["datajson"];

            //リクエストボディを取得
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            // クエリパラメータが空の場合、リクエストボディのdatajsonを使用  
            if (datajson == null && data != null)
            {
                // datajsonが存在するか確認し、文字列に変換する  
                datajson = JsonConvert.SerializeObject(data.datajson);
            }

            //ActionResultを設定
            ActionResult result;

            try
            {

                //アセンブリ取得
                var assembly = Assembly.GetExecutingAssembly();

                //レポートファイルのストリームを取得
                var stream = assembly.GetManifestResourceStream("FunctionReportsApp.Invoice_bluegray.rdlx");
                var streamReader = new StreamReader(stream);

                //レポートファイルを読み込み
                GrapeCity.ActiveReports.PageReport pageReport = new PageReport(streamReader);
                //フォントの設定
                pageReport.FontResolver = new EmbeddedFontResolver();
                //レポートにパラメータを設定
                pageReport.Document.Parameters[0].Values[0].Value = datajson;

                //PDF出力設定
                GrapeCity.ActiveReports.Export.Pdf.Page.Settings pdfSetting = new GrapeCity.ActiveReports.Export.Pdf.Page.Settings();
                GrapeCity.ActiveReports.Export.Pdf.Page.PdfRenderingExtension pdfRenderingExtension = new GrapeCity.ActiveReports.Export.Pdf.Page.PdfRenderingExtension();
                GrapeCity.ActiveReports.Rendering.IO.MemoryStreamProvider outputProvider = new GrapeCity.ActiveReports.Rendering.IO.MemoryStreamProvider();

                //PDFレンダリング
                pageReport.Document.Render(pdfRenderingExtension, outputProvider, pdfSetting);

                //PDFファイルをMemoryStreamに変換
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                outputProvider.GetPrimaryStream().OpenStream().CopyTo(ms);

                //ActionResultにPDFファイルを設定
                result = new FileContentResult(ms.ToArray(), "application/pdf")
                {
                    FileDownloadName = "report.pdf"
                };
                

            }
            catch (Exception ex)    //エラー処理
            {
                //エラー時はステータスコード500を返す
                result = new StatusCodeResult(500);


                //エラーログを出力
                _logger.LogInformation(ex.Message);
            }

            return result;
        }
    }
}

デプロイ

ライセンス設定とフォント設定が完了し、Azureへのデプロイの準備が整いましたので、アプリケーションをAzure上に発行します。

まず、ソリューションエクスプローラーからプロジェクトを選択し、コンテキストメニューから「発行」をクリックします。

Azureへデプロイ1

ウィザード形式のダイアログが表示されたら、「Azure」を選択し、[次へ]を押します。

Azureへデプロイ2

つぎに、「Azure Function App(Windows)」を選択し、[次へ]を押します。

Azureへデプロイ3

ウィザードで「Functionsインスタンス」タブが表示されたら、[新規作成]を押します。

Azureへデプロイ4

インスタンス作成ダイアログでは、以下のように必要な情報を入力し、[作成]を押します。

Azureへデプロイ5

「Functionsインスタンス」タブに戻り、以下のようにインスタンスが設定されていることを確認したうえで[次へ]を押します。

Azureへデプロイ6

「API Management 」タブでは、「この手順をスキップする」にチェックしたうえで、[完了]を押します。

Azureへデプロイ7

ウィザードが完了後、プロジェクト公開タブ内の[発行]を押し、Azure環境へデプロイします。

Azureへデプロイ8

デプロイ後、次のように「公開が成功しました」のメッセージが表示されれば、デプロイ完了です。

Azureへデプロイ9

動作確認

最後に動作確認を行っていきますが、最初に、Azure PortalにログインしAPIにアクセスする為のAPIキーを取得します。

Azure Portalにログイン後、上部の検索ボックスに作成したアプリ名「ActiveReportsFunctionReportsApp 」を入力し、Azure Functionsの管理画面を開きます。

APIキーの取得1

管理画面を開いたら、以下のように「アプリキー」を選択します。

APIキーの取得2

以下の画面に遷移しますので、「default」キーをコピーしておきます。

APIキーの取得3

ブラウザで「デプロイ先のホストアドレス/api/swagger/ui」のURLを入力し、ローカル環境と同様にSwagger UIを利用して動作確認を行います。取得済みのAPIキーをSwagger UI上で設定することで、ローカル環境と同じように動作確認が可能です。

Azure環境においても、ローカル環境と同様にPDF帳票が生成されました。

さいごに

今回の記事では、ActiveReports for .NET 18.0Jでサポートされた「Azure Functions」を利用し、帳票機能をWeb APIとして作成する方法をご紹介しました。帳票機能をWeb API化することで、従来のプラットフォームに加え、ローコードプラットフォームなどからも利用が可能となり、使用シーンが大幅に広がります。さまざまなプラットフォームで帳票アプリを利用したいとお考えの方は、今回ご紹介した方法をぜひ参考にしていただければ幸いです。

今回の内容について動作を確認されたい方は、是非トライアル版ダウンロードの上、GitHub上のコードをお試しください。

製品の機能を手軽に体験できるデモアプリケーションも公開しておりますので、こちらもご確認ください。

また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。

\  この記事をシェアする  /