概要
プレビュー段階ですが、PowerApps/Automateのカスタムコネクタ内でC#コードを使って処理が記述できるようになりました。
C#のコードで外部APIへの要求/応答に手を加えたりすることが可能です。
更に、外部APIには要求せずC#内で処理して応答といったことも可能で、以前はAzure Functionsで実装したような処理をカスタムコネクタ内で完結させるといったことも可能です!
やってみた事例
Power Appsからテキストを受け取ってオウム返しする。
Power Appsから画像を受け取り簡単な画像処理を行ってPower Appsに応答する。
外部APIへのリクエスト時にAuthorizationヘッダを生成して付加、カスタムコネクタでOAuth1.0のAPIに接続する。
制約条件など
カスタムコードには以下のような制約事項がありできることが限られます。
C#の中でも比較的簡単なコードしか書けないということで、これもローコードかもしれません(違
コードは全てScriptクラス内に書く必要がある
使用可能なクラスライブラリに制限がある
5 秒以内に実行を終了、コードサイズが1MBまでなどの制限がある
呼び出し初回に数秒の起動時間あり、裏側はAzure Functions無料枠?
それからカスタムコネクタなので、PowerAppsの開発者環境か有償プランが必要です。
試しに作ってみる
今回のコード
リクエストボディに入力されたテキストをそのまま返す。
追加でContextやRequestの内容を確認用に表示する。
カスタムコネクタの登録時にバックエンドAPIのURLを登録しますが、今回の例ではバックエンドにはアクセスしません。処理内容はコードでオーバーライドします。
コード
public class Script: ScriptBase {
public override async Task<HttpResponseMessage> ExecuteAsync() {
// Create a new response
var response = new HttpResponseMessage();
var contentAsJson = JObject.Parse(await Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false));
// Set the content
// Initialize a new JObject and call .ToString() to get the serialized JSON
response.Content = CreateJsonContent(new JObject {
["message"] = (string)contentAsJson["message"],
["request_uri"] = Context.Request.RequestUri,
["operation_id"] = Context.OperationId,
["correlation_id"] = Context.CorrelationId,
["content"] = await Context.Request.Content.ReadAsStringAsync()
}.ToString());
return response;
}
}
簡単な解説
ScriptBaseクラスを継承したScriptクラスがあり、カスタムコネクタが呼び出されるとExecuteAsyncメソッドが実行されます。
ExecuteAsyncはoverrideしているため、カスタムコネクタの処理内容はこのメソッド内のコードに上書きされます。
ContextやCreateJsonContentなどが登場しますが、これはベースクラスで定義されているものです。
Contextにはカスタムコネクタの情報やリクエスト情報が格納されています。
https://docs.microsoft.com/ja-jp/connectors/custom-connectors/write-code#definition-of-supporting-classes-and-interfaces
以下に開発環境を用意しました。
カスタムコードの開発環境
現状はカスタムコネクタ上でコードを書いてもコンパイルエラーの内容を教えてくれないため、公式に従い自分で開発環境を整えます。
今回はVisualStudioでテスト環境を整えてみます。
方法は以下にのブログに投稿しました。
またはこちらのKUSOリポジトリをクローンで
カスタムコネクタの作成
カスタムコネクタの基本操作についてはこちらが詳しいです。
カスタムコネクタの画面で一から作成を選択します。
全般画面
ホストの部分は架空の「api.contoso.com」としておきます。
今回は実際のAPIは呼び出さずC#内で処理を行って応答しますが、形式上指定する必要があるためです。
※設定保存のやり方によってはC#コードが無効になり入力したURLにリクエストされるため、送信されてもいいURLで。
セキュリティ
→無しでOKです。
定義画面
新しいアクションを追加し「Hello」などのアクションを作成します。
要求内容はサンプルからのインポートで以下のように設定します。
5.コード画面
コードが有効をオンにして、作成したコードを貼り付けます。
特定のアクションのみコードを有効にできるようですが、今回は指定しません。
ここで必ずコネクタの更新をクリックします。
カスタムコードを有効にしていますと表示され、その後正常に保存できたことを確認します。
現状、他の画面でコネクタの更新を行ってしまうと、コードが無効になり指定のURIに要求されてしまいます。
なので最後は必ずこの画面で、一旦コードを編集してからコネクタの更新を行います。
※コードを編集せず更新をすると反映されない場合あり
もしコードが反映されていないとテストのときに失敗し、api.contoso.comの名前解決ができないと表示されます。
6.テスト画面
接続が作成されていない場合は接続を作成。
テストの前には上記の通り、最後はコード画面で編集し、コネクタの更新をクリックしてコードを反映させます。
パラメータを適当に設定して、テスト操作します。
以下のように応答が返ってくれば成功です。
OperationIdは操作IDが入るようです。CorrelationIdは分かりません・・
7.応答の定義
先ほどのボディをコピーし、応答部分にインポートして定義を作成します。
最後は必ずコード画面でコードを編集して更新します。
PowerAppsからの呼び出し
アプリを作成し、先ほど作成したカスタムコネクタへの接続を作成します。
以下のような感じで呼び出すと応答結果が得られました。
以下その他
クラスを利用する
Script以外にClassを書いて呼び出してもエラーとなります。
読みにくいですがClassの入れ子なら利用可能です。以下はJSONをデシリアライズする例です。
public class Script : ScriptBase {
public override async Task<HttpResponseMessage> ExecuteAsync() {
// Parse request body
var entity = JsonConvert.DeserializeObject<PersonEntity>(await Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false));
// Create a new response
var response = new HttpResponseMessage();
response.Content = CreateJsonContent(new JObject {
["fullname"] = entity.FullName
}.ToString());
return response;
}
class PersonEntity {
public string SurName { get;}
public string GivenName { get;}
public string FullName =>
$"{SurName} {GivenName}";
[JsonConstructor]
public PersonEntity(string surname, string givenname) {
SurName = surname;
GivenName = givenname;
}
}
}
APIを呼び出して応答を変形する(情報を削減)
QiitaAPIを呼び出し、渡されたワードで記事を検索して結果を返す例です。
標準の応答スキーマに不要の情報がありPowerAppsで扱いにくいといった場合に、カスタムコネクタ側で応答を加工し情報を削減します。
QiitaAPIの呼び出し定義はカスタムコネクタ側で設定します。
要求の例
情報を削減した応答
public class Script : ScriptBase {
public override async Task<HttpResponseMessage> ExecuteAsync() {
// カスタムコネクタで定義したAPIを呼び出し
var responseMessage = await Context.SendAsync(Context.Request, CancellationToken).ConfigureAwait(false);
if (responseMessage.StatusCode !=HttpStatusCode.OK ) {
throw new ArgumentException(responseMessage.ReasonPhrase);
}
// レスポンスのコンテンツを取得
var requestBody = await responseMessage.Content.ReadAsStringAsync();
// Parse request body
var entities = JsonConvert.DeserializeObject<List<QiitaEntity>>(requestBody);
// Create a new response
var response = new HttpResponseMessage();
response.Content = CreateJsonContent(JsonConvert.SerializeObject(entities));
return response;
}
//特定のフィールドしか用意しないことで不要なフィールドを削除
class QiitaEntity {
public string Title { get; private set; }
public string Body { get; private set; }
public DateTime UpdateAt { get; private set; }
public int LikesCount { get; private set; }
public string Url { get; private set; }
[JsonConstructor]
public QiitaEntity(string title, string body, DateTime updated_at, int likes_count, string url) {
Title = title;
var singleLineBody = Regex.Replace(body, @"\t|\n|\r", "");
Body = SubstringSafe(singleLineBody, 0, 100);
UpdateAt = updated_at;
LikesCount = likes_count;
Url = url;
}
static string SubstringSafe(string value, int startIndex, int length) {
return new string((value ?? string.Empty).Skip(startIndex).Take(length).ToArray());
}
}
}
あとがき
カスタムコネクタ内でAPIを完結させるとPowerAppsの料金だけでよいですし、リソース管理が楽になりますね。
色々な活用の可能性がありそうです。
値下げして大丈夫なのか・・
PowerAppsにない関数をカスタムコードで実現!と思いましたがPowerAppsでもかなりのことができるようですので、カスタムコードでしかできないことが思いつきませんね・・
次回の予定
画像処理の例
カスタムコネクタでOAuth1.0のAPIに接続してみた
↧