【ASP.NET Web API教程】3.2 通过.NET客户端调用Web API(C#)

注:本文是【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)。

WebAPI3-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包管理器,按如下步骤安装。

  1. Start Visual Studio.
    启动Visual Studio.
  2. From the Tools menu, select Extensions and Updates.
    从“工具”菜单选择“扩展与更新”
  3. In the Extensions and Updates dialog, select Online.
    在“扩展与更新”对话框中,选择“在线”
  4. If you don't see "NuGet Package Manager", type "nuget package manager" in the search box.
    如果未看到“NuGet包管理器”,在搜索框中输入“nuget package manager”。
  5. Select the NuGet Package Manager and click Download.
    选择“NuGet包管理器”,并点击“下载”。
  6. After the download completes, you will be prompted to install.
    下载完成后,会提示你安装。
  7. After the installation completes, you might be prompted to restart Visual Studio.
    安装完成后,可能会提示重启Visual Studio。

上述安装过程如图3-2所示。

WebAPI3-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客户端库包添加到你的项目。步骤如下:

  1. 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包管理器。
  2. Select Manage NuGet Packages for Solution...
    选择“管理解决方案的NuGet包…”
  3. In the Manage NugGet Packages dialog, select Online.
    在“管理NuGet包”对话框中,选择“在线”。
  4. In the search box, type "Microsoft.AspNet.WebApi.Client".
    在搜索框中输入“Microsoft.AspNet.WebApi.Client”。
  5. Select the ASP.NET Web API Self Host package and click Install.
    选择“ASP.NET Web API自托管包”,并点击“安装”。
  6. After the package installs, click Close to close the dialog.
    这个包安装后,点击“关闭”,关闭此对话框。

上述安装步骤如图3-3所示。

WebAPI3-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小节 — 译者注)。

看完此文如果觉得有所收获,恳请给个推荐

posted @ 2012-11-20 14:39  r01cn  阅读(27844)  评论(7编辑  收藏  举报