Web サービスの概要
このガイドでは、さまざまな Web サービス テクノロジを使用する方法について説明します。 説明するトピックには、REST サービス、SOAP サービス、Windows Communication Foundation サービスとの通信が含まれます。
多くのモバイル アプリケーションは、正しく機能するためにクラウドに依存しているため、Web サービスをモバイル アプリケーションに統合することは一般的なシナリオです。 Xamarin プラットフォームでは、さまざまな Web サービス テクノロジの使用がサポートされており、RESTful、ASMX、Windows Communication Foundation (WCF) サービスを使用するための組み込みおよびサードパーティのサポートが含まれています。
Xamarin.Forms を使用しているお客様向けには、Xamarin.Forms Web サービスのドキュメントに、これらの各テクノロジを使用した完全な例があります。
重要
iOS 9 では、アプリ トランスポート セキュリティ (ATS) により、インターネット リソース (アプリのバックエンド サーバーなど) とアプリの間にセキュリティで保護された接続が適用されるので、機密情報が誤って漏えいするのを防ぐことができます。 iOS 9 用に構築されたアプリでは ATS が既定で有効になっているため、すべての接続は ATS セキュリティ要件の対象となります。 接続がこれらの要件を満たしていない場合、例外が発生して失敗します。
HTTPS
プロトコルを使ってインターネット リソースの通信をセキュリティで保護できない場合は、ATS をオプトアウトできます。 これは、アプリの Info.plist ファイルを更新することで実現できます。 詳細については、アプリ トランスポート セキュリティに関する記事を参照してください。
REST
Representational State Transfer (REST) は、Web サービスをビルドするためのアーキテクチャ スタイルです。 REST 要求は、Web ブラウザーが Web ページの取得やサーバーへのデータの送信に使用するのと同じ HTTP 動詞を使用して HTTP 経由で実行されます。 次の動詞があります。
- GET – この操作は、Web サービスからデータを取得するために使用されます。
- POST – この操作は、Web サービスに新しいデータ項目を作成するために使用されます。
- PUT – この操作は、Web サービスのデータ項目を更新するために使用されます。
- PATCH – この操作は、項目の変更方法に関する一連の命令を記述することにより、Web サービスのデータ項目を更新するために使用されます。 この動詞は、サンプル アプリケーションでは使用されていません。
- DELETE – この操作は、Web サービスのデータ項目を削除するために使用されます。
REST に準拠する Web サービス API は RESTful API と呼ばれ、次のものを使用して定義されます。
- ベース URI。
- GET、POST、PUT、PATCH、DELETE などの HTTP メソッド。
- JavaScript Object Notation (JSON) などの、データのメディアの種類。
REST は、そのシンプルさもあって、モバイル アプリケーションで Web サービスにアクセスするための主要な方法となっています。
REST サービスの使用
REST サービスを使用するために使用可能なライブラリとクラスは多数あり、以下のサブセクションでそれらについて説明します。 REST サービスの使用について詳しくは、「RESTful Web サービスの使用」を参照してください。
HttpClient
Microsoft HTTP クライアント ライブラリには、HTTP 経由での要求の送受信に使用する HttpClient
クラスが用意されています。 これにより、HTTP 要求を送信し、URI で識別されたリソースから HTTP 応答を受信するための機能が提供されます。 これらの各要求は、非同期操作として送信されます。 非同期操作の詳細については、「非同期サポートの概要」を参照してください。
HttpResponseMessage
クラスは、HTTP 要求が行われた後に Web サービスから受信した HTTP 応答メッセージを表します。 これには、状態コード、ヘッダー、本文など、応答に関する情報が含まれます。 HttpContent
クラスは、HTTP 本文とコンテンツ ヘッダー (Content-Type
や Content-Encoding
など) を表します。 コンテンツは、データの形式に応じて、ReadAsStringAsync
や ReadAsByteArrayAsync
などの ReadAs
メソッドのどれかを使用して読み取ることができます。
HttpClient
クラスの詳細については、HTTPClient オブジェクトの作成に関するページを参照してください。
HTTPWebRequest
HTTPWebRequest
による Web サービスの呼び出しには、次の処理が含まれます。
- 特定の URI の要求インスタンスを作成する。
- 要求インスタンスに対してさまざまな HTTP プロパティを設定する。
- 要求から
HttpWebResponse
を取得する。 - 応答からデータを読み取る。
たとえば、次のコードは、アメリカ国立医学図書館の Web サービスからデータを取得します。
var rxcui = "198440";
var request = HttpWebRequest.Create(string.Format(@"https://rxnav.nlm.nih.gov/REST/RxTerms/rxcui/{0}/allinfo", rxcui));
request.ContentType = "application/json";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if(string.IsNullOrWhiteSpace(content)) {
Console.Out.WriteLine("Response contained empty body...");
}
else {
Console.Out.WriteLine("Response Body: \r\n {0}", content);
}
Assert.NotNull(content);
}
}
上記の例では、JSON 形式のデータを返す HttpWebRequest
を作成しています。 データは HttpWebResponse
で返され、そこから StreamReader
を取得してデータを読み取ることができます。
RestSharp
REST サービスを使用するもう 1 つの方法は、RestSharp ライブラリを使用することです。 RestSharp は、未加工の文字列コンテンツまたは逆シリアル化された C# オブジェクトとして結果を取得するためのサポートを含む HTTP 要求をカプセル化します。 たとえば、次のコードは、アメリカ国立医学図書館の Web サービスに対して要求を行い、結果を JSON 形式の文字列として取得します。
var request = new RestRequest(string.Format("{0}/allinfo", rxcui));
request.RequestFormat = DataFormat.Json;
var response = Client.Execute(request);
if(string.IsNullOrWhiteSpace(response.Content) || response.StatusCode != System.Net.HttpStatusCode.OK) {
return null;
}
rxTerm = DeserializeRxTerm(response.Content);
DeserializeRxTerm
は、RestSharp.RestResponse.Content
プロパティから未加工の JSON 文字列を受け取り、それを C# オブジェクトに変換するメソッドです。 Web サービスから返されるデータの逆シリアル化については、この記事の後半で説明します。
NSUrlConnection
Mono 基本クラス ライブラリ (BCL) で使用できる HttpWebRequest
などのクラスや、RestSharp などのサードパーティの C# ライブラリに加えて、プラットフォーム固有のクラスも Web サービスを使用できます。 たとえば、iOS では NSUrlConnection
クラスと NSMutableUrlRequest
クラスを使用できます。
次のコード例は、iOS クラスを使用してアメリカ国立医学図書館の Web サービスを呼び出す方法を示しています。
var rxcui = "198440";
var request = new NSMutableUrlRequest(new NSUrl(string.Format("https://rxnav.nlm.nih.gov/REST/RxTerms/rxcui/{0}/allinfo", rxcui)),
NSUrlRequestCachePolicy.ReloadRevalidatingCacheData, 20);
request["Accept"] = "application/json";
var connectionDelegate = new RxTermNSURLConnectionDelegate();
var connection = new NSUrlConnection(request, connectionDelegate);
connection.Start();
public class RxTermNSURLConnectionDelegate : NSUrlConnectionDelegate
{
StringBuilder _ResponseBuilder;
public bool IsFinishedLoading { get; set; }
public string ResponseContent { get; set; }
public RxTermNSURLConnectionDelegate()
: base()
{
_ResponseBuilder = new StringBuilder();
}
public override void ReceivedData(NSUrlConnection connection, NSData data)
{
if(data != null) {
_ResponseBuilder.Append(data.ToString());
}
}
public override void FinishedLoading(NSUrlConnection connection)
{
IsFinishedLoading = true;
ResponseContent = _ResponseBuilder.ToString();
}
}
一般に、Web サービスを使用するためのプラットフォーム固有のクラスは、ネイティブ コードを C# に移植するシナリオに限定する必要があります。 可能であれば、Web サービス アクセス コードを移植可能にして、クロスプラットフォームで共有できるようにする必要があります。
ServiceStack
Web サービスを呼び出すためのもう 1 つのオプションは、Service Stack ライブラリです。 たとえば、次のコードは、Service Stack の IServiceClient.GetAsync
メソッドを使用してサービス要求を発行する方法を示しています。
client.GetAsync<CustomersResponse>("",
(response) => {
foreach(var c in response.Customers) {
Console.WriteLine(c.CompanyName);
}
},
(response, ex) => {
Console.WriteLine(ex.Message);
});
重要
ServiceStack や RestSharp などのツールを使用すると、REST サービスを簡単に呼び出して使用できますが、標準の DataContract シリアル化規則に準拠していない XML または JSON を使用することは簡単でない場合があります。 必要に応じて、要求を呼び出し、以下で説明する ServiceStack.Text ライブラリを使用して適切なシリアル化を明示的に処理します。
RESTful データの使用
RESTful Web サービスにより、通常は JSON メッセージを使用してクライアントにデータが返されます。 JSON は、コンパクトなペイロードを生成するテキスト ベースのデータ交換形式です。その結果、データを送信するときに帯域幅の要件が減ります。 このセクションでは、JSON と Plain-Old-XML (POX) で RESTful 応答を使用するメカニズムについて説明します。
System.JSON
Xamarin プラットフォームには、すぐに使える JSON のサポートが付属しています。 JsonObject
を使用することで、次のコード例に示すように結果を取得できます。
var obj = JsonObject.Parse(json);
var properties = obj["rxtermsProperties"];
term.BrandName = properties["brandName"];
term.DisplayName = properties["displayName"];
term.Synonym = properties["synonym"];
term.FullName = properties["fullName"];
term.FullGenericName = properties["fullGenericName"];
term.Strength = properties["strength"];
ただし、重要なこととして、System.Json
ツールによってデータ全体がメモリに読み込まれることに注意してください。
JSON.NET
NewtonSoft JSON.NET ライブラリは、JSON メッセージのシリアル化と逆シリアル化に広く使用されているライブラリです。 次のコード例は、JSON.NET を使用して JSON メッセージを C# オブジェクトに逆シリアル化する方法を示しています。
var term = new RxTerm();
var properties = JObject.Parse(json)["rxtermsProperties"];
term.BrandName = properties["brandName"].Value<string>();
term.DisplayName = properties["displayName"].Value<string>();
term.Synonym = properties["synonym"].Value<string>();;
term.FullName = properties["fullName"].Value<string>();;
term.FullGenericName = properties["fullGenericName"].Value<string>();;
term.Strength = properties["strength"].Value<string>();
term.RxCUI = properties["rxcui"].Value<string>();
ServiceStack.Text
ServiceStack.Text は、ServiceStack ライブラリを操作するように設計された JSON シリアル化ライブラリです。 次のコード例は、ServiceStack.Text.JsonObject
を使用して JSON を解析する方法を示しています。
var result = JsonObject.Parse(json).Object("rxtermsProperties")
.ConvertTo(x => new RxTerm {
BrandName = x.Get("brandName"),
DisplayName = x.Get("displayName"),
Synonym = x.Get("synonym"),
FullName = x.Get("fullName"),
FullGenericName = x.Get("fullGenericName"),
Strength = x.Get("strength"),
RxTermDoseForm = x.Get("rxtermsDoseForm"),
Route = x.Get("route"),
RxCUI = x.Get("rxcui"),
RxNormDoseForm = x.Get("rxnormDoseForm"),
});
System.Xml.Linq
XML ベースの REST Web サービスを使用する場合、次のコード例に示すように、LINQ to XML を使用して XML を解析し、C# オブジェクトをインラインで設定できます。
var doc = XDocument.Parse(xml);
var result = doc.Root.Descendants("rxtermsProperties")
.Select(x=> new RxTerm()
{
BrandName = x.Element("brandName").Value,
DisplayName = x.Element("displayName").Value,
Synonym = x.Element("synonym").Value,
FullName = x.Element("fullName").Value,
FullGenericName = x.Element("fullGenericName").Value,
//bind more here...
RxCUI = x.Element("rxcui").Value,
});
ASP.NET Web サービス (ASMX)
ASMX は、簡易オブジェクト アクセス プロトコル (SOAP) を使用してメッセージを送信する Web サービスを構築する機能を提供します。 SOAP は、Web サービスを構築およびアクセスするためのプラットフォームに依存せず、言語にも依存しないプロトコルです。 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 サービスを使用するには、"プロキシ" を生成する必要があります。これにより、アプリケーションはサービスに接続できます。 プロキシは、メソッドと関連するサービス構成を定義するサービス メタデータを使用して構築されます。 このメタデータは、Web サービスによって生成される Web サービス記述言語 (WSDL) ドキュメントとして公開されています。 プロキシは、Visual Studio for Mac または Visual Studio を使用して、プラットフォーム固有のプロジェクトに Web サービスに対する Web 参照を追加することによって構築されます。
Web サービスの URL には、file:///
パス プレフィックスを使用してアクセスできる、ホストされたリモート ソースまたはローカル ファイル システム リソースを指定できます。次に例を示します。
file:///Users/myUserName/projects/MyProjectName/service.wsdl
これにより、プロジェクトの Web またはサービス参照フォルダーにプロキシが生成されます。 プロキシは生成されたコードであるため、変更しないでください。
プロジェクトへの手動によるプロキシの追加
互換性のあるツールを使用して生成された既存のプロキシがある場合、この出力は、プロジェクトの一部として含まれている場合に使用できます。 Visual Studio for Mac で、[ファイルの追加...] メニュー オプションを使用してプロキシを追加します。 さらに、[参照の追加...] ダイアログを使用して、System.Web.Services.dll を明示的に参照する必要があります。
プロキシの使用
生成されたプロキシ クラスには、非同期プログラミング モデル (APM) 設計パターンを使う Web サービスを使用するためのメソッドが用意されています。 このパターンでは、非同期操作は、非同期操作を開始および終了する BeginOperationName と EndOperationName という 2 つのメソッドとして実装されます。
BeginOperationName メソッドは非同期操作を開始し、IAsyncResult
インターフェイスを実装するオブジェクトを返します。 BeginOperationName を呼び出した後、アプリケーションは呼び出し元のスレッドに対して命令の実行を継続できますが、非同期操作はスレッド プール スレッドに対して行われます。
BeginOperationName を呼び出すたびに、アプリケーションで EndOperationName も呼び出して操作の結果を取得する必要があります。 EndOperationName の戻り値は、同期 Web サービス メソッドから返されるものと同じ型です。 次のコード例は、この例を示しています。
public async Task<List<TodoItem>> RefreshDataAsync ()
{
...
var todoItems = await Task.Factory.FromAsync<ASMXService.TodoItem[]> (
todoService.BeginGetTodoItems,
todoService.EndGetTodoItems,
null,
TaskCreationOptions.None);
...
}
タスク並列ライブラリ (TPL) は、非同期操作を同じ Task
オブジェクトにカプセル化することで、APM の開始/終了メソッド ペアを使用するプロセスを簡略化できます。 このカプセル化は、Task.Factory.FromAsync
メソッドの複数のオーバーロードによって提供されます。 このメソッドは、TodoService.BeginGetTodoItems
メソッドが完了すると TodoService.EndGetTodoItems
メソッドを実行する、BeginGetTodoItems
デリゲートにデータが渡されないことを示す null
パラメーターが指定された Task
を作成します。 最後に、TaskCreationOptions
列挙型の値は、タスクの作成と実行に既定の動作を使用する必要があることを指定します。
APM の詳細については、MSDN の「非同期プログラミング モデル」と「TPL と従来の .NET Framework 非同期プログラミング」を参照してください。
ASMX サービスの使用について詳しくは、「ASP.NET Web サービス (ASMX) を使用する」を参照してください。
Windows Communication Foundation (WCF)
WCF は、サービス指向アプリケーションを構築する Microsoft の統合フレームワークです。 これにより、開発者は、セキュリティで保護され、信頼性が高く、トランザクションが行われ、相互運用可能な分散アプリケーションを構築できます。
WCF は、次のようなさまざまなコントラクトを含むサービスについて説明しています。
- データ コントラクト - メッセージ内のコンテンツの基礎を形成するデータ構造を定義します。
- メッセージ コントラクト - 既存のデータ コントラクトからメッセージを作成します。
- エラー コントラクト - カスタム SOAP エラーを指定できます。
- サービス コントラクト - サービスがサポートする操作と、各操作との対話に必要なメッセージを指定します。 また、各サービスの操作に関連付けることができるカスタムのエラー動作も指定します。
ASP.NET Web サービス (ASMX) と WCF には違いがありますが、WCF では ASMX が提供するのと同じ機能 (HTTP 経由の SOAP メッセージ) がサポートされていることを理解することが重要です。
重要
WCF に対する Xamarin プラットフォームのサポートは、BasicHttpBinding
クラスを使った HTTP/HTTPS 経由のテキストエンコード SOAP メッセージに限定されています。 さらに、WCF のサポートでは、プロキシを生成するために Windows 環境でのみ使用できるツールの使用が求められます。
プロキシの生成
WCF サービスを使用するには、"プロキシ" を生成する必要があります。これでアプリケーションはサービスに接続できるようになります。 プロキシを構築するには、メソッドと、関連付けられたサービス構成を定義するサービス メタデータを使います。 このメタデータは、Web サービスによって生成される Web サービス記述言語 (WSDL) ドキュメントの形式で公開されています。 プロキシを構築するには、Visual Studio 2017 の Microsoft WCF Web Service Reference プロバイダーを使い、Web サービスのサービス参照を .NET Standard ライブラリに追加します。
Visual Studio 2017 で Microsoft WCF Web Service Reference プロバイダーを使ってプロキシを作成する代わりに、ServiceModel メタデータ ユーティリティ ツール (svcutil.exe) を使うこともできます。 詳細については、「ServiceModel メタデータ ユーティリティ ツール (Svcutil.exe)」を参照してください。
プロキシの構成
生成されたプロキシを構成するには、一般に、初期化時に 2 つの構成引数 (SOAP 1.1/ASMX か WCF かに応じて) が取られます。次の例に示すように、EndpointAddress
と関連付けられたバインディング情報のどちらか一方または両方です。
var binding = new BasicHttpBinding () {
Name= "basicHttpBinding",
MaxReceivedMessageSize = 67108864,
};
binding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas() {
MaxArrayLength = 2147483646,
MaxStringContentLength = 5242880,
};
var timeout = new TimeSpan(0,1,0);
binding.SendTimeout= timeout;
binding.OpenTimeout = timeout;
binding.ReceiveTimeout = timeout;
client = new Service1Client (binding, new EndpointAddress ("http://192.168.1.100/Service1.svc"));
バインディングを使用して、アプリケーションとサービスが相互に通信するために必要なトランスポート、エンコード、プロトコルの詳細を指定します。 BasicHttpBinding
には、テキストエンコードされた SOAP メッセージが HTTP トランスポート プロトコル経由で送信されることを指定します。 エンドポイント アドレスを指定すると、複数の発行済みインスタンスがある場合に、アプリケーションは WCF サービスのさまざまなインスタンスに接続できます。
プロキシの使用
生成されたプロキシ クラスには、非同期プログラミング モデル (APM) 設計パターンを使う Web サービスを使用するためのメソッドが提供されています。 このパターンでは、非同期操作は、非同期操作を開始および終了する BeginOperationName と EndOperationName という 2 つのメソッドとして実装されます。
BeginOperationName メソッドは非同期操作を開始し、IAsyncResult
インターフェイスを実装するオブジェクトを返します。 BeginOperationName を呼び出した後、アプリケーションは呼び出し元のスレッドに対して命令の実行を継続できますが、非同期操作はスレッド プール スレッドに対して行われます。
BeginOperationName を呼び出すたびに、アプリケーションで EndOperationName も呼び出して操作の結果を取得する必要があります。 EndOperationName の戻り値は、同期 Web サービス メソッドから返されるものと同じ型です。 次のコード例は、この例を示しています。
public async Task<List<TodoItem>> RefreshDataAsync ()
{
...
var todoItems = await Task.Factory.FromAsync <ObservableCollection<TodoWCFService.TodoItem>> (
todoService.BeginGetTodoItems,
todoService.EndGetTodoItems,
null,
TaskCreationOptions.None);
...
}
タスク並列ライブラリ (TPL) は、非同期操作を同じ Task
オブジェクトにカプセル化することで、APM の開始/終了メソッド ペアを使用するプロセスを簡略化できます。 このカプセル化は、Task.Factory.FromAsync
メソッドの複数のオーバーロードによって提供されます。 このメソッドは、TodoServiceClient.BeginGetTodoItems
メソッドが完了すると TodoServiceClient.EndGetTodoItems
メソッドを実行する、BeginGetTodoItems
デリゲートにデータが渡されないことを示す null
パラメーターが指定された Task
を作成します。 最後に、TaskCreationOptions
列挙型の値は、タスクの作成と実行に既定の動作を使用する必要があることを指定します。
APM の詳細については、MSDN の「非同期プログラミング モデル」と「TPL と従来の .NET Framework 非同期プログラミング」を参照してください。
WCF サービスの使用について詳しくは、「Windows Communication Foundation (WCF) Web サービスを使用する」を参照してください。
トランスポート セキュリティの使用
WCF サービスでは、メッセージのインターセプトから保護するためにトランスポート レベルのセキュリティを採用する場合があります。 Xamarin プラットフォームでは、SSL を使用したトランスポート レベルのセキュリティを採用したバインディングがサポートされています。 ただし、スタックで証明書の検証が必要になる場合があり、予期しない動作が発生する可能性があります。 次のコード例に示すように、サービスを呼び出す前に ServerCertificateValidationCallback
デリゲートを登録することで、検証をオーバーライドできます。
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) => { return true; };
これにより、サーバー側の証明書の検証を無視しながら、トランスポートの暗号化が維持されます。 ただし、この方法は証明書に関連した信頼の問題を実質的に無視するため、適切でない場合があります。 詳細については、mono-project.com にある信頼されたルートを慎重に使用することに関するページを参照してください。
クライアント資格情報セキュリティの使用
WCF サービスでは、サービス クライアントが資格情報を使用して認証を行うことが必な場合もあります。 Xamarin プラットフォームでは、クライアントが SOAP メッセージ エンベロープ内で資格情報を送信できる WS-Security プロトコルはサポートされていません。 ただし、Xamarin プラットフォームでは、適切な ClientCredentialType
を指定して HTTP 基本認証の資格情報をサーバーに送信する機能がサポートされています。
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
そのため、基本認証の資格情報を指定できます。
client.ClientCredentials.UserName.UserName = @"foo";
client.ClientCredentials.UserName.Password = @"mrsnuggles";
REST Web サービスのコンテキストでの HTTP 基本認証の詳細については、「RESTful Web サービスを認証する」を参照してください。