C# cơ bản .NET Core
Lập trình C# (C Sharp)

Lớp HttpMessageHandler

Lớp HttpMessageHandler là lớp trừu tượng, nó là lớp cơ sở được thư viện .NET Core triển khai ra các lớp như DelegatingHandler, HttpMessageHandler, HttpClientHandler ... các lớp triển khai này (hoặc nếu tự xây dựng lớp triển khai HttpMessageHandler) thì phải nạp chồng phương thức SendAsync:

protected Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken);

Các lớp triển khai HttpMessageHandler dùng để khởi tạo HttpClient, lúc này HttpCliet thực hiện gửi truy vấn (SendAsync) thì SendAsync của handler sẽ thực thi.

Sử dụng HttpClientHandler cho HttpClient

HttpClientHandler là một lớp triển khai từ HttpMessageHandler, nó thực hiện cuối cùng trong chuỗi các handler nếu có để thực sự gửi truy vấn HTTP

Chú ý, từ .NET Core 2.1 khuyến khích sử dụng SocketsHttpHandler thay cho HttpClientHandler

Một số thuộc tính trong HttpClientHandler

Thuộc tính Mô tả
AllowAutoRedirect Thuộc tính, mặc định là true, để thiết lập tự động chuyển hướng. Ví dụ truy vấn đến URI có chuyển hướng đến đích mới (301) thì - HttpClient sẽ tự động chuyển hướng truy vấn đến đó.
AutomaticDecompression Thuộc tính thuộc tính để handler tự động giải nén / nén nội dung HTTP, nó thuộc kiểu enum DecompressionMethods gồm có:
  • DecompressionMethods.None không sử dụng nén
  • DecompressionMethods.GZip dùng thuật toán gZip
  • DecompressionMethods.Deflate dùng thuật toán nén deflate

Ví dụ có thể gán:

AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
UseCookies Mặc định là true: cho phép sử dụng thuộc tính CookieContainer để lưu các Cookie của server khi respone trả về, cũng như tự động gửi Cookie khi gửi truy vấn
CookieContainer Thuộc tính thuộc lớp CookieContainer, nó lưu các cookie.

Ví dụ sau có sử dụng HttpClientHandler để làm handler cho HttpClient, handler này có thiết lập để sử dụng Cookie gửi đi hoặc nhận về.

static async Task Main(string[] args)
{
    var url = "https://postman-echo.com/post";
    // Tạo handler
    using HttpClientHandler handler = new HttpClientHandler();

    // Tạo bộ chứa cookie và sử dụng bởi handler
    CookieContainer cookies = new CookieContainer();
    // Thêm các cookie nêu muốn
    // cookies.Add(new Uri(url), new Cookie("name", "value"));

    handler.CookieContainer = cookies;

    // Tạo HttpClient - thiết lập handler cho nó
    using var httpClient = new HttpClient(handler);


    // Tạo HttpRequestMessage
    using var httpRequestMessage = new HttpRequestMessage();
    httpRequestMessage.Method = HttpMethod.Post;
    httpRequestMessage.RequestUri = new Uri(url);
    httpRequestMessage.Headers.Add("User-Agent", "Mozilla/5.0");
    var parameters = new List<KeyValuePair<string,string>>()
    {
        new KeyValuePair<string, string>("key1", "value1"),
        new KeyValuePair<string, string>("key2", "value2")

    };
    httpRequestMessage.Content = new FormUrlEncodedContent(parameters);

    // Thực hiện truy vấn
    var response = await httpClient.SendAsync(httpRequestMessage);

    // Hiện thị các cookie (các cookie trả về có thể sử dụng cho truy vấn tiếp theo)
    cookies.GetCookies(new Uri(url)).ToList().ForEach(cookie => {
        Console.WriteLine($"{cookie.Name} = {cookie.Value}");
    });

    // Đọc chuỗi nội dung trả về (HTML)
    var result =  await response.Content.ReadAsStringAsync();
    Console.WriteLine(result);
}

Tham khảo mã nguồn HttpMessageHandler (git) hoặc tải về tại ex032

Sử dụng SocketsHttpHandler cho HttpClient

Lớp handler SocketsHttpHandler sử dụng giống hệt HttpClientHandler nó được thiết kế để sử dụng tốt hơn - nhanh hơn trên .NET Core, nó độc lập thiết bị tốt hơn (chạy tốt trên macOS, Linux).

Ví dụ trên sử dụng SocketsHttpHandler

static async Task Main(string[] args)
{
    var url = "https://postman-echo.com/post";
    // Tạo bộ chứa cookie và sử dụng bởi handler
    CookieContainer cookies = new CookieContainer();
    // Thêm các cookie nêu muốn
    // cookies.Add(new Uri(url), new Cookie("name", "value"));

    // Tạo handler
    using SocketsHttpHandler handler = new SocketsHttpHandler();
    handler.CookieContainer         = cookies;     // Thay thế CookieContainer mặc định
    handler.AllowAutoRedirect       = false;                // không cho tự động Redirect
    handler.AutomaticDecompression  = DecompressionMethods.Deflate | DecompressionMethods.GZip;
    handler.UseCookies              = true;

    // Tạo HttpClient - thiết lập handler cho nó
    using var httpClient = new HttpClient(handler);


    // Tạo HttpRequestMessage
    using var httpRequestMessage = new HttpRequestMessage();
    httpRequestMessage.Method = HttpMethod.Post;
    httpRequestMessage.RequestUri = new Uri(url);
    httpRequestMessage.Headers.Add("User-Agent", "Mozilla/5.0");
    httpRequestMessage.Headers.Add("Accept", "text/html,application/xhtml+xml+json");

    var parameters = new List<KeyValuePair<string,string>>()
    {
        new KeyValuePair<string, string>("key1", "value1"),
        new KeyValuePair<string, string>("key2", "value2")

    };
    httpRequestMessage.Content = new FormUrlEncodedContent(parameters);

    // Thực hiện truy vấn
    var response = await httpClient.SendAsync(httpRequestMessage);

    // Hiện thị các cookie (các cookie trả về có thể sử dụng cho truy vấn tiếp theo)
    cookies.GetCookies(new Uri(url)).ToList().ForEach(cookie => {
        Console.WriteLine($"{cookie.Name} = {cookie.Value}");
    });

    // Đọc chuỗi nội dung trả về (HTML)
    var result =  await response.Content.ReadAsStringAsync();
    Console.WriteLine(result);

Tham khảo mã nguồn 5.SocketsHttpHandler (git) hoặc tải về tại ex0323

Sử dụng DelegatingHandler cho HttpClient

DelegatingHandler (cũng triển khải từ HttpMessageHandler) là một handler đặc biệt, nó như một MiddleWare để tạo ra một pipeline (chuỗi các handler). Mỗi đối tượng DelegatingHandler có một thuộc tính InnerHandler (kiểu HttpMessageHandler), phải được gán bằng một đối tượng SocketsHttpHandler, HttpClientHandler hoặc DelegatingHandler... Thiết lập InnerHandler qua phương thức khởi tạo lớp DelegatingHandler. Khi thực hiện truy vấn SendAsync thì nó tiếp tục gọi SendAsync trong InnerHandler, cứ như vậy nó sẽ tạo thành chuỗi.

Nếu InnerHandler không phải là một DelegatingHandler khác thì InnerHandler đó là handler dưới cùng của chuỗi handler. Request - respone sẽ đi qua chuỗi handler từ trên cùng xuống dưới khi truy vấn và ngược lại khi trả về.

Ví dụ:

Tạo ra các handler, gồm có:

  • MyHttpClientHandler là một handler kế thừa HttpClientHandler, khi thực hiện truy vấn nó thực trực tiếp liên lạc với server
  • ChangeUri là một DelegatingHandler, khi SendAsync của nó thi hành nó kiểm tra nếu Uri là đến google.com thì tự động đổi thành github.com, rồi chuyển cho InnerHander của nó thực hiện tiếp SendAsync
  • DenyAccessFacebook là một DelegatingHandler, khi SendAsync nó thực hiện kiểm tra nếu Uri truy vấn đến Facebook thì bị cấm (trả về response ngay), nếu khác thì chuyển để InnerHander thực hiện tiếp SendAsync

Sau khi có 3 loại Hander này thì tạo chúng thành chuỗi theo thức tự:


 * Request  --> ......................  -->  .............  -->  .......................
                . DenyAccessFacebook .       . ChangeUri .       . MyHttpClientHandler .
 * Response <-- ......................  <--  .............  <--  .......................

Xây dựng các lớp Hander MyHttpClientHandler, ChangeUri, DenyAccessFacebook

public class MyHttpClientHandler : HttpClientHandler {
    public MyHttpClientHandler(CookieContainer  cookie_container) {

        CookieContainer         = cookie_container;     // Thay thế CookieContainer mặc định
        AllowAutoRedirect       = false;                // không cho tự động Redirect
        AutomaticDecompression  = DecompressionMethods.Deflate | DecompressionMethods.GZip;
        UseCookies              = true;
    }
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                                 CancellationToken cancellationToken)
    {
        Console.WriteLine("Bất đầu kết nối " + request.RequestUri.ToString());
        // Thực hiện truy vấn đến Server
        var response  = await base.SendAsync(request, cancellationToken);
        Console.WriteLine("Hoàn thành tải dữ liệu");
        return response;
    }
}

public class ChangeUri : DelegatingHandler
{
    public ChangeUri(HttpMessageHandler innerHandler) : base(innerHandler) {}

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                           CancellationToken cancellationToken)
    {
        var host = request.RequestUri.Host.ToLower();
        Console.WriteLine($"Check in  ChangeUri - {host}");
        if (host.Contains("google.com"))
        {
            // Đổi địa chỉ truy cập từ google.com sang github
            request.RequestUri = new Uri("https://github.com/");
        }
        // Chuyển truy vấn cho base (thi hành InnerHandler)
        return base.SendAsync(request, cancellationToken);
    }
}


public class DenyAccessFacebook : DelegatingHandler
{
    public DenyAccessFacebook(HttpMessageHandler innerHandler) : base(innerHandler) { }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                                 CancellationToken cancellationToken)
    {

        var host = request.RequestUri.Host.ToLower();
        Console.WriteLine($"Check in DenyAccessFacebook - {host}");
        if (host.Contains("facebook.com"))
        {
            var response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content  = new ByteArrayContent(Encoding.UTF8.GetBytes("Không được truy cập"));
            return await Task.FromResult<HttpResponseMessage>(response);
        }
        // Chuyển truy vấn cho base (thi hành InnerHandler)
        return await base.SendAsync(request, cancellationToken);
    }
}

Áp dụng thử

static async Task Main(string[] args)
{
    string url = "https://www.facebook.com/xuanthulab";

    CookieContainer cookies = new CookieContainer();

    // TẠO CHUỖI HANDLER
    var bottomHandler = new MyHttpClientHandler(cookies);              // handler đáy (cuối)
    var changeUriHandler = new ChangeUri(bottomHandler);
    var denyAccessFacebook = new DenyAccessFacebook(changeUriHandler); // handler đỉnh

    // Khởi tạo HttpCliet với hander đỉnh chuỗi hander
    var httpClient = new HttpClient(denyAccessFacebook);

    // Thực hiện truy vấn
    httpClient.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml+json");
    httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0");
    HttpResponseMessage response = await httpClient.GetAsync(url);
    response.EnsureSuccessStatusCode();
    string htmltext = await response.Content.ReadAsStringAsync();

    Console.WriteLine(htmltext);
}

Chạy thử, nếu truy cập facebook.com sẽ không truy cập được

Tương tự, nếu truy cập google.com sẽ chuyển thành bing.com

Truy cập các trang khác bình thường

Tham khảo mã nguồn 6.DelegatingHandler (git) hoặc tải về tại ex034


Đăng ký nhận bài viết mới