HttpClient in .net Framework 4.5

来源:互联网 发布:二值化算法c语言 编辑:程序博客网 时间:2024/04/29 03:32

转自书:C# 5.0 IN A NUTSHELL (待翻译)

HttpClient


HttpClient is new to Framework 4.5 and provides another layer on top of
HttpWebRequest and HttpWebResponse. It was written in response to the growth of
HTTP-based web APIs and REST services, to provide a better experience than Web
Client when dealing with protocols more elaborate than simply fetching a web page.
Specifically:
• A single HttpClient instance supports concurrent requests. To get concurrency
with  WebClient, you need to create a fresh instance per concurrent request,
which can get awkward when you introduce custom headers, cookies, and au-
thentication schemes.
• HttpClient lets you write and plug in custom message handlers. This enables
mocking in unit tests, and the creation of custom pipelines (for logging, com-
pression, encryption, and so on). Unit-testing code that calls WebClient is a pain.

• HttpClient has a rich and extensible type system for headers and content.
HttpClient is not a complete replacement for  WebClient, be-
cause it doesn’t support progress reporting. WebClient also has
the advantage of supporting FTP,  file:// and custom URI
schemes. It’s also available in all Framework versions.
The simplest way to use HttpClient is to instantiate it and then call one its Get*
methods, passing in a URI:
string html = await new HttpClient().GetStringAsync ("http://linqpad.net");
(There’s also GetByteArrayAsync and GetStreamAsync.) All I/O-bound methods in
HttpClient are asynchronous (there are no synchronous equivalents).
Unlike with WebClient, to get the best performance with HttpClient, you must re-
use same instance (otherwise things such as DNS resolution may be unnecessarily
repeated.) HttpClient permits concurrent operations, so the following is legal and
downloads two web pages at once:


    var client = new HttpClient();
     var task1 = client.GetStringAsync ("http://www.linqpad.net");
     var task2 = client.GetStringAsync ("http://www.albahari.com");
     Console.WriteLine (await task1);
     Console.WriteLine (await task2);


HttpClient has a  Timeout property and a  BaseAddress property which prefixes a

URI to every request.  HttpClient is somewhat of a thin shell: most of the other
properties that you might expect to find here are defined in another classed called
HttpClientHandler. To access this class, you instantiate it and then pass the instance
into HttpClient’s constructor:
var handler = new HttpClientHandler { UseProxy = false };
var client = new HttpClient (handler);
...

In this example, we told the handler to disable proxy support. There are also
properties to control cookies, automatic redirection, authentication, and so on
(we’ll describe these in the following sections, and in  “Working with
HTTP” on page 671).


GetAsync and response messages


The  GetStringAsync,  GetByteArrayAsync, and  GetStreamAsync methods are conve-
nient shortcuts for calling the more general  GetAsync method, which returns a 
response message:
var client = new HttpClient();
// The GetAsync method also accepts a CancellationToken.
HttpResponseMessage response = await client.GetAsync ("http://...");
response.EnsureSuccessStatusCode();
string html = await response.Content.ReadAsStringAsync();

HttpResponseMessage exposes properties for accessing the headers (see “Working
with HTTP” on page 671) and the HTTP StatusCode. Unlike with WebClient, an
unsuccessful status code such as 404 (not found) doesn’t cause an exception to be
thrown unless you explicitly call EnsureSuccessStatusCode. Communication or DNS
errors, however, do throw exceptions (see “Exception Handling” on page 670).
HttpResponseMessage has a CopyToAsync method for writing to another stream, which
is useful in writing the output to a file:
using (var fileStream = File.Create ("linqpad.html"))
  await response.Content.CopyToAsync (fileStream);
GetAsync is one of four methods corresponding to HTTP’s four verbs (the others are
PostAsync, PutAsync and DeleteAsync). We demonstrate PostAsync later in “Upload-
ing Form Data” on page 673.


SendAsync and request messages

The four methods just described are all shortcuts for calling SendAsync, the single
low-level method into which everything else feeds. To use this, you first construct
an HttpRequestMessage:
var client = new HttpClient();
var request = new HttpRequestMessage (HttpMethod.Get, "http://...");
HttpResponseMessage response = await client.SendAsync (request);
response.EnsureSuccessStatusCode();
...
Instantiating an HttpRequestMessage object means you can customize properties of
the request, such as the headers (see “Headers” on page 671) and the content itself,
allowing you to upload data.


Uploading data and HttpContent
After instantiating an HttpRequestMessage object, you can upload content by assign-
ing its Content property. The type for this property is an abstract class called HttpCon
tent. The Framework includes the following concrete subclasses for different kinds
of content (you can also write your own):
• ByteArrayContent
• StreamContent
• FormUrlEncodedContent (see “Uploading Form Data” on page 673)
• StreamContent


For example:
var client = new HttpClient (new HttpClientHandler { UseProxy = false });
var request = new HttpRequestMessage (
  HttpMethod.Post, "http://www.albahari.com/EchoPost.aspx");
request.Content = new StringContent ("This is a test");
HttpResponseMessage response = await client.SendAsync (request);
response.EnsureSuccessStatusCode();
Console.WriteLine (await response.Content.ReadAsStringAsync());


HttpMessageHandler
We said previously that most of the properties for customizing requests are defined
not in HttpClient but in HttpClientHandler. The latter is actually a subclass of the
abstract HttpMessageHandler class, defined as follows:
public abstract class HttpMessageHandler : IDisposable
{
  protected internal abstract Task<HttpResponseMessage> SendAsync
    (HttpRequestMessage request, CancellationToken cancellationToken);
  public void Dispose();
  protected virtual void Dispose (bool disposing);
}
The SendAsync method is called from HttpClient’s SendAsync method.
HttpMessageHandler is simple enough to subclass easily and offers an extensibility
point into HttpClient.


Unit testing and mocking

We can subclass HttpMessageHandler to create a mocking handler to assist with unit
testing:
class MockHandler : HttpMessageHandler
{
  Func <HttpRequestMessage, HttpResponseMessage> _responseGenerator;
  public MockHandler
    (Func <HttpRequestMessage, HttpResponseMessage> responseGenerator)
  {
    _responseGenerator = responseGenerator;
  }
  protected override Task <HttpResponseMessage> SendAsync
    (HttpRequestMessage request, CancellationToken cancellationToken)
  {
    cancellationToken.ThrowIfCancellationRequested();
    var response = _responseGenerator (request);
    response.RequestMessage = request;
    return Task.FromResult (response);
  }
}
Its constructor accepts a function that tells the mocker how to generate a response
from a request. This is the most versatile approach, as the same handler can test
multiple requests.
We’ve thunked down to being synchronous by virtue of using Task.FromResult. We
could have maintained asynchrony by having our response generator return a
Task<HttpResponseMessage>, but this is pointless given that we can expect a mocking
function to be short-running. Here’s how to use our mocking handler:
var mocker = new MockHandler (request =>
  new HttpResponseMessage (HttpStatusCode.OK)
Client-Side Classes | 665
Networking  {
    Content = new StringContent ("You asked for " + request.RequestUri)
  });
var client = new HttpClient (mocker);
var response = await client.GetAsync ("http://www.linqpad.net");
string result = await response.Content.ReadAsStringAsync();
Assert.AreEqual ("You asked for http://www.linqpad.net/", result);
(Assert.AreEqual is a method you’d expect to find in a unit-testing framework such
as NUnit.)


Chaining handlers with DelegatingHandler
You can create a message handler that calls another (resulting in a chain of handlers)
by subclassing DelegatingHandler. This can be used to implement custom authen-
tication, compression, and encryption protocols. The following demonstrates a
simple logging handler:
class LoggingHandler : DelegatingHandler
{
  public LoggingHandler (HttpMessageHandler nextHandler)
  {
     InnerHandler = nextHandler;
  }
  protected async override Task <HttpResponseMessage> SendAsync
    (HttpRequestMessage request, CancellationToken cancellationToken)
  {
    Console.WriteLine ("Requesting: " + request.RequestUri);
    var response = await base.SendAsync (request, cancellationToken);
    Console.WriteLine ("Got response: " + response.StatusCode);
    return response;
  }
}
Notice that we’ve maintained asynchrony in overriding SendAsync. Introducing the
async modifier when overriding a task-returning method is perfectly legal—and de-
sirable in this case.
A better solution than writing to the Console would be to have the constructor accept
some kind of logging object. Better still would be to accept a couple of Action<T>
delegates which tell it how to log the request and response objects.







原创粉丝点击