注:本文是【ASP.NET Web API系列教程】的一部分,如果您是第一次看本博客文章,請先看前面的內容。
3.2 Calling a Web API From a .NET Client (C#)
3.2 通過.NET客戶端調用Web API(C#)
本文引自:http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client
By Mike Wasson | July 18, 2012
作者:Mike Wasson | 日期:2012-7-18
This tutorial shows how to call a web API from a console application, using HttpClient.
本教程展示如何在一個控制台應用程序中使用HttpClient調用Web API。
In this tutorial, we will consume the "ProductStore" API, described in Creating a Web API that Supports CRUD Operations.
在本教程中,我們將使用在“創建支持CRUD操作的Web API(本系列教程的第2.1小節 — 譯者注)”小節中描述的“ProductStore”API。
Create the Console Application
創建控制台應用程序
Start Visual Studio and select New Project from the Start page. Or, from the File menu, select New and then Project.
啟動Visual studio,並從“開始”頁面選擇“新項目”。或者從“文件”菜單選擇“新建”,然后選擇“項目”。
In the Templates pane, select Installed Templates and expand the Visual C# node. Under Visual C#, select Windows. In the list of project templates, select Console Application. Name the project and click OK.
在“模板”面板中,選擇“已安裝模板”,並展開“Visual C#”節點。在“Visual C#”下選擇“Windows”。在項目模板列表中選擇“控制台應用程序”。命名此項目並點擊“OK”(見圖3-1)。

圖3-1. 創建控制台項目
Install NuGet Package Manager
安裝NuGet包管理器
NuGet Package Manager is the easiest way to add the Web API Client library to a project. If you do not have NuGet Package Manager already installed, install it as follows.
“NuGet包管理器(NuGet Package Manager)”是把Web API客戶端庫添加到項目的一種最容易的方法。如果尚未安裝NuGet包管理器,按如下步驟安裝。
- Start Visual Studio.
啟動Visual Studio. - From the Tools menu, select Extensions and Updates.
從“工具”菜單選擇“擴展與更新” - In the Extensions and Updates dialog, select Online.
在“擴展與更新”對話框中,選擇“在線” - If you don't see "NuGet Package Manager", type "nuget package manager" in the search box.
如果未看到“NuGet包管理器”,在搜索框中輸入“nuget package manager”。 - Select the NuGet Package Manager and click Download.
選擇“NuGet包管理器”,並點擊“下載”。 - After the download completes, you will be prompted to install.
下載完成后,會提示你安裝。 - After the installation completes, you might be prompted to restart Visual Studio.
安裝完成后,可能會提示重啟Visual Studio。
上述安裝過程如圖3-2所示。

圖3-2. 安裝NuGet包管理器
Install the Web API Client Libraries
安裝Web API客戶端庫
After NuGet Package Manager is installed, add the Web API Client Libraries package to your project.
安裝NuGet包管理器后,把Web API客戶端庫包添加到你的項目。步驟如下:
- From the Tools menu, select Library Package Manager. Note: If do you not see this menu item, make sure that NuGet Package Manager installed correctly.
從“工具”菜單選擇“庫包管理器”。注:如果看不到這個菜單項,請確保已正確安裝了NuGet包管理器。 - Select Manage NuGet Packages for Solution...
選擇“管理解決方案的NuGet包…” - In the Manage Nu
gGet Packages dialog, select Online.
在“管理NuGet包”對話框中,選擇“在線”。 - In the search box, type "Microsoft.AspNet.WebApi.Client".
在搜索框中輸入“Microsoft.AspNet.WebApi.Client”。 - Select the ASP.NET Web API Self Host package and click Install.
選擇“ASP.NET Web API自托管包”,並點擊“安裝”。 - After the package installs, click Close to close the dialog.
這個包安裝后,點擊“關閉”,關閉此對話框。
上述安裝步驟如圖3-3所示。

圖3-3. 安裝Web API客戶端庫
Add the Model Class
添加模型類
Add the following class to the application:
將以下類添加到應用程序:
class Product { public string Name { get; set; } public double Price { get; set; } public string Category { get; set; } }
This class creates a data object that HttpClient will write into the HTTP request body and read from the HTTP response body.
這個類創建一個數據對象,HttpClient將把它寫入HTTP請求體中,也從HTTP響應體中讀取它。
Initialize HttpClient
初始化HttpClient
Create a new instance of HttpClient and initialize it as follows:
創建一個新的HttpClient實例,並像下面這樣初始化它:
namespace ProductStoreClient { using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers;
class Program { static void Main(string[] args) { HttpClient client = new HttpClient(); client.BaseAddress = new Uri("http://localhost:9000/"); // Add an Accept header for JSON format. // 為JSON格式添加一個Accept報頭 client.DefaultRequestHeaders.Accept.Add( new MediaTypeWithQualityHeaderValue("application/json")); } } }
This code sets the base URI to "http://localhost:9000/", and sets the Accept header to "application/json", which tells the server to send data in JSON format.
這段代碼把基URI設置為“http://localhost:9000/”,並將Accept報頭設置為“application/json”,這是告訴服務器,以JSON格式發送數據。
Getting a Resource (HTTP GET)
獲取資源(HTTP GET)
The following code shows how to query the API for a list of products:
以下代碼展示如何對產品列表查詢API:
// List all products. // 列出所有產品 HttpResponseMessage response = client.GetAsync("api/products").Result; // Blocking call(阻塞調用)! if (response.IsSuccessStatusCode) { // Parse the response body. Blocking! // 解析響應體。阻塞! var products = response.Content.ReadAsAsync<IEnumerable<Product>>().Result; foreach (var p in products) { Console.WriteLine("{0}\t{1};\t{2}", p.Name, p.Price, p.Category); } } else { Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase); }
The GetAsync method sends an HTTP GET request. As the name implies, GetAsyc is asynchronous. It returns immediately, without waiting for a response from the server. The return value is a Task object that represents the asynchronous operation. When the operation completes, the Task.Result property contains the HTTP response.
GetAsync方法發送HTTP GET請求。正如其名稱所暗示的,GetAsync是異步的。它立即返回,不會等待服務器的響應。返回值是一個表示異步操作的Task對象。當該操作完成時,Task.Result屬性包含HTTP響應。
It is important to understand that taking the Result property blocks your application thread until the request completes (or times out). Blocking in a console application is OK, but you should never do this on the UI thread of a Windows application, because it blocks the UI from responding to user input. In the next part of this tutorial, we'll see how to write non-blocking calls.
重要的是理解,直到請求完成(或超時),采用Result屬性的過程是應用程序線程阻塞的。控制台應用程序的阻塞沒問題,但是,你決不應該在一個Windows應用程序的UI上做這種事,因為這會阻塞UI去響應用戶的輸入。在本教程的下一部分中,我們將看到如何編寫非阻塞調用。
If the HTTP response indicates success, the response body contains a list of products in JSON format. To parse the list, call ReadAsAsync. This method reads the response body and tries to deserialize it to a specified CLR type. This method is also asynchronous, because the body can be arbitrarily large. Again, taking the Result property blocks the thread.
如果HTTP響應指示成功,響應體便含有一個JSON格式的產品列表。要解析這個列表,需調用ReadAsAsync。該方法讀取響應體,並試圖把它解序列化成一個具體的CLR(公共語言運行時)類型。這個方法也是異步的,因為體可能有任意大小。再次強調,采用Result屬性的過程是線程阻塞的。
Example HTTP session:
HTTP會話示例:
GET http://localhost:9000/api/products HTTP/1.1 Accept: application/json Host: localhost:9000 Connection: Keep-Alive
HTTP/1.1 200 OK Server: ASP.NET Development Server/11.0.0.0 Date: Mon, 20 Aug 2012 22:14:59 GMT X-AspNet-Version: 4.0.30319 Cache-Control: no-cache Pragma: no-cache Expires: -1 Content-Type: application/json; charset=utf-8 Content-Length: 183 Connection: Close
[{"Id":1,"Name":"Tomato soup","Category":"Groceries","Price":1.39},{"Id":2,"Name":"Yo-yo", "Category":"Toys","Price":3.75},{"Id":3,"Name":"Hammer","Category":"Hardware","Price":16.99}]
Getting a product by ID is similar:
通過ID獲取產品是類似的:
// Get a product by ID // 通過ID獲取產品 response = client.GetAsync("api/products/1").Result; if (response.IsSuccessStatusCode) { // Parse the response body. Blocking! // 解析響應休。阻塞! var product = response.Content.ReadAsAsync<Product>().Result; Console.WriteLine("{0}\t{1};\t{2}", product.Name, product.Price, product.Category); } else { Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase); }
Media-Type Formatters
媒體類型格式化器
ReadAsAsync is an extension method defined in the System.Net.Http.HttpContentExtensions class. With no parameters, it uses the default set of media-type formatters to try to parse the response body. The default formatters support JSON, XML, and Form-url-encoded data. (For more information about media-type formatters, see Formats and Model Binding.)
ReadAsAsync是在System.Net.Http.HttpContentExtensions類中定義的一個擴展方法。不帶參數,它會使用媒體類型格式化器的默認設置,以試圖解析響應體。默認格式化器支持JSON、XML和經過url編碼的表單數據(Form-url-encoded data)。(關於媒體類型格式化器的更多信息,參閱“格式化與模型綁定(本教程系列的第6章 — 譯者注)”)
You can also explicitly specify the media-types formatters to use. This is useful if you have a custom media-type formatter.
也可以明確指定所使用的媒體類型格式化器。如果你有一個自定義媒體類型格式化器,這是有用的。
var formatters = new List<MediaTypeFormatter>() { new MyCustomFormatter(), new JsonMediaTypeFormatter(), new XmlMediaTypeFormatter() };
resp.Content.ReadAsAsync<IEnumerable<Product>>(formatters);
Creating a Resource (HTTP POST)
創建一個資源(HTTP POST)
The following code sends a POST request that contains a Product instance in JSON format:
以下代碼發送一個POST請求,它含有一個JSON格式的Product實例:
// Create a new product // 創建一個新產品 var gizmo = new Product() { Name = "Gizmo", Price = 100, Category = "Widget" }; Uri gizmoUri = null;
response = client.PostAsJsonAsync("api/products", gizmo).Result; if (response.IsSuccessStatusCode) { gizmoUri = response.Headers.Location; } else { Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase); }
PostAsJsonAsync is an extension method defined in System.Net.Http.HttpClientExtensions. It is equivalent to the following:
PostAsJsonAsync是在System.Net.Http.HttpClientExtensions中定義的一個擴展方法。上述代碼與以下代碼等效:
var product = new Product() { Name = "Gizmo", Price = 100, Category = "Widget" };
// Create the JSON formatter. // 創建JSON格式化器。 MediaTypeFormatter jsonFormatter = new JsonMediaTypeFormatter();
// Use the JSON formatter to create the content of the request body. // 使用JSON格式化器創建請求體內容。 HttpContent content = new ObjectContent<Product>(product, jsonFormatter);
// Send the request. // 發送請求。 var resp = client.PostAsync("api/products", content).Result;
For XML format, use the PostAsXmlAsync method.
對於XML格式,使用PostAsXmlAsync方法。
Example HTTP session:
HTTP會話示例:
POST http://localhost:9000/api/products HTTP/1.1 Accept: application/json Content-Type: application/json; charset=utf-8 Host: localhost:9000 Content-Length: 50 Expect: 100-continue
{"Name":"Gizmo","Price":100.0,"Category":"Widget"}
HTTP/1.1 201 Created Server: ASP.NET Development Server/11.0.0.0 Date: Mon, 20 Aug 2012 22:15:00 GMT X-AspNet-Version: 4.0.30319 Location: http://localhost:9000/api/products/7 Cache-Control: no-cache Pragma: no-cache Expires: -1 Content-Type: application/json; charset=utf-8 Content-Length: 57 Connection: Close
{"Id":7,"Name":"Gizmo","Category":"Widget","Price":100.0}
By default, the JSON formatter sets the content-type to "application/json". You can also specify the media type explicitly. For example, suppose that "application/vnd.example.product" is your media type for Product instances. You could set this media type as follows:
默認地,JSON格式化器將content-type(內容類型)設置為“application/json”。你也可以明確地指定媒體類型。例如,假設“application/vnd.example.product”是你用於Product實例的媒體類型。可以像下面這樣來設置這種媒體:
HttpContent content = new ObjectContent<Product>(product, jsonFormatter, "application/vnd.example.product+json");
Updating a Resource (HTTP PUT)
更新一個資源(HTTP PUT)
The following code sends a PUT request.
以下代碼發送一個PUT請求:
// Update a product // 更新一個產品 gizmo.Price = 99.9; response = client.PutAsJsonAsync(gizmoUri.PathAndQuery, gizmo).Result; Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
The PutAsJsonAsync method works like PostAsJsonAsync, except it sends a PUT request instead of POST.
PutAsJsonAsync方法與PostAsJsonAsync類似,只是它發送的是一個PUT請求,而不是POST。
Deleting a Resource (HTTP DELETE)
刪除一個資源(HTTP DELETE)
By now, you can probably predict how to send a DELETE request:
現在,你可能已經能預測到如何發送一個DELETE請求了:
// Delete a product // 刪除一個產品 response = client.DeleteAsync(gizmoUri).Result; Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
Like GET, a DELETE request does not have a request body, so you don't need to specify JSON or XML format.
與GET一樣,DELETE請求沒有請求體,因此你不必指定JSON或XML格式。
Error Handling
錯誤處理
HttpClient does not thrown an exception when it receives an HTTP response with an error code. Instead, the StatusCode property on the response contains the status code. Also, the IsSuccessStatusCode property is true if the status is a success code (status codes in the range 200-299).
HttpClient在它接收到一個帶有錯誤代碼的HTTP響應時,不會拋出異常。但是,該響應的StatusCode屬性含有狀態碼。而且,如果其狀態是一個成功的狀態碼(狀態碼在200-299之間)時,IsSuccessStatusCode屬性為true。
The previous examples used this pattern:
使用這種模式的前面這個例子為:
HttpResponseMessage response = client.GetAsync("api/products").Result; if (response.IsSuccessStatusCode) { // .... }
If you prefer to treat error codes as exceptions, call the EnsureSuccessStatusCode method. This method throws an exception if the response status is not a success code.
如果你喜歡把錯誤代碼處理成異常,可以調用EnsureSuccessStatusCode方法。這個方法在響應狀態不是一個成功的代碼時,會拋出一個異常。
try { var resp = client.GetAsync("api/products").Result; resp.EnsureSuccessStatusCode(); // Throw if not a success code.
// ... } catch (HttpRequestException e) { Console.WriteLine(e.Message); }
HttpClient can throw exceptions for other reasons, of course — for example, if the request times out.
當然,由於其它原因,HttpClient也可能拋出異常 — 例如,請求超時的時候。
Configuring HttpClient
配置HttpClient
To configure HttpClient, create a WebRequestHandler instance, set properties on it, and pass it to the HttpClient constructor:
為了配置HttpClient,可以創建一個WebRequestHandler實例,設置其屬性,並把它傳遞給HttpClient構造器:
WebRequestHandler handler = new WebRequestHandler() { AllowAutoRedirect = false, UseProxy = false }; HttpClient client = new HttpClient(handler);
WebRequestHandler derives from HttpMessageHandler. You can also plug in custom message handlers by deriving from HttpMessageHandler. For more information, see HTTP Message Handlers.
WebRequestHandler派生於HttpMessageHandler。通過從HttpMessageHandler派生,你也可以插入自定義消息處理程序。更多信息參閱“HTTP消息處理器(本系列教程的第5.1小節 — 譯者注)”。
Additional Resources
其它資源
A console application makes it easy to see the code flow. However, the blocking calls are not good practice for applications with a graphical UI. To learn how to handle asynchronous operations in HttpClient without blocking, see Calling a Web API From a WPF Application
控制台應用程序使我們很容易看到代碼流程。然而,阻塞調用對於圖形UI的應用程序並不是一種好的做法。要了解如何處理非阻塞HttpClient中的異步操作,參閱“通過WPF應用程序調用Web API”(本系列教程的第3.3小節 — 譯者注)。
看完此文如果覺得有所收獲,懇請給個推薦