次の方法で共有


ASP.NET Web サービス (ASMX) を使用する

ASMX は、簡易オブジェクト アクセス プロトコル (SOAP) を使用してメッセージを送信する Web サービスを構築する機能を提供します。 SOAP は、Web サービスを構築およびアクセスするためのプラットフォームに依存せず、言語にも依存しないプロトコルです。 ASMX サービスのお客様は、サービスの実装に使用されるプラットフォーム、オブジェクト モデル、またはプログラミング言語について何も知る必要はありません。 理解する必要があるのは SOAP メッセージを送受信する方法のみです。 この記事では、Xamarin.Forms アプリケーションから ASMX SOAP サービスを使用する方法について説明します。

SOAP メッセージは、次の要素を含む XML ドキュメントです。

  • XML ドキュメントを SOAP メッセージとして識別する、Envelope という名前のルート要素。
  • 認証データなどのアプリケーション固有の情報を含む省略可能な Header 要素。 Header 要素が存在する場合、それは Envelope 要素の最初の子要素である必要があります。
  • 受信者に向けた SOAP メッセージを含む必須の Body 要素。
  • エラー メッセージを示すために使用される省略可能な Fault 要素。 Fault 要素が存在する場合、それは Body 要素の子要素である必要があります。

SOAP は、HTTP、SMTP、TCP、UDP など、多くのトランスポート プロトコルで動作します。 ただし、ASMX サービスは HTTP 経由でのみ動作します。 Xamarin プラットフォームでは、HTTP 経由の標準の SOAP 1.1 実装がサポートされています。これには、標準の ASMX サービス構成の多くに対するサポートが含まれています。

このサンプルには、物理デバイスまたはエミュレートされたデバイスで実行されるモバイル アプリケーションと、データを取得、追加、編集、削除するメソッドを提供する ASMX サービスが含まれます。 次のスクリーンショットに示すように、モバイル アプリケーションが実行されると、ローカルでホストされている ASMX サービスに接続します。

サンプル アプリケーション

Note

iOS 9 以降では、App Transport Security (ATS) により、インターネット リソース (アプリのバックエンド サーバーなど) とアプリの間にセキュリティで保護された接続が適用されるため、機密情報の漏洩が防止されます。 iOS 9 用に構築されたアプリ内では ATS が既定で有効なため、すべての接続が ATS セキュリティ要件の対象となります。 接続がこれらの要件を満たさない場合は、例外を伴って失敗します。 HTTPS プロトコルを使用してインターネット リソースの通信をセキュリティで保護できない場合は、ATS をオプトアウトできます。 これは、アプリの Info.plist ファイルを更新することで実現できます。 詳細については、アプリ トランスポート セキュリティに関する記事を参照してください。

Web サービスを使用する

ASMX サービスは、次の操作を提供します。

操作 説明 パラメーター
GetTodoItems To Do アイテムのリストの取得
CreateTodoItem 新しい To Do 項目を作成する XML シリアル化 TodoItem
EditTodoItem To Do アイテムの更新 XML シリアル化 TodoItem
DeleteTodoItem To Do アイテムの削除 XML シリアル化 TodoItem

アプリケーションで使われるデータ モデルの詳細については、データのモデリングに関する記事を参照してください。

TodoService プロキシを作成する

TodoService と呼ばれるプロキシ クラスは、SoapHttpClientProtocol を拡張し、HTTP 経由で ASMX サービスと通信するためのメソッドを提供します。 このプロキシは、Visual Studio 2019 または Visual Studio 2017 の各プラットフォーム固有のプロジェクトに Web 参照を追加することによって生成されます。 Web 参照は、サービスの Web サービス記述言語 (WSDL) ドキュメントで定義されているアクションごとにメソッドとイベントを生成します。

たとえば、GetTodoItems サービス アクションによって、プロキシで GetTodoItemsAsync メソッドと GetTodoItemsCompleted イベントが生成されます。 生成されたメソッドは void 型の戻り値を持ち、親 SoapHttpClientProtocol クラスで GetTodoItems アクションを呼び出します。 呼び出されたメソッドは、サービスから応答を受け取ると、GetTodoItemsCompleted イベントを発生させ、イベントの Result プロパティ内で応答データを提供します。

ISoapService 実装を作成する

共有のクロスプラットフォーム プロジェクトがサービスと連携できるようにするために、サンプルでは、C# でのタスク非同期プログラミング モデルに従う ISoapService インターフェイスを定義します。 各プラットフォームは、ISoapService を実装して、プラットフォーム固有のプロキシを公開します。 このサンプルでは、TaskCompletionSource オブジェクトを使用して、プロキシをタスク非同期インターフェイスとして公開します。 TaskCompletionSource の使用の詳細については、以下のセクションで各アクションの種類の実装を参照してください。

サンプル SoapService では次のことが行われます。

  1. クラス レベルのインスタンスとして TodoService をインスタンス化します
  2. Items と呼ばれるコレクションを作成して、TodoItem オブジェクトを格納します
  3. 省略可能な Url プロパティのカスタム エンドポイントを TodoService で指定します
public class SoapService : ISoapService
{
    ASMXService.TodoService todoService;
    public List<TodoItem> Items { get; private set; } = new List<TodoItem>();

    public SoapService ()
    {
        todoService = new ASMXService.TodoService ();
        todoService.Url = Constants.SoapUrl;
        ...
    }
}

データ転送オブジェクトを作成する

このサンプル アプリケーションは、TodoItem クラスを使ってデータをモデル化しています。 Web サービスに TodoItem 項目を保存するには、まずプロキシで生成された TodoItem 型に変換する必要があります。 これを実行するには、次のコード例に示すように、ToASMXServiceTodoItem メソッドを使います。

ASMXService.TodoItem ToASMXServiceTodoItem (TodoItem item)
{
    return new ASMXService.TodoItem {
        ID = item.ID,
        Name = item.Name,
        Notes = item.Notes,
        Done = item.Done
    };
}

このメソッドを実行すると、新しい ASMService.TodoItem インスタンスが作成され、各プロパティは TodoItem インスタンスの同じプロパティに設定されます。

同様に、Web サービスからデータを取得する場合は、プロキシで生成された TodoItem 型から TodoItem インスタンスに変換する必要があります。 これを実行するには、次のコード例に示すように、FromASMXServiceTodoItem メソッドを使います。

static TodoItem FromASMXServiceTodoItem (ASMXService.TodoItem item)
{
    return new TodoItem {
        ID = item.ID,
        Name = item.Name,
        Notes = item.Notes,
        Done = item.Done
    };
}

このメソッドを実行すると、プロキシで生成された TodoItem 型からデータが取得され、新しく作成された TodoItem インスタンスでそれが設定されます。

データを取得する

ISoapService インターフェイスでは、RefreshDataAsync メソッドが項目コレクションと一緒に Task を返すことが想定されています。 しかし、TodoService.GetTodoItemsAsync メソッドが void を返します。 インターフェイス パターンを満たすには、GetTodoItemsAsync を呼び出し、GetTodoItemsCompleted イベントが発生するまで待機し、コレクションに読み込みます。 これにより、有効なコレクションを UI に返すことができます。

次の例では、新しい TaskCompletionSource を作成し、RefreshDataAsync メソッドで非同期呼び出しを開始し、TaskCompletionSource で提供される Task を待機します。 TodoService_GetTodoItemsCompleted イベント ハンドラーが呼び出されると、Items コレクションに読み込み、TaskCompletionSource を更新します。

public class SoapService : ISoapService
{
    TaskCompletionSource<bool> getRequestComplete = null;
    ...

    public SoapService()
    {
        ...
        todoService.GetTodoItemsCompleted += TodoService_GetTodoItemsCompleted;
    }

    public async Task<List<TodoItem>> RefreshDataAsync()
    {
        getRequestComplete = new TaskCompletionSource<bool>();
        todoService.GetTodoItemsAsync();
        await getRequestComplete.Task;
        return Items;
    }

    private void TodoService_GetTodoItemsCompleted(object sender, ASMXService.GetTodoItemsCompletedEventArgs e)
    {
        try
        {
            getRequestComplete = getRequestComplete ?? new TaskCompletionSource<bool>();

            Items = new List<TodoItem>();
            foreach (var item in e.Result)
            {
                Items.Add(FromASMXServiceTodoItem(item));
            }
            getRequestComplete?.TrySetResult(true);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(@"\t\tERROR {0}", ex.Message);
        }
    }

    ...
}

詳細については、「非同期プログラミング モデル」と TPL と従来の .NET Framework 非同期プログラミングに関する記事を参照してください。

データを作成または編集する

データを作成または編集するときは、ISoapService.SaveTodoItemAsync メソッドを実装する必要があります。 このメソッドは、TodoItem が新しい項目か更新された項目かを検出し、todoService オブジェクトに対して適切なメソッドを呼び出します。 todoService が ASMX サービスからの応答をいつ受信したかを把握できるように、CreateTodoItemCompleted および EditTodoItemCompleted イベント ハンドラーも実装する必要があります (これらは同じ操作を実行するため、1 つのハンドラーに結合できます)。 次の例では、インターフェイスとイベント ハンドラーの実装と、非同期操作に使用される TaskCompletionSource オブジェクトを示します。

public class SoapService : ISoapService
{
    TaskCompletionSource<bool> saveRequestComplete = null;
    ...

    public SoapService()
    {
        ...
        todoService.CreateTodoItemCompleted += TodoService_SaveTodoItemCompleted;
        todoService.EditTodoItemCompleted += TodoService_SaveTodoItemCompleted;
    }

    public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
    {
        try
        {
            var todoItem = ToASMXServiceTodoItem(item);
            saveRequestComplete = new TaskCompletionSource<bool>();
            if (isNewItem)
            {
                todoService.CreateTodoItemAsync(todoItem);
            }
            else
            {
                todoService.EditTodoItemAsync(todoItem);
            }
            await saveRequestComplete.Task;
        }
        catch (SoapException se)
        {
            Debug.WriteLine("\t\t{0}", se.Message);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("\t\tERROR {0}", ex.Message);
        }
    }

    private void TodoService_SaveTodoItemCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        saveRequestComplete?.TrySetResult(true);
    }

    ...
}

データの削除

データを削除するには、同様の実装が必要です。 TaskCompletionSource を定義し、イベント ハンドラーと ISoapService.DeleteTodoItemAsync メソッドを実装します。

public class SoapService : ISoapService
{
    TaskCompletionSource<bool> deleteRequestComplete = null;
    ...

    public SoapService()
    {
        ...
        todoService.DeleteTodoItemCompleted += TodoService_DeleteTodoItemCompleted;
    }

    public async Task DeleteTodoItemAsync (string id)
    {
        try
        {
            deleteRequestComplete = new TaskCompletionSource<bool>();
            todoService.DeleteTodoItemAsync(id);
            await deleteRequestComplete.Task;
        }
        catch (SoapException se)
        {
            Debug.WriteLine("\t\t{0}", se.Message);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("\t\tERROR {0}", ex.Message);
        }
    }

    private void TodoService_DeleteTodoItemCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        deleteRequestComplete?.TrySetResult(true);
    }

    ...
}

Web サービスをテストする

ローカルでホストされるサービスを使用して物理デバイスまたはエミュレートされたデバイスをテストするには、カスタム IIS 構成、エンドポイント アドレス、ファイアウォール規則を設定する必要があります。 テスト用に環境を設定する方法の詳細については、「IIS Express へのリモート アクセスを構成する」を参照してください。 WCF と ASMX のテストの違いは、TodoService のポート番号のみです。