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ó:
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