やる気駆動型エンジニアの備忘録

WPF(XAML+C#)の話題を中心に.NET/Android/CI やたまに趣味に関するブログです

Azure Functionsで自作したカスタムコネクタをPower Automateで使おうとしたらハマった事象と原因と回避策について

随分とタイトルの示す範囲が大きくなりましたが、そういうことです。

やろうとしていたことは「Azure Functionsで作成した関数アプリをPower Automateで使う」という至ってシンプルなものでした。 しかし、いざ実践してみると期待した通りに動作しなかったため、しばらく悩み続けてなんとか解消した話です。

事象

Azure Functionsで作成した関数アプリをカスタムコネクタを使って呼び出すようにしました。 新規にフローを作成してカスタムコネクタのアクションを実行すると失敗し、次のようなメッセージが表示されました。

ResponseSwaggerSchemaValidationFailure. API 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' が、種類 'OpenApiConnection' のワークフロー操作 '<API名>' について無効な応答を返しました。エラーの詳細: 'API 操作 '<API名>' では、プロパティ 'body' の種類を 'Object' にする必要がありますが、種類が 'String' です。'

内容的には json の応答を Object から String にしろと言うことのようです。


2020/11/16 追記 (コメント頂きましたので修正)

内容的にはカスタムコネクタでは Object が来る想定だったけど String が来たよ、ということのようです。


ちなみにAzure Functionsでは、作成した関数の応答は json で返していました。

ただし、この現象が発生するのは実際のフローのみで、カスタムコネクタの[テスト]タブで実行した場合や、HTTP アクションで同じ URL 呼び出しを行っても同じ現象は発生しませんでした。

似たような事象が発生していないか Power Automate で探したところ、ドンピシャなスレッドがありました。が、質問した方はカスタムコネクタを修正して対応したようです。(良いのかな…)

Error in Power Automate Flow When Using Custom Connector -requires the property 'body' to be of type 'String' but is of type 'Object'

原因

色々試した結果、原因はAzure Functions側にあることがわかりました。 応答する際に jsonシリアライズしたのは良いのですが、json 文字列を body に設定してしまっていました。

はい、つまり完全に私のしょーもないミスが原因ですね。

具体的には次のようなコードになっていました。

[FunctionName("function")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req, ILogger log)
{
    ...省略
    var json = Newtonsoft.Json.JsonConvert.SerializeObject(result, _settings);

    return new OkObjectResult(json);
}

このまま関数を呼び出すと応答結果のContent-Typeは、text/plainになってしまいます。 要はこれをapplication/jsonにすれば良いわけですね。

対策

今回のケースでは応答は json で返し、シリアライズする際の書式設定はNewtonsoft.Json.JsonSerializerSettingsで設定していたため、Microsoft.AspNetCore.Mvc.JsonResultで応答する手法を取りました。 この方法にたどり着くまでに twitter 上でしばてぃさん(@shibatea365)に何度もアドバイスいただきました。感謝の極みです😂

さて、変更後のコードはこのようになります。

[FunctionName("function")]
public async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req, ILogger log)
{
    ...省略
    return new JsonResult(response, _settings);
}

なお、JsonResultを使う場合はMicrosoft.AspNetCore.Mvc.NewtonsoftJsonパッケージを追加する必要があります。 また、csprojファイルに<FrameworkReference Include="Microsoft.AspNetCore.App" />を追加しないと以下のような事象が発生するそうなのでご注意を。

MethodNotFound when trying to call JsonResult ctor with JsonSerializerSettings #1907

これを実行するとContent-Typeapplication/jsonになり、Power Autoamte のフローも成功します。

最後に

本当にしょうもなことが原因で何日もハマってしまいました… Azure Functionsについてはまだまだ初学者であったとは言え、今回のような事象はユニットテストでレスポンス ヘッダーまでテストして入れば検知できたはずです(やってませんでした!)。 ちょっと手を抜いた結果がこれです。 手を抜くの絶対ダメ!!!