Làm thế nào tôi có thể biết khi nào httpClient đã hết thời gian?


142

Theo như tôi có thể nói, không có cách nào để biết rằng đó cụ thể là thời gian chờ đã xảy ra. Tôi không tìm đúng chỗ, hay tôi đang thiếu thứ gì lớn hơn?

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
{ 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
};
try
{
    var s = client.GetAsync("").Result;
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);
}

Điều này trả về:

Một hoặc nhiều lỗi xảy ra.

Một nhiệm vụ đã bị hủy bỏ.


3
Chúng tôi có thể nâng cấp vấn đề trên GitHub: HttpClient ném Task HủyedException khi hết thời gian # 20296
csrowell

Upvote lớn cho câu hỏi. Ngoài ra ... có ý tưởng nào để làm điều này trên UWP không? Windows.Web.HTTP.HTTPClient của nó không có thành viên hết thời gian. Ngoài ra, phương thức GetAsync không chấp nhận mã thông báo hủy ...
Do-do-new

1
6 năm sau, và dường như vẫn không thể biết liệu khách hàng có hết thời gian không.
Steve Smith

Câu trả lời:


61

Bạn cần chờ đợi GetAsyncphương pháp. Sau đó nó sẽ ném TaskCanceledExceptionnếu nó đã hết thời gian. Ngoài ra, GetStringAsyncGetStreamAsyncnội bộ xử lý thời gian chờ, vì vậy họ sẽ KHÔNG BAO GIỜ ném.

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
{ 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
};
try
{
    var s = await client.GetAsync();
}
catch(Exception e)
{
    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);
}

2
Tôi đã thử nghiệm điều này, và GetStreamAsyncném TaskCanceledExceptioncho tôi.
Sam

38
Làm thế nào tôi có thể biết nếu TaskCanceledExceptionđược gây ra bởi thời gian chờ HTTP và không, nói hủy bỏ trực tiếp hoặc lý do khác?
UserControl

8
@UserControl kiểm tra TaskCanceledException.CancellationToken.IsCancellationRequested. Nếu sai, bạn có thể chắc chắn rằng đó là một khoảng thời gian chờ.
Todd Menier

3
Hóa ra, bạn không thể tin vào IsCancellationRequestedviệc đặt mã thông báo ngoại lệ khi hủy trực tiếp như tôi nghĩ trước đây: stackoverflow.com/q/29319086/62600
Todd Menier

2
@testing Họ không cư xử khác nhau. Chỉ là bạn có một mã thông báo sẽ đại diện cho yêu cầu hủy của người dùng và nội bộ (bạn không thể truy cập và bạn không cần) đại diện cho thời gian chờ của khách hàng. Đó là trường hợp sử dụng khác nhau
Ngài Rufo

59

Tôi đang tái tạo vấn đề tương tự và nó thực sự gây phiền nhiễu. Tôi đã tìm thấy những điều hữu ích:

HttpClient - xử lý các trường hợp ngoại lệ tổng hợp

Lỗi trong httpClient.GetAsync sẽ ném WebException, không phải là Hủy bỏ nhiệm vụ

Một số mã trong trường hợp các liên kết không đi đến đâu:

var c = new HttpClient();
c.Timeout = TimeSpan.FromMilliseconds(10);
var cts = new CancellationTokenSource();
try
{
    var x = await c.GetAsync("http://linqpad.net", cts.Token);  
}
catch(WebException ex)
{
    // handle web exception
}
catch(TaskCanceledException ex)
{
    if(ex.CancellationToken == cts.Token)
    {
        // a real cancellation, triggered by the caller
    }
    else
    {
        // a web request timeout (possibly other things!?)
    }
}

Theo kinh nghiệm của tôi, WebException không thể bị bắt trong bất kỳ trường hợp nào. Là những người khác trải nghiệm một cái gì đó khác nhau?
đè bẹp

1
@crush WebException thể bị bắt. Có lẽ điều này sẽ giúp.
DavidRR

Điều này không hiệu quả với tôi nếu tôi không sử dụng cts. Tôi chỉ đang sử dụng Tác vụ <T> task = someTask () thử {T result = task.Result} Catch (Task HủyedException) {} Catch (Exception e) {} Chỉ có ngoại lệ chung được bắt, không phải là Nhiệm vụ bị loại bỏ. Có gì sai trong phiên bản mã của tôi?
Naomi

1
Tôi đã tạo một báo cáo lỗi mới kể từ khi báo cáo ban đầu xuất hiện trong một bài đăng trên diễn đàn được lưu trữ: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWar Warrior

1
Nếu mã thông báo được truyền từ bên ngoài, hãy kiểm tra nó default(CancellationToken)trước khi so sánh với ex.CancellationToken.
SerG

25

Tôi thấy rằng cách tốt nhất để xác định xem cuộc gọi dịch vụ đã hết hạn hay chưa là sử dụng mã thông báo hủy chứ không phải thuộc tính hết thời gian chờ của httpClient:

var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);

Và sau đó xử lý Hủy bỏ ngoại lệ trong cuộc gọi dịch vụ ...

catch(TaskCanceledException)
{
    if(!cts.Token.IsCancellationRequested)
    {
        // Timed Out
    }
    else
    {
        // Cancelled for some other reason
    }
}

Tất nhiên, nếu thời gian chờ xảy ra ở phía dịch vụ, điều đó sẽ có thể được xử lý bởi WebException.


1
Hmm, tôi đoán rằng toán tử phủ định (đã được thêm vào trong một chỉnh sửa) nên được loại bỏ cho mẫu này để có ý nghĩa? Nếu cts.Token.IsCancellationRequestedtruenó phải có nghĩa là một thời gian chờ đã xảy ra?
Lasse Christiansen

9

Từ http://msdn.microsoft.com/en-us/l Library / system.net.http.httpclient.timeout.aspx

Truy vấn hệ thống tên miền (DNS) có thể mất tới 15 giây để quay lại hoặc hết thời gian. Nếu yêu cầu của bạn chứa tên máy chủ yêu cầu độ phân giải và bạn đặt Thời gian chờ thành giá trị dưới 15 giây, có thể mất 15 giây trở lên trước khi ném WebException để biểu thị thời gian chờ theo yêu cầu của bạn .

Sau đó, bạn có quyền truy cập vào Statustài sản, xem WebExceptionStatus


3
Hừm, tôi đang trở lại AggregateExceptionvới một TaskCancelledExceptionbên trong. Tôi phải làm gì đó sai ...
Stewol

Bạn đang sử dụng catch(WebException e)?
user247702

Không, và nếu tôi cố gắng, AggregateExceptionthì không bị cản trở. Nếu bạn tạo một dự án bảng điều khiển VS, hãy thêm một tham chiếu System.Net.Httpvà thả mã vào main, bạn có thể tự mình xem (nếu bạn muốn).
Stewol

5
Nếu thời gian chờ vượt quá thời gian chờ của nhiệm vụ, bạn sẽ nhận được một TaskCanceledException. Điều này dường như bị ném bởi việc xử lý thời gian chờ nội bộ của TPL, ở mức cao hơn so với HttpWebClient. Có dường như không phải là một cách tốt để phân biệt giữa một hủy timeout và hủy dùng. Kết quả cuối cùng là bạn có thể không nhận được WebExceptiontrong vòng của bạn AggregateException.
JT.

1
Như những người khác đã nói, bạn phải cho rằng Task HủyedException là thời gian chờ. Tôi đang sử dụng try {// Mã here} catch (AggregateException ngoại lệ) {if {// timeout Xử lý here} (exception.InnerExceptions.OfType <TaskCanceledException> () Bất kỳ ().)}
Vdex

8

Về cơ bản, bạn cần nắm bắt OperationCanceledExceptionvà kiểm tra trạng thái của mã thông báo hủy được chuyển đến SendAsync(hoặc GetAsync, hoặc bất kỳ HttpClientphương thức nào bạn đang sử dụng):

  • nếu nó bị hủy ( IsCancellationRequestedlà đúng), điều đó có nghĩa là yêu cầu thực sự đã bị hủy
  • nếu không, nó có nghĩa là yêu cầu đã hết thời gian

Tất nhiên, điều này không thuận tiện lắm ... sẽ tốt hơn nếu nhận được TimeoutExceptiontrong trường hợp hết thời gian. Tôi đề xuất một giải pháp ở đây dựa trên trình xử lý thông báo HTTP tùy chỉnh: Xử lý thời gian chờ tốt hơn với HttpClient


Ah! Đó là bạn! Tôi đã viết một bình luận trong blogpost của bạn đầu ngày hôm nay. Nhưng viết câu trả lời này, tôi nghĩ rằng quan điểm của bạn về IsCancnameRequested là không đúng, bởi vì dường như nó luôn đúng với tôi, khi tôi không tự hủy nó
knocte

@knocte thật kỳ lạ ... Nhưng trong trường hợp đó, giải pháp từ bài đăng trên blog của tôi sẽ không giúp ích gì cho bạn, vì nó cũng dựa vào điều này
Thomas Levesque

1
trong vấn đề github về vấn đề này, nhiều người khẳng định những gì tôi đã nói: rằng IsCancnameRequested là đúng khi có thời gian chờ; vì vậy tôi rất muốn hạ thấp câu trả lời của bạn;)
knocte

@knocte, tôi không biết phải nói gì với bạn ... Tôi đã sử dụng nó trong một thời gian dài và nó luôn hoạt động với tôi. Bạn đã thiết lập HttpClient.Timeoutđến vô cùng?
Thomas Levesque

không, tôi đã không làm vì tôi không thể tự mình kiểm soát httpClient, đó là thư viện của bên thứ ba mà tôi đang sử dụng, đó là thư viện sử dụng nó
knocte

-1
_httpClient = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(5)};

là những gì tôi thường làm, dường như làm việc khá tốt đối với tôi, nó đặc biệt tốt khi sử dụng proxy.


1
Đây là cách bạn thiết lập thời gian chờ của httpclient. Điều này không giải quyết được câu hỏi, đó là cách bạn nói khi httpclient đã hết thời gian.
Ethan Fischer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.