Flurl proxy: gắn WebProxy vào Flurl 4 theo client và theo host
Flurl proxy là cách cho thư viện Flurl.Http trong .NET gửi request qua máy trung gian bằng cách gắn proxy vào handler bên dưới mỗi client. Flurl 4 quản lý client theo host, nên proxy cũng nên cấu hình theo host hoặc theo client có tên. ProxyVN đối chiếu tài liệu Flurl 4.0.2 để viết project mẫu, client theo host và các ca kiểm request qua handler đã cấu hình.
Flurl proxy nằm ở handler, không ở request
Chuỗi FlurlClient tới SocketsHttpHandler

Proxy thuộc handler của client
Trang cấu hình của Flurl mô tả rõ chuỗi bên dưới: FlurlClient bọc HttpClient, HttpClient giao việc cho HttpClientHandler, và từ .NET Core 2.1 HttpClientHandler giao gần hết việc cho SocketsHttpHandler trên mọi nền tảng trừ trình duyệt. Proxy là thuộc tính của handler, nên muốn Flurl đi proxy thì phải chạm tới handler.
Flurl không có phương thức kiểu WithProxy trên request. Mọi thiết lập proxy đi qua IFlurlClientBuilder với hai lựa chọn: ConfigureInnerHandler để chỉnh HttpClientHandler, hoặc UseSocketsHttpHandler để dùng thẳng SocketsHttpHandler. Mỗi FlurlClient có một handler, nên proxy gắn với client chứ không gắn với từng request.
Muốn hai tuyến mạng thì cần hai client, mỗi client một handler. Đây là nguyên tắc thiết kế xuyên suốt bài.
Ranh giới với bài C# HttpClient proxy
Bài C# HttpClient proxy đã hướng dẫn WebProxy, HttpClientHandler và DefaultProxy ở mức HttpClient thuần. Bài này không trình bày lại phần đó. Nó chỉ đi vào API của Flurl 4: client theo host trong chế độ không client, client có tên trong DI và hai cách cấu hình handler. Bài cũng nói những thiết lập của Flurl ảnh hưởng tới proxy như redirect, cookie và dải mã trạng thái.
Nếu dự án dùng HttpClient trực tiếp hay IHttpClientFactory, bài HttpClient phù hợp hơn. Flurl có cách quản lý client riêng, áp cấu hình HttpClient thuần vào Flurl dễ dẫn tới proxy không có tác dụng.
Phạm vi kiểm thử
Máy lab hôm viết bài không có .NET SDK, nên project trong bài chưa được biên dịch và chạy trên máy này. Mình viết theo tài liệu Flurl 4 trên flurl.dev và bản mới nhất trên NuGet là 4.0.2 ngày 27/10/2026. Phần hành vi của handler theo tài liệu .NET.
Cuối bài có bộ ca kiểm với proxy cục bộ có nhật ký để anh em tự chạy. Kết quả trên đúng bản .NET và Flurl của anh em là chuẩn cuối cùng.
Chuẩn bị project .NET cho Flurl proxy
Tạo project và thêm gói
Brief yêu cầu một project .NET. Tạo ứng dụng console, thêm gói Flurl.Http trên NuGet với phiên bản ghim cụ thể.
dotnet new console -n FlurlProxyLab
cd FlurlProxyLab
dotnet add package Flurl.Http --version 4.0.2
Flurl 4 dùng System.Text.Json làm bộ tuần tự JSON mặc định, khác bản 3 dùng Newtonsoft. Nếu mã cũ phụ thuộc Newtonsoft, thêm gói Flurl.Http.Newtonsoft như tài liệu hướng dẫn. Chuyện này không liên quan proxy nhưng hay gây lỗi khi nâng cấp cùng lúc.
Thông tin proxy trong cấu hình
Đọc địa chỉ và tài khoản proxy từ biến môi trường hoặc user secrets, không ghi trong mã. Trong .NET, WebProxy nhận tài khoản qua NetworkCredential với tên và mật khẩu là chuỗi riêng, nên mật khẩu có @ hay # không cần mã hóa phần trăm.
static WebProxy BuildProxy() => new(Environment.GetEnvironmentVariable("PROXY_URL"))
{
Credentials = new NetworkCredential(
Environment.GetEnvironmentVariable("PROXY_USER"),
Environment.GetEnvironmentVariable("PROXY_PASS")),
BypassProxyOnLocal = true
};
PROXY_URL có dạng http://203.0.113.10:8080, không chứa tài khoản. Tách tài khoản ra NetworkCredential gọn hơn và tránh lỗi mã hóa. Bài định dạng proxy có bảng đổi từ ip:port:user:pass.
HttpClient đọc biến môi trường khi không đặt proxy
Trên .NET Core trở đi, nếu handler không đặt Proxy, HttpClient dùng HttpClient.DefaultProxy, vốn đọc HTTP_PROXY, HTTPS_PROXY, ALL_PROXY và NO_PROXY trên Linux, macOS, còn trên Windows đọc thêm cài đặt proxy của hệ thống. Nghĩa là client Flurl không cấu hình proxy vẫn có thể đi proxy tùy máy.
Với dự án cần hành vi nhất quán, đặt Proxy tường minh cho client cần proxy, và đặt UseProxy bằng false cho client cần đi thẳng. Đừng dựa vào môi trường của từng máy.
Cấu hình Flurl proxy bằng ConfigureInnerHandler
Ví dụ từ tài liệu
Tài liệu Flurl đưa ví dụ gắn proxy qua ConfigureInnerHandler trong chế độ không client. Hàm nhận HttpClientHandler, anh em đặt Proxy và UseProxy.
FlurlHttp.Clients.WithDefaults(builder => builder
.ConfigureInnerHandler(hch =>
{
hch.Proxy = BuildProxy();
hch.UseProxy = true;
}));
WithDefaults áp cho mọi client mà Flurl tạo trong chế độ không client. Mọi request kiểu "https://some-api.com".GetJsonAsync đều đi qua proxy. Đây là cách nhanh nhất cho ứng dụng nhỏ mà mọi request ra ngoài đều cần proxy.
Cảnh báo về redirect và cookie
Tài liệu cảnh báo Flurl tắt AllowAutoRedirect và UseCookies trên handler để tự cài lại hai tính năng này theo cách của nó. Nếu đặt một trong hai thành true trong ConfigureInnerHandler, có thể làm hỏng tính năng redirect hay cookie của Flurl. Khi chép đoạn cấu hình handler từ bài HttpClient thuần, xóa hai dòng này.
Điều này quan trọng với proxy vì redirect qua proxy là chuyện hay gặp. Để Flurl tự xử lý redirect, nó áp đúng các thiết lập Redirects như MaxAutoRedirects và ForwardAuthorizationHeader. Handler tự redirect sẽ bỏ qua các thiết lập đó.
Proxy có tài khoản và 407
Với WebProxy có Credentials, handler gửi tài khoản khi proxy yêu cầu. Với URL https, request đi qua đường hầm CONNECT. Khi tài khoản sai, .NET thường ném HttpRequestException ở bước mở đường hầm với thông điệp nói proxy tunnel request thất bại với mã 407. Flurl bọc ngoại lệ này thành FlurlHttpException.
Với URL http, proxy trả 407 như một response. Flurl coi mã ngoài dải 2xx, 3xx là lỗi theo AllowedHttpStatusRange mặc định, nên cũng ném FlurlHttpException, lần này có StatusCode là 407. Mã xử lý lỗi cần nhìn cả hai dạng. Bài sửa lỗi 407 Proxy Authentication Required liệt kê nguyên nhân phía nhà cung cấp.
Cấu hình Flurl proxy bằng UseSocketsHttpHandler
Khi nào dùng SocketsHttpHandler trực tiếp
Tài liệu giải thích một số cấu hình của SocketsHttpHandler không có trên HttpClientHandler, ví dụ PooledConnectionLifetime. Với proxy, PooledConnectionLifetime có ý nghĩa thực tế: proxy xoay đổi IP theo thời gian, kết nối giữ quá lâu sẽ gắn với IP cũ.
FlurlHttp.Clients.WithDefaults(builder => builder
.UseSocketsHttpHandler(shh =>
{
shh.Proxy = BuildProxy();
shh.UseProxy = true;
shh.PooledConnectionLifetime = TimeSpan.FromMinutes(5);
shh.ConnectTimeout = TimeSpan.FromSeconds(5);
}));
ConnectTimeout của SocketsHttpHandler giới hạn bước kết nối, khi có proxy là kết nối tới proxy. Đây là thiết lập không có trên HttpClientHandler.
Không dùng cả hai trên một builder
Tài liệu ghi rõ: gọi cả ConfigureInnerHandler và UseSocketsHttpHandler trên cùng một builder sẽ gây ngoại lệ lúc chạy. Chọn một trong hai. Nếu đã có cấu hình cũ bằng ConfigureInnerHandler và muốn thêm PooledConnectionLifetime, chuyển toàn bộ sang UseSocketsHttpHandler.
UseSocketsHttpHandler không dùng được trên Blazor chạy trong trình duyệt. Với Blazor WebAssembly, request do trình duyệt thực hiện, proxy do trình duyệt quyết định, mã .NET không đặt được proxy.
Middleware không thay được proxy
Flurl hỗ trợ DelegatingHandler qua AddMiddleware, ví dụ Polly để thử lại. Middleware nằm phía trên handler, nó thấy request nhưng không đổi được đường mạng. Đừng tìm cách đặt proxy trong middleware. Middleware hợp cho thử lại, ghi log, đo thời gian quanh request đi qua proxy.
Client theo host cho Flurl proxy

Mỗi host một client
Chế độ không client lưu client theo host
Trang quản lý client của Flurl ghi rằng ở chế độ không client, mọi request tới cùng tổ hợp scheme, host và cổng dùng chung một client, lưu trong FlurlHttp.Clients. Cấu hình ở cấp client, như tiêu đề mặc định, đi theo mọi request tới host đó.
Điều này cho phép đặt proxy khác nhau cho từng host bằng ConfigureClientForUrl. Brief yêu cầu client theo host, đây là cách làm trực tiếp nhất.
FlurlHttp.ConfigureClientForUrl("https://shop.example.com")
.ConfigureInnerHandler(hch => { hch.Proxy = BuildProxy(); hch.UseProxy = true; });
FlurlHttp.ConfigureClientForUrl("http://inventory.internal:8080")
.ConfigureInnerHandler(hch => { hch.UseProxy = false; });
Request tới shop.example.com đi proxy, request tới API nội bộ đi thẳng, không phụ thuộc biến môi trường. Gọi cấu hình này lúc khởi động, trước khi có request nào tới các host đó.
Thứ tự gọi quan trọng
Client cho một host được tạo ở lần đầu có request tới host đó. Nếu request đã chạy trước khi gọi ConfigureClientForUrl, client đã được tạo với cấu hình mặc định, và cấu hình sau có thể không áp được lên handler đã dựng. Đặt mọi ConfigureClientForUrl trong mã khởi động.
Nếu dùng chiến lược lưu client tùy chỉnh bằng UseClientCachingStrategy, tài liệu nhắc phải gọi nó trước mọi ConfigureClientForUrl. Chiến lược tùy chỉnh hữu ích khi cùng một host cần nhiều proxy, ví dụ theo tiêu đề phiên bản dịch vụ hay theo tài khoản đang xử lý.
Cổng khác là client khác
Vì khóa client gồm cả cổng, https://api.example.com và https://api.example.com:8443 là hai client. Cấu hình proxy cho một cái không áp cho cái kia. Khi API dùng cổng không chuẩn, ConfigureClientForUrl với đúng URL có cổng.
Flurl proxy với dependency injection
FlurlClientCache có tên
Với ứng dụng dùng DI của Microsoft, tài liệu khuyên đăng ký IFlurlClientCache là singleton, gắn với FlurlClientCache và cấu hình ở gốc ứng dụng. Cache hỗ trợ client có tên với URL gốc và cấu hình riêng.
builder.Services.AddSingleton<IFlurlClientCache>(_ => new FlurlClientCache()
.Add("ShopApi", "https://shop.example.com", b => b
.UseSocketsHttpHandler(shh =>
{
shh.Proxy = BuildProxy();
shh.UseProxy = true;
shh.ConnectTimeout = TimeSpan.FromSeconds(5);
})
.WithSettings(s => s.Timeout = TimeSpan.FromSeconds(30)))
.Add("Inventory", "http://inventory.internal:8080", b => b
.ConfigureInnerHandler(hch => hch.UseProxy = false)));
Service lấy client bằng tên từ IFlurlClientCache được tiêm vào. ShopApi đi proxy, Inventory đi thẳng. Hai client có handler riêng, đổi cấu hình bên này không ảnh hưởng bên kia.
Dùng client trong service
Trong service, lấy client một lần trong hàm khởi tạo rồi dùng cho mọi request. Không tạo FlurlClient mới mỗi request, vì tài liệu nhắc lại lời khuyên chung với HttpClient: tạo mới liên tục gây cạn socket.
public sealed class PriceService(IFlurlClientCache clients)
{
private readonly IFlurlClient _shop = clients.Get("ShopApi");
public Task<Price[]> GetPricesAsync(string sku) =>
_shop.Request("api", "prices").SetQueryParam("sku", sku).GetJsonAsync<Price[]>();
}
Tên ShopApi là điểm duy nhất gắn service với cấu hình mạng. Khi đổi proxy, chỉ sửa đăng ký ở gốc ứng dụng.
Với Flurl proxy, proxy thuộc về handler của từng client. Client có tên trong FlurlClientCache là cách rõ ràng nhất để biết request nào đi đường nào.
Thiết lập Flurl ảnh hưởng tới Flurl proxy

HttpTest không chạm tới proxy
Timeout mặc định 100 giây
Bảng thiết lập trong tài liệu ghi Timeout mặc định 100 giây. Với proxy treo, 100 giây là lâu. Đặt Timeout ngắn hơn ở cấp client cho tuyến proxy, và ConnectTimeout của SocketsHttpHandler để lỗi kết nối tới proxy đến sớm.
Khi hết giờ, Flurl ném FlurlHttpTimeoutException. Phân biệt ngoại lệ này với FlurlHttpException thường giúp biết request chậm hay bị từ chối.
Dải mã trạng thái cho phép
AllowedHttpStatusRange mặc định coi 2xx, 3xx là thành công, còn lại ném FlurlHttpException. Có thể đổi bằng AllowAnyHttpStatus để tự xử lý mọi mã. Với proxy, mình giữ mặc định nhưng bắt FlurlHttpException và đọc StatusCode, tách 407 khỏi lỗi website.
try
{
var data = await _shop.Request("api", "items").GetJsonAsync<Item[]>();
}
catch (FlurlHttpTimeoutException)
{
log.LogWarning("Qua han khi goi qua proxy");
}
catch (FlurlHttpException ex) when (ex.StatusCode == 407)
{
log.LogError("Proxy tu choi tai khoan");
}
catch (FlurlHttpException ex) when (ex.StatusCode is 403 or 429)
{
log.LogWarning("Website tu choi: {Code}", ex.StatusCode);
}
catch (FlurlHttpException ex)
{
log.LogError(ex, "Loi mang hoac proxy");
}
Nhánh cuối bắt cả trường hợp 407 trong đường hầm HTTPS, nơi không có StatusCode. Ghi ex.Message và ex.InnerException để thấy thông điệp gốc của .NET.
Redirect
Thiết lập Redirects mặc định cho phép theo tối đa 10 lần, không chuyển từ https sang http, không chuyển tiếp tiêu đề và không chuyển tiếp tiêu đề Authorization. Khi redirect, request sau vẫn đi qua cùng client, nên qua cùng proxy nếu host không đổi. Nếu redirect sang host khác, chế độ không client sẽ dùng client của host mới, với cấu hình proxy của host mới.
Đây là điểm dễ trượt khi cấu hình proxy theo host. Website chuyển hướng sang tên miền CDN, mà tên miền CDN chưa được ConfigureClientForUrl, request sau đi theo cấu hình mặc định. Với client có tên trong DI, client giữ nguyên handler cho mọi URL đầy đủ, nên không có vấn đề này. Sự kiện OnRedirect cho phép ghi log URL đích để phát hiện.
Flurl proxy với proxy xoay và nhiều tài khoản
Một host, nhiều proxy
Với việc lấy dữ liệu từ một website qua nhiều IP, chế độ không client mặc định không đủ, vì mọi request tới cùng host dùng chung một client và một proxy. Tài liệu Flurl có UseClientCachingStrategy để đổi khóa lưu client. Có thể ghép host với một tiêu đề riêng chứa mã phiên proxy, mỗi mã phiên một client.
FlurlHttp.UseClientCachingStrategy(request =>
{
var name = FlurlHttp.BuildClientNameByHost(request);
return request.Headers.TryGetFirst("X-Proxy-Session", out var session)
? $"{name}:{session}"
: name;
});
Sau đó với mỗi mã phiên, cấu hình client tương ứng với tài khoản proxy chứa mã phiên theo quy ước của nhà cung cấp. Tiêu đề X-Proxy-Session chỉ dùng làm khóa, nên gỡ nó trước khi gửi đi bằng sự kiện BeforeCall để website không thấy. Đây là ví dụ triển khai dựa trên API trong tài liệu, mình chưa chạy.
Dùng client có tên thay cho chiến lược lưu
Cách dễ đọc hơn là tạo sẵn vài client có tên trong FlurlClientCache, mỗi client một tài khoản proxy, rồi chọn client theo vòng. Số client bằng số phiên proxy muốn dùng song song. Cách này tránh logic khóa phức tạp và dễ kiểm bằng nhật ký proxy.
Với proxy xoay đổi IP theo thời gian ở phía nhà cung cấp, một client là đủ, nhưng đặt PooledConnectionLifetime ngắn để kết nối không giữ quá lâu. Kết nối giữ lâu sẽ tiếp tục dùng IP cũ dù nhà cung cấp đã xoay, vì xoay thường áp cho kết nối mới.
Giới hạn số request đồng thời
SocketsHttpHandler có MaxConnectionsPerServer để giới hạn số kết nối tới một máy chủ. Khi có proxy, kết nối đi tới proxy, nên giới hạn này áp lên số kết nối tới proxy. Đặt bằng hoặc thấp hơn hạn mức luồng của gói proxy, và nếu nhiều client cùng dùng một tài khoản proxy thì cộng số kết nối của tất cả client lại để so với hạn mức. Vượt hạn mức, proxy có thể từ chối hoặc cắt kết nối, trong log trông như mạng chập chờn.
Flurl proxy sau proxy kiểm tra TLS của công ty
Lỗi chứng chỉ chỉ ở công ty
Ở mạng công ty có thiết bị giải mã HTTPS, website trả chứng chỉ do CA nội bộ ký. Trên Windows, .NET dùng kho chứng chỉ của hệ điều hành, nơi CA nội bộ thường đã được cài qua chính sách nhóm, nên ít gặp lỗi. Trên Linux trong container, kho chứng chỉ của ảnh container thường không có CA nội bộ, request báo lỗi xác thực chứng chỉ.
Cách sửa là thêm CA nội bộ vào kho chứng chỉ của ảnh container ở bước dựng ảnh. Đổi proxy hay đổi tài khoản không sửa được lỗi này, vì đường đi vẫn đúng, chỉ có lòng tin chứng chỉ là thiếu.
Không tắt kiểm tra chứng chỉ
Nhiều ví dụ đặt ServerCertificateCustomValidationCallback trả true để bỏ qua lỗi. Làm vậy trong ConfigureInnerHandler khiến mọi request của client chấp nhận chứng chỉ bất kỳ, kể cả chứng chỉ giả của kẻ đứng giữa. Với client gửi khóa API qua proxy, đây là rủi ro lớn hơn nhiều so với lỗi ban đầu.
Nếu buộc phải chấp nhận một CA riêng, kiểm chứng chỉ trong callback theo đúng dấu vân tay của CA đó thay vì trả true cho mọi trường hợp. Ghi rõ lý do trong mã để người sau không chép đoạn này sang client khác.
Ghi log Flurl proxy bằng sự kiện
BeforeCall, AfterCall và OnError
Tài liệu Flurl có bốn sự kiện BeforeCall, AfterCall, OnError, OnRedirect. Đối tượng FlurlCall trong sự kiện có Request, Response, Exception, Duration, Succeeded. Đây là chỗ ghi log tập trung cho mọi request qua proxy mà không chạm vào mã nghiệp vụ.
FlurlHttp.Clients.WithDefaults(b => b
.AfterCall(call => log.LogInformation(
"{Url} {Status} {Ms}ms",
call.Request.Url.ToString(),
call.Response?.StatusCode,
call.Duration?.TotalMilliseconds))
.OnRedirect(call => log.LogInformation("Redirect toi {Url}", call.Redirect.Url)));
Ghi URL đã bỏ query nếu query có dữ liệu nhạy cảm. Không ghi tiêu đề request. Với OnError, ghi call.Exception.Message và InnerException để thấy thông điệp gốc của .NET khi proxy từ chối.
Đọc log để chọn proxy
Sau một ngày, nhóm log theo client có tên và tính thời gian trung vị. Client nào có Duration tăng dần vào giờ cao điểm có thể đang dùng proxy quá tải. Client nào có tỷ lệ 403, 429 cao đang dùng IP bị website hạn chế. Log OnRedirect cho biết website có đang chuyển hướng sang host chưa cấu hình proxy hay không.
Kiểm request Flurl proxy với handler đã cấu hình
HttpTest không đi qua mạng
Flurl có HttpTest để giả lập response trong test. Tài liệu ghi thiết lập của HttpTest luôn thắng mọi cấu hình khác. Khi HttpTest đang hoạt động, request không đi ra mạng, nên mọi test dùng HttpTest đều không chạm tới proxy.
Vì vậy test nghiệp vụ dùng HttpTest, còn test proxy phải chạy với handler thật. Tách hai nhóm test, đặt tên rõ ràng để không ai nhầm test HttpTest xanh nghĩa là proxy đúng.
Test tích hợp với proxy cục bộ
Brief yêu cầu test request với handler đã cấu hình. Dựng một proxy cục bộ có nhật ký, ví dụ mitmproxy hay Squid, đòi tài khoản. Viết test gọi client ShopApi trỏ về một URL công khai, rồi đọc nhật ký proxy để xác nhận có dòng CONNECT tới host đó với tài khoản đúng.
- ShopApi tới URL https: nhật ký có CONNECT, test nhận 200.
- Inventory tới API nội bộ: nhật ký không có dòng mới.
- ShopApi với mật khẩu sai: test nhận FlurlHttpException, nhật ký ghi tài khoản bị từ chối.
- ShopApi với proxy không mở cổng: ngoại lệ trong khoảng ConnectTimeout.
- Clientless tới host chưa cấu hình: kiểm nó có đi theo biến môi trường hay không.
- Redirect sang host khác: kiểm request sau đi đường nào.
- Kiểm IP ra: gọi dịch vụ trả IP qua ShopApi, so với IP proxy mong đợi.
Ca thứ năm và thứ sáu trả lời câu hỏi chế độ không client có rò request ra ngoài proxy hay không. Đây là hai ca hay bị bỏ qua nhất.
Ghi kết quả
Ghi bản .NET, bản Flurl, hệ điều hành và kết quả từng ca. Trên Windows, DefaultProxy đọc cả cài đặt proxy hệ thống, nên kết quả ca thứ năm có thể khác giữa Windows và Linux.
Nâng cấp Flurl proxy từ Flurl 3 lên Flurl 4
Cấu hình proxy kiểu cũ không còn chạy
Ở Flurl 3, nhiều dự án đặt proxy bằng cách viết một lớp kế thừa DefaultHttpClientFactory, ghi đè CreateMessageHandler để trả HttpClientHandler có Proxy, rồi gán factory đó qua FlurlHttp.Configure. Flurl 4 thay cách quản lý client bằng IFlurlClientBuilder và FlurlClientCache. Mã factory cũ cần viết lại theo ConfigureInnerHandler hoặc UseSocketsHttpHandler.
Tài liệu có trang hướng dẫn nâng cấp từ 3.x riêng. Khi nâng cấp, đừng chỉ sửa cho biên dịch được. Chạy lại bộ ca kiểm proxy, vì một factory bị bỏ qua sẽ khiến mọi request rơi về DefaultProxy, có thể vẫn chạy được trên máy lập trình có biến môi trường proxy nhưng đi thẳng trên máy chủ.
Kiểm tra sau nâng cấp
Ca quan trọng nhất sau nâng cấp là kiểm IP ra từ máy chủ thật, không phải từ máy lập trình. Máy lập trình thường có cài đặt proxy hệ thống hoặc biến môi trường, che mất lỗi cấu hình. Chạy ca kiểm IP trong pipeline triển khai, dừng triển khai nếu IP ra là IP của máy chủ.
Ghi lại trong ghi chú phát hành rằng cách cấu hình proxy đã đổi, kèm đoạn mã mới và danh sách client có tên đang dùng proxy nào. Khi có sự cố sau nâng cấp, danh sách này giúp khoanh vùng nhanh client nào đang cấu hình sai, thay vì phải đọc lại toàn bộ mã khởi động. Người duy trì sau này sẽ không phải đoán vì sao lớp factory cũ biến mất.
Đưa Flurl proxy vào dự án thật
Một chỗ cấu hình mạng
Gom mọi ConfigureClientForUrl hoặc đăng ký FlurlClientCache vào một tệp cấu hình mạng. Service chỉ biết tên client. Khi đổi nhà cung cấp proxy hay thêm host mới, sửa tệp đó.
Không ghi Credentials của WebProxy ra log. Sự kiện BeforeCall và AfterCall của Flurl hữu ích để ghi log request qua proxy, chỉ ghi URL, mã trạng thái, thời gian, không ghi tiêu đề.
Chọn loại proxy
Flurl hay dùng trong dịch vụ .NET gọi API đối tác và lấy dữ liệu. Với API yêu cầu IP cố định, proxy tĩnh phù hợp. Với lấy dữ liệu nhiều trang, proxy xoay chia tải, nhớ đặt PooledConnectionLifetime để kết nối không giữ IP cũ quá lâu.
ProxyVN có proxy dân cư tĩnh, datacenter và proxy xoay, hỗ trợ HTTP và SOCKS5, cấp tài khoản user và mật khẩu. Nếu cần proxy cho client ShopApi, anh em có thể mua proxy tốc độ cao tại trang chủ ProxyVN rồi chạy bộ ca kiểm ở trên.
SOCKS5
Từ .NET 6, SocketsHttpHandler hỗ trợ proxy SOCKS qua WebProxy với địa chỉ socks5://. Vì Flurl dùng SocketsHttpHandler bên dưới, về nguyên tắc có thể dùng SOCKS5. Mình chưa kiểm với Flurl, và cần kiểm cách .NET xử lý tài khoản SOCKS trước khi dùng thật. Bài proxy HTTP và SOCKS5 giúp chọn loại.
Câu hỏi thường gặp về Flurl proxy
Flurl có phương thức đặt proxy cho một request không. Không. Proxy thuộc handler của client. Dùng client khác cho tuyến khác.
Dùng ConfigureInnerHandler hay UseSocketsHttpHandler. UseSocketsHttpHandler nếu cần ConnectTimeout, PooledConnectionLifetime. Không gọi cả hai trên một builder.
Có cần đặt AllowAutoRedirect trên handler không. Không. Flurl tắt nó để tự xử lý redirect. Bật lại có thể làm hỏng tính năng redirect của Flurl.
Mật khẩu proxy có ký tự đặc biệt. Dùng NetworkCredential, không cần mã hóa phần trăm.
Client không cấu hình proxy có đi thẳng không. Không chắc. HttpClient có thể dùng DefaultProxy từ biến môi trường hoặc cài đặt hệ thống. Đặt UseProxy bằng false nếu cần đi thẳng.
HttpTest có kiểm được proxy không. Không. HttpTest không đi ra mạng.
Đặt proxy cho chế độ không client và DI cùng lúc được không. Được, nhưng hai cơ chế dùng hai bộ lưu client khác nhau: FlurlHttp.Clients toàn cục và FlurlClientCache đăng ký trong DI. Cấu hình bên này không áp cho bên kia. Chọn một cơ chế cho cả dự án để tránh nhầm.
Có nên dùng Polly để thử lại qua proxy không. Có thể, qua AddMiddleware. Chỉ thử lại GET và lỗi mạng tạm thời, không thử lại POST tạo dữ liệu, và không thử lại khi nhận 407 vì tài khoản sai thì thử lại cũng sai.
Checklist Flurl proxy

Checklist Flurl proxy
Trước khi triển khai
- Flurl.Http 4.x ghim phiên bản.
- Proxy đặt trong ConfigureInnerHandler hoặc UseSocketsHttpHandler, không cả hai.
- Không bật AllowAutoRedirect, UseCookies trên handler.
- ConfigureClientForUrl gọi lúc khởi động, trước request đầu tiên.
- UseProxy bằng false cho client cần đi thẳng.
- Timeout ngắn hơn 100 giây cho tuyến proxy.
- Bắt FlurlHttpException và đọc StatusCode 407.
- Test tích hợp với handler thật, không chỉ HttpTest.
Giới hạn của bài
Project chưa được biên dịch và chạy trên máy lab vì không có .NET SDK. Bài theo tài liệu Flurl 4 và bản NuGet 4.0.2. Thông điệp lỗi 407 trong đường hầm và hành vi DefaultProxy theo tài liệu .NET, cần kiểm trên đúng bản .NET. Chưa thử SOCKS5, chưa thử Blazor, chưa thử gateway thương mại.
Flurl proxy và những điều cần nhớ
Flurl proxy gắn WebProxy vào handler của từng client qua ConfigureInnerHandler hoặc UseSocketsHttpHandler. Cấu hình theo host bằng ConfigureClientForUrl hoặc client có tên trong DI, đặt UseProxy false cho tuyến nội bộ, bắt 407 qua FlurlHttpException. Cần proxy ổn định cho Flurl proxy, liên hệ Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam.