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
では次のことが行われます。
- クラス レベルのインスタンスとして
TodoService
をインスタンス化します Items
と呼ばれるコレクションを作成して、TodoItem
オブジェクトを格納します- 省略可能な
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 のポート番号のみです。