次の方法で共有


強化されたクイック スタート

この記事では、再利用可能な HttpClient およびエラー処理方法を追加することによって、クイック スタート 記事のコードをどのようにリファクターするかを示します。 この記事を開始する前に、クイック スタート記事の手順を実行して、新しい Visual Studio プロジェクトを作成するか、または Visual Studio プロジェクトの MSAL バージョンをダウンロードします。

この強化されたクイック スタートの後で行き詰まった場合は、完成したソリューションをダウンロードできます。

接続文字列で資格情報を渡すことを有効にする

コード内にユーザー資格情報を配置することはお勧めできません。 ユーザーの資格情報をどのように取り込むかは、作成しているクライアントのタイプによって異なります。 このコンソール アプリケーション用には、App.config ファイル内に資格情報を設定します。資格情報をコード外へ移動する便利な手段だからです。 またこれは Web API データ操作のサンプル (C#) で使用した方法です。よって、この方法を理解していれば、他の Web API サンプルがどのように機能するかが簡単にわかります。

これらの認証情報の受け渡しを有効にするには、次の 3 つの手順が必要です。

  1. System.Configuration.ConfigurationManager NuGet パッケージを Visual Studio プロジェクトに追加する
  2. アプリケーション構成ファイルを追加する
  3. Program.cs に using ディレクティブを追加する

System.Configuration.ConfigurationManager NuGet パッケージを Visual Studio プロジェクトに追加する

  1. Solution Explorer で、依存関係 を右クリックして、コンテキスト メニューで NuGet パッケージの管理を選択します。
  2. System.Configuration.ConfigurationManager という名前の NuGet パッケージを探し、それを選択し、次にインストール選択します。

System.Configuration.ConfigurationManager パッケージをインストールする

アプリケーション構成ファイルを追加する

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、[追加] > [新しい項目] を選択します...

  2. アプリケーション構成ファイルを選択します。

    これらの手順により、プロジェクト内に App.config という名前の新しいファイルが作成されます。

  3. ソリューション エクスプローラーで、App.config ファイルを開きます。 次のように見えるはずです。

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    </configuration>
    
  4. <configuration> 要素を編集し、以下に示すように connectionStrings ノードを加えます。

    <?xml version="1.0" encoding="utf-8" ?>
       <configuration>
       <!--Online using Microsoft 365-->
       <add name="Connect"
             connectionString="Url=https://yourorg.api.crm.dynamics.com;Username=yourname@yourorg.onmicrosoft.com;Password=mypassword;" />
    </connectionStrings>
    </configuration>
    

これは、名前で参照できる接続文字列を作成します。この場合は Connect です。これにより、必要に応じて複数の接続を定義できます。

Microsoft Dataverse テスト環境への接続に必要なものに一致するように、 connectionString パラメータの接続文字列 UrlUsername および Password の値を編集します。

using ディレクティブを Program.cs に追加

Program.cs ファイルの先頭に、この using ディレクティブを追加します:

using System.Configuration;

ヘルパー コードを追加

クイック スタート の例では、すべてのコードは Program.cs ファイル内にあります。 HttpClient の接続と作成を扱うコードを、ヘルパー メソッドの別のファイルに移動します。

これらのヘルパーは、Web API データ操作のサンプル (C#) で使用された SampleHelper.cs ファイルでも使用されます。 これらのヘルパーを理解すれば、これらがサンプルでどのように使用されるかを理解します。

  1. ソリューション エクスプローラーで、プロジェクトを右クリックし、追加 > クラス... を選択して (または Shift+Alt+C を押して) 新しいアイテムの追加 ダイアログを開きます。

  2. クラス ファイルの名前を指定します。 Web API データ操作のサンプル (C#) によって使用されるパターンをフォローするには、SampleHelpers.cs と名前を付けます。

    注意

    クラスの名前によって、 メイン プログラム内でのこれらのヘルパー プロパティとメソッドの参照方法が決まります。 残りの手順では、クラスに SampleHelpers という名前を付けたと想定しています。

  3. 次のコードを SampleHelpers.cs ファイルに追加します。

    
    using System;
    using System.Linq;
    using System.Net.Http;
    
    namespace EnhancedQuickStart
    {
    /// <summary>
    /// Shared code for common operations used by many Power Apps samples.
    /// </summary>
    class SampleHelpers
    {
          //These sample application registration values are available for all online instances.
          //You can use these while running sample code, but you should get your own for your own apps
          public static string clientId = "51f81489-12ee-4a9e-aaae-a2591f45987d";
          public static string redirectUrl = "app://58145B91-0C36-4500-8554-080854F2AC97";
    
          /// <summary>
          /// Method used to get a value from the connection string
          /// </summary>
          /// <param name="connectionString"></param>
          /// <param name="parameter"></param>
          /// <returns>The value from the connection string that matches the parameter key value</returns>
          public static string GetParameterValueFromConnectionString(string connectionString, string parameter)
          {
             try
             {
                return connectionString.Split(';').Where(s => s.Trim().StartsWith(parameter)).FirstOrDefault().Split('=')[1];
             }
             catch (Exception)
             {
                return string.Empty;
             }
          }
    
          /// <summary>
          /// Returns an HttpClient configured with an OAuthMessageHandler
          /// </summary>
          /// <param name="connectionString">The connection string to use.</param>
          /// <param name="clientId">The client id to use when authenticating.</param>
          /// <param name="redirectUrl">The redirect Url to use when authenticating</param>
          /// <param name="version">The version of Web API to use. Defaults to version 9.2 </param>
          /// <returns>An HttpClient you can use to perform authenticated operations with the Web API</returns>
          public static HttpClient GetHttpClient(string connectionString, string clientId, string redirectUrl, string version = "v9.2")
          {
             string url = GetParameterValueFromConnectionString(connectionString, "Url");
             string username = GetParameterValueFromConnectionString(connectionString, "Username");
             string password = GetParameterValueFromConnectionString(connectionString, "Password");
             try
             {
                HttpMessageHandler messageHandler = new OAuthMessageHandler(url, clientId, redirectUrl, username, password,
                               new HttpClientHandler());
    
                HttpClient httpClient = new HttpClient(messageHandler)
                {
                      BaseAddress = new Uri(string.Format("{0}/api/data/{1}/", url, version)),
    
                      Timeout = new TimeSpan(0, 2, 0)  //2 minutes
                };
    
                return httpClient;
             }
             catch (Exception)
             {
                throw;
             }
          }
    
          /// <summary> Displays exception information to the console. </summary>
          /// <param name="ex">The exception to output</param>
          public static void DisplayException(Exception ex)
          {
             Console.WriteLine("The application terminated with an error.");
             Console.WriteLine(ex.Message);
             while (ex.InnerException != null)
             {
                Console.WriteLine("\t* {0}", ex.InnerException.Message);
                ex = ex.InnerException;
             }
          }
    }
    }
    
  4. OAuthMessageHandler クラスを、以下に提供されているコードを使用して、独自のクラスファイル内に追加します。

    このクラスにより、操作が実行されるたびにアクセス トークンが更新されます。 各アクセス トークンは約 1 時間後に期限切れとなります。 このクラスは DelegatingHandler を実装します。これは、操作が実行されるたびに Microsoft Authentication Library (MSAL) 認証コンテキストと連携して正しい AcquireToken バリエーションを呼び出すため、トークンの有効期限を明示的に管理する必要はありません。

    using Microsoft.Identity.Client;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Security;
    using System.Threading.Tasks;
    
    namespace EnhancedQuickStart
    {
    /// <summary>
    /// Custom HTTP message handler that uses OAuth authentication through
    /// Microsoft Authentication Library (MSAL).
    /// </summary>
    class OAuthMessageHandler : DelegatingHandler
    {
          private AuthenticationHeaderValue authHeader;
    
          public OAuthMessageHandler(string serviceUrl, string clientId, string redirectUrl, string username, string password,
                HttpMessageHandler innerHandler)
             : base(innerHandler)
          {
    
             string apiVersion = "9.2";
             string webApiUrl = $"{serviceUrl}/api/data/v{apiVersion}/";
    
             //Build Microsoft.Identity.Client (MSAL) OAuth Token Request
             var authBuilder = PublicClientApplicationBuilder.Create(clientId)
                            .WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs)
                            .WithRedirectUri(redirectUrl)
                            .Build();
             var scope = serviceUrl + "//.default";
             string[] scopes = { scope };
    
             AuthenticationResult authBuilderResult;
             if (username != string.Empty && password != string.Empty)
             {
                //Make silent Microsoft.Identity.Client (MSAL) OAuth Token Request
                var securePassword = new SecureString();
                foreach (char ch in password) securePassword.AppendChar(ch);
                authBuilderResult = authBuilder.AcquireTokenByUsernamePassword(scopes, username, securePassword)
                            .ExecuteAsync().Result;
             }
             else
             {
                //Popup authentication dialog box to get token
                authBuilderResult = authBuilder.AcquireTokenInteractive(scopes)
                            .ExecuteAsync().Result;
             }
    
             //Note that an Azure AD access token has finite lifetime, default expiration is 60 minutes.
             authHeader = new AuthenticationHeaderValue("Bearer", authBuilderResult.AccessToken);
          }
    
          protected override Task<HttpResponseMessage> SendAsync(
                   HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
          {
             request.Headers.Authorization = authHeader;
             return base.SendAsync(request, cancellationToken);
          }
    }
    }
    

Program.cs を更新する

これで 接続文字列で資格情報を渡すことを有効にする および ヘルパー コードを追加する に変更を施したため、 Program.cs ファイルで次のものだけを含むように Main メソッドを更新できます。

using Newtonsoft.Json.Linq;
using System;
using System.Configuration;
using System.Net.Http;

namespace EnhancedQuickStart
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //Get configuration data from App.config connectionStrings
                string connectionString = ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;

                using (HttpClient client = SampleHelpers.GetHttpClient(connectionString, SampleHelpers.clientId, SampleHelpers.redirectUrl))
                {
                    // Use the WhoAmI function
                    var response = client.GetAsync("WhoAmI").Result;

                    if (response.IsSuccessStatusCode)
                    {
                        //Get the response content and parse it.
                        JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
                        Guid userId = (Guid)body["UserId"];
                        Console.WriteLine("Your UserId is {0}", userId);
                    }
                    else
                    {
                        Console.WriteLine("The request failed with a status of '{0}'",
                                    response.ReasonPhrase);
                    }
                    Console.WriteLine("Press any key to exit.");
                    Console.ReadLine();
                }
            }
            catch (Exception ex)
            {
                SampleHelpers.DisplayException(ex);
                Console.WriteLine("Press any key to exit.");
                Console.ReadLine();
            }
        }
    }
}

これはコード量がより少なく、HttpClient を使うたびにエラー処理とアクセス トークンを更新する手段を追加しました。

ただし、JObject の下に赤い波線があることがわかります。 このプロジェクトをビルドしようとすると失敗します。

クイック スタート では System.Text.Jsonを使用しましたが、このコードは NewtonSoft を使用します。

Newtonsoft.Json をインストールする

  1. その下に赤い波線がある JObject を右クリックし、クイック アクションとリファクタリング... を選択します。
  2. パッケージ「Newtonsoft.Json」をインストールする > 最新バージョンを見つけてインストールする を選択します。

プログラムを実行する

F5 キーを押してプログラムを実行します。 クイック スタート サンプルのように、出力は次のようになります。

Your UserId is 969effb0-98ae-478c-b547-53a2968c2e75
Press any key to exit.

再利用可能なメソッドを作成

Program.Main メソッドでコードの総量を減らしましたが、1 つの操作を呼び出すためだけにプログラムを書くわけではありません。そして、たった 1 つの操作を呼び出すためだけにそれほど多くのコードを書くのは現実的ではありません。

このセクションでは、これを変更する方法を示します。

var response = client.GetAsync("WhoAmI").Result;

if (response.IsSuccessStatusCode)
{
  //Get the response content and parse it.
  JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
  Guid userId = (Guid)body["UserId"];
  Console.WriteLine("Your UserId is {0}", userId);
}
else
{
  Console.WriteLine("The request failed with a status of '{0}'",
      response.ReasonPhrase);
}

結果として、次を実現します。

WhoAmIResponse response = WhoAmI(client);
      Console.WriteLine("Your system user ID is: {0}", response.UserId);

開始する前に、Web API 参照にアクセスして、次の記事を確認しておくことをお勧めします。

WhoAmI 関数 がどのように WhoAmIResponse ComplexType を返し、WhoAmIResponse ComplexType が次の 3 つの GUID プロパティを含んでいることに注意してください: BusinessUnitIdUserId および OrganizationId

追加するコードは、単に関数を、パラメーターとして HttpClient を受け取る再利用可能なメソッドにモデル化します。

注意

これをどのように行うかは、個人用な好みの問題です。 この設計は比較的単純なために提供されています。

Visual Studio プロジェクトで、次のステップを実行します:

  1. Program クラスを編集して部分クラスにします。

    上位では、これを変更します。

    class Program

    結果として、次を実現します。

    partial class Program

  2. ProgramMethods.cs ファイルという名前の新しいクラスを作成します

    ProgramMethods.cs では、これを変更します。

    class ProgramMethods

    結果として、次を実現します。

    partial class Program

    このように、ProgramMethods.cs ファイルの Program クラスは、Program.cs ファイルの元の Program クラスの単なる拡張です。

  3. 次のディレクティブを ProgramMethods.cs ファイルの上位に追加します。

    using Newtonsoft.Json.Linq;
    using System;
    using System.Net.Http;
    
  4. 次のメソッドを ProgramMethods.cs ファイルの Program クラスに追加します。

    public static WhoAmIResponse WhoAmI(HttpClient client) {
      WhoAmIResponse returnValue = new WhoAmIResponse();
      //Send the WhoAmI request to the Web API using a GET request.
      HttpResponseMessage response = client.GetAsync("WhoAmI",
              HttpCompletionOption.ResponseHeadersRead).Result;
      if (response.IsSuccessStatusCode)
      {
          //Get the response content and parse it.
          JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
          returnValue.BusinessUnitId = (Guid)body["BusinessUnitId"];
          returnValue.UserId = (Guid)body["UserId"];
          returnValue.OrganizationId = (Guid)body["OrganizationId"];
      }
      else
      {
          throw new Exception(string.Format("The WhoAmI request failed with a status of '{0}'",
                  response.ReasonPhrase));
      }
      return returnValue;
    }
    
  5. 次のクラスを、Program クラスの外側で ProgramMethods.cs の名前空間内に追加します。

    public class WhoAmIResponse
    {
        public Guid BusinessUnitId { get; set; }
        public Guid UserId { get; set; }
        public Guid OrganizationId { get; set; }
    }
    
  6. 元の Program.cs ファイル内の Program.Main メソッド:

    編集前:

    var response = client.GetAsync("WhoAmI").Result;
    
    if (response.IsSuccessStatusCode)
    {
      //Get the response content and parse it.
      JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
      Guid userId = (Guid)body["UserId"];
      Console.WriteLine("Your UserId is {0}", userId);
    }
    else
    {
      Console.WriteLine("The request failed with a status of '{0}'",
                  response.ReasonPhrase);
    }
    

    編集後:

    WhoAmIResponse response = WhoAmI(client);
    Console.WriteLine("Your system user ID is: {0}", response.UserId);
    
  7. F5 キーを押してサンプルを実行します。以前と同じ結果を取得します。

トラブルシューティング​​

このサンプルを実行するときに問題がある場合、https://github.com/Microsoft/PowerApps-Samples の GitHub のリポジトリから、すべての Power Apps のサンプルをダウンロードできます 。

重要

GitHub リポジトリのすべてのサンプルは、PowerApps-Samples:cds\App.config にある共通の App.config を使用するように構成されています。 接続文字列を設定するときは、このファイルを編集する必要があります。 その場合、資格情報を再度設定することなくすべてのサンプルを実行できます。

テンプレート プロジェクトを作成

この記事を閉じる前に、プロジェクト テンプレートとしてプロジェクトを保存することを検討します。 次に、今後の学習プロジェクトのためにそのテンプレートを再利用すると、新しいプロジェクトの設定の時間と労力を節約できます。 このため、プロジェクトが Microsoft Visual Studioでオープンしている間に、 ファイル メニューで、テンプレートのエクスポートを選択します。 テンプレート ウィザードのエクスポート の指示に従い、テンプレートを作成します。

次のステップ

詳細については、以下のリソースを参照してください。

注意

ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)

この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。