Web 服務簡介
本指南示範如何使用不同的 Web 服務技術。 涵蓋的主題包括與 REST 服務、SOAP 服務和 Windows Communication Foundation 服務通訊。
為了正確運作,許多行動應用程式都相依於雲端,因此將Web服務整合到行動應用程式是常見的案例。 Xamarin 平台支援使用不同的 Web 服務技術,並包含內建和第三方支援來取用 RESTful、ASMX 和 Windows Communication Foundation (WCF) 服務。
對於使用 Xamarin.Forms 的客戶,在 Xamarin.Forms Web 服務檔中,有使用這些技術的完整範例。
重要
在 iOS 9 中,應用程式傳輸安全性 (ATS) 會強制執行因特網資源(例如應用程式後端伺服器)與應用程式之間的安全連線,從而防止意外洩漏敏感性資訊。 由於 ATS 預設會在針對 iOS 9 建置的應用程式中啟用,因此所有連線都會受限於 ATS 安全性需求。 如果連線不符合這些需求,它們將會失敗併發生例外狀況。
如果無法使用 HTTPS
通訊協議和保護因特網資源的通訊,則可以退出宣告 ATS。 您可以藉由更新應用程式的 Info.plist 檔案來達成此目的。 如需詳細資訊,請參閱 應用程式傳輸安全性。
REST
具象狀態傳輸 (REST) 是建立 Web 服務的架構樣式。 REST 要求是使用網頁瀏覽器用來取出網頁以及將資料傳送至伺服器的相同 HTTP 指令動詞,透過 HTTP 進行。 指令動詞如下:
- GET - 這項作業用來從 Web 服務取出資料。
- POST - 這項作業用來在 Web 服務上建立新的資料項目。
- PUT - 這項作業用來在 Web 服務上更新資料項目。
- PATCH - 這項作業用來透過描述有關應該如何修改項目的一組指示,更新 Web 服務上的資料項目。 範例應用程式中未使用此指令動詞。
- DELETE - 這項作業用來在 Web 服務上刪除資料項目。
遵守 REST 的 Web 服務 API 稱為 RESTful API,並使用下列項目定義:
- 基底 URI。
- HTTP 方法,例如 GET、POST、PUT、PATCH 或 DELETE。
- 數據的媒體類型,例如 JavaScript 物件表示法(JSON)。
REST 的簡單性有助於它成為在行動應用程式中存取 Web 服務的主要方法。
取用 REST 服務
有許多連結庫和類別可用來取用 REST 服務,下列小節會討論它們。 如需取用 REST 服務的詳細資訊,請參閱 取用 RESTful Web 服務。
HttpClient
Microsoft HTTP 用戶端連結庫提供 類別HttpClient
,用來透過 HTTP 傳送和接收要求。 它提供從 URI 識別資源傳送 HTTP 要求和接收 HTTP 回應的功能。 每個要求都會以異步操作的形式傳送。 如需異步操作的詳細資訊,請參閱 異步支援概觀。
類別 HttpResponseMessage
代表在提出 HTTP 要求之後,從 Web 服務接收的 HTTP 回應消息。 其中包含回應的相關信息,包括狀態代碼、標頭和本文。 類別 HttpContent
代表 HTTP 主體和內容標頭,例如 Content-Type
和 Content-Encoding
。 視數據的格式而定,可以使用 和 ReadAsByteArrayAsync
等ReadAsStringAsync
任何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);
}
}
上述範例會建立 , HttpWebRequest
以傳回格式化為 JSON 的數據。 數據會在 中 HttpWebResponse
傳回, StreamReader
您可以從中取得 來讀取數據。
RestSharp
取用 REST 服務的另一種方法是使用 RestSharp 連結庫。 RestSharp 會封裝 HTTP 要求,包括支援以原始字串內容或還原串行化 C# 物件的形式擷取結果。 例如,下列程式代碼會向美國國家醫學連結庫 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 服務傳回的數據還原串行化。
NSUrl 連線 ion
除了 Mono 基類庫 (BCL) 中可用的類別,例如 HttpWebRequest
、 和第三方 C# 連結庫,例如 RestSharp、平臺特定類別也可用於取用 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 服務的另一個選項是 服務堆疊 連結庫。 例如,下列程式代碼示範如何使用 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 是 JSON 串行化連結庫,其設計目的是要與 ServiceStack 連結庫搭配使用。 下列程式代碼範例示範如何使用 剖析 JSON ServiceStack.Text.JsonObject
:
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 可讓您建置 Web 服務,以使用簡單物件存取通訊協定 (SOAP) 傳送訊息。 SOAP 是平台獨立且與語言無關的通訊協定,可用於建置和存取 Web 服務。 ASMX 服務的取用者不需要知道用來實作服務的平臺、物件模型或程式設計語言。 他們只需要瞭解如何傳送和接收SOAP訊息。
SOAP 訊息是包含下列元素的 XML 檔:
- 名為 Envelope 的根元素,會將 XML 檔識別為 SOAP 訊息。
- 選擇性 的 Header 元素,其中包含應用程式特定資訊,例如驗證數據。 如果 Header 元素存在,它必須是 Envelope 元素的第一個子元素。
- 必要的 Body 元素,其中包含要供收件者使用的 SOAP 訊息。
- 選擇性 的Fault 元素,用來指出錯誤訊息。 如果 Fault 元素存在,它必須是 Body 元素的子元素。
SOAP 可以透過許多傳輸通訊協議運作,包括 HTTP、SMTP、TCP 和 UDP。 不過,ASMX 服務只能透過 HTTP 運作。 Xamarin 平臺透過 HTTP 支援標準 SOAP 1.1 實作,這包括許多標準 ASMX 服務組態的支援。
產生 Proxy
必須產生 Proxy 才能取用 ASMX 服務,讓應用程式能夠連線到服務。 Proxy 是藉由取用定義方法和相關聯服務組態的服務元數據來建構。 此元數據會公開為 Web 服務描述語言 (WSDL) 檔,由 Web 服務產生。 Proxy 是使用 Visual Studio for Mac 或 Visual Studio 建置,將 Web 服務的 Web 參考新增至平臺特定專案。
Web 服務 URL 可以是可透過 file:///
路徑前置詞存取的託管遠端來源或本機檔系統資源,例如:
file:///Users/myUserName/projects/MyProjectName/service.wsdl
這會在專案的 Web 或服務參考資料夾中產生 Proxy。 由於 Proxy 是產生的程式代碼,所以不應該修改它。
手動將 Proxy 新增至專案
如果您有使用相容工具產生的現有 Proxy,則可以在專案隨附時取用此輸出。 在 Visual Studio for Mac 中,使用 [ 新增檔案... ] 功能表選項來新增 Proxy。 此外,這需要使用 [新增參考...] 對話框明確參考System.Web.Services.dll。
使用 Proxy
產生的 Proxy 類別提供使用異步程式設計模型 (APM) 設計模式之 Web 服務的方法。 在此模式中,異步操作會實作為名為 BeginOperationName 和 EndOperationName 的兩種方法,以開始和結束異步操作。
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
多個多載所提供。 這個方法會Task
建立 ,一旦TodoService.BeginGetTodoItems
方法完成,就會執行 TodoService.EndGetTodoItems
方法,並使用 null
參數指出不會將任何數據傳遞至BeginGetTodoItems
委派。 最後,列舉的值 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 環境中使用工具,才能產生 Proxy。
產生 Proxy
必須產生 Proxy 才能取用 WCF 服務,讓應用程式能夠連線到服務。 Proxy 是藉由取用定義方法和相關聯服務組態的服務元數據來建構。 此元數據會以 Web 服務描述語言 (WSDL) 檔的形式公開,由 Web 服務產生。 您可以使用 Visual Studio 2017 中的 Microsoft WCF Web 服務參考提供者來建置 Proxy,將 Web 服務的服務參考新增至 .NET Standard 連結庫。
在 Visual Studio 2017 中使用 Microsoft WCF Web 服務參考提供者建立 Proxy 的替代方法是使用 ServiceModel 元數據公用程式工具(svcutil.exe)。 如需詳細資訊,請參閱 ServiceModel 元數據公用程式工具(Svcutil.exe)。
設定 Proxy
設定產生的 Proxy 通常會在初始化期間接受兩個組態自變數(視 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
指定會透過 HTTP 傳輸通訊協定傳送文字編碼的 SOAP 訊息。 指定端點位址可讓應用程式連線到 WCF 服務的不同實例,前提是有多個已發佈的實例。
使用 Proxy
產生的 Proxy 類別提供使用異步程式設計模型 (APM) 設計模式之 Web 服務的方法。 在此模式中,異步操作會實作為名為 BeginOperationName 和 EndOperationName 的兩種方法,以開始和結束異步操作。
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
多個多載所提供。 這個方法會Task
建立 ,一旦TodoServiceClient.BeginGetTodoItems
方法完成,就會執行 TodoServiceClient.EndGetTodoItems
方法,並使用 null
參數指出不會將任何數據傳遞至BeginGetTodoItems
委派。 最後,列舉的值 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 平臺不支援 WS-Security 通訊協定,可讓用戶端在 SOAP 訊息信封內傳送認證。 不過,Xamarin 平台藉由指定適當的 ClientCredentialType
來支援將 HTTP 基本身份驗證認證傳送至伺服器的能力:
basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
然後,可以指定基本身份驗證認證:
client.ClientCredentials.UserName.UserName = @"foo";
client.ClientCredentials.UserName.Password = @"mrsnuggles";
如需 HTTP 基本身份驗證的詳細資訊,雖然在 REST Web 服務的內容中,請參閱 驗證 RESTful Web 服務。