ฉันจะบอกได้อย่างไรเมื่อ HttpClient หมดเวลา


142

เท่าที่ฉันสามารถบอกได้ว่าไม่มีวิธีใดที่จะรู้ได้ว่าเป็นการหมดเวลาเฉพาะที่เกิดขึ้น ฉันไม่ได้มองถูกที่หรือว่าฉันพลาดบางอย่างที่ใหญ่กว่า?

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);
}

ผลตอบแทนนี้:

เกิดข้อผิดพลาดอย่างน้อยหนึ่งรายการ

งานถูกยกเลิก


3
เราสามารถโหวตปัญหาใน GitHub: HttpClient พ่น TaskCanceledException เมื่อหมดเวลา # 20296
csrowell

upvote มากสำหรับคำถาม ยัง ... มีความคิดวิธีการทำเช่นนี้ใน UWP? Windows.Web.HTTP.HTTPClient มันไม่มีสมาชิกหมดเวลา ด้วยวิธี GetAsync ไม่ยอมรับโทเค็นการยกเลิก ...
Do-do-new

1
6 ปีต่อมาและยังไม่สามารถทราบได้ว่าลูกค้าหมดเวลาหรือไม่
สตีฟสมิ ธ

คำตอบ:


61

คุณต้องรอGetAsyncวิธีการ จากนั้นจะโยน a TaskCanceledExceptionถ้ามันหมดเวลา นอกจากนี้GetStringAsyncและGetStreamAsyncจัดการการหมดเวลาภายในดังนั้นพวกเขาจะไม่โยน

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
ฉันทดสอบสิ่งนี้และGetStreamAsyncโยนTaskCanceledExceptionให้ฉัน
Sam

38
ฉันจะทราบได้อย่างไรว่าTaskCanceledExceptionเกิดจากการหมดเวลาของ HTTP ไม่ใช่การยกเลิกโดยตรงหรือด้วยเหตุผลอื่น
UserControl

8
@ ตรวจสอบผู้TaskCanceledException.CancellationToken.IsCancellationRequestedใช้งาน หากเป็นเท็จคุณสามารถมั่นใจได้ว่าเป็นการหมดเวลา
Todd Menier

3
ตามที่ปรากฎคุณไม่สามารถนับได้ว่าIsCancellationRequestedได้รับโทเค็นข้อยกเว้นของการยกเลิกโดยตรงอย่างที่ฉันคิดไว้ก่อนหน้านี้: stackoverflow.com/q/29319086/62600
Todd Menier

2
@ การทดสอบพวกเขาไม่ทำงานแตกต่างกัน เป็นเพียงว่าคุณมีโทเค็นเดียวที่จะแสดงถึงคำขอยกเลิกผู้ใช้และภายใน (คุณไม่สามารถเข้าถึงและคุณไม่ต้องการ) ที่แสดงถึงการหมดเวลาของไคลเอ็นต์ มันเป็นกรณีการใช้งานที่แตกต่าง
Sir Rufo

59

ฉันกำลังทำซ้ำปัญหาเดียวกันและมันน่ารำคาญจริงๆ ฉันพบว่ามีประโยชน์เหล่านี้:

HttpClient - การจัดการกับข้อยกเว้นรวม

ข้อผิดพลาดใน HttpClient.GetAsync ควรโยน WebException ไม่ใช่ TaskCanceledException

โค้ดบางส่วนในกรณีที่ลิงก์ไปที่อื่น:

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!?)
    }
}

จากประสบการณ์ของฉัน WebException ไม่สามารถถูกจับได้ในทุกสถานการณ์ คนอื่นประสบบางสิ่งที่แตกต่างออกไปหรือไม่?
บดขยี้

1
@ crush WebException สามารถถูกจับได้ บางทีนี่อาจช่วยได้
DavidRR

สิ่งนี้ไม่ได้ผลสำหรับฉันหากฉันไม่ได้ใช้ cts ฉันเพิ่งใช้งาน <T> task = SomeTask () ลอง {T result = task.Result} catch (TaskCanceledException) {} catch (Exception e) {} เฉพาะข้อยกเว้นทั่วไปเท่านั้นไม่ใช่ TaskCanceledException มีอะไรผิดปกติในรหัสรุ่นของฉัน
นาโอมิ

1
ฉันสร้างรายงานข้อผิดพลาดใหม่เนื่องจากฉบับดั้งเดิมดูเหมือนจะอยู่ในโพสต์ในฟอรั่มที่เก็บถาวร: connect.microsoft.com/VisualStudio/feedback/details/3141135
StriplingWarrior

1
ถ้าโทเค็นจะถูกส่งผ่านจากการตรวจสอบนอกก็ไม่ได้ก่อนที่จะเปรียบเทียบกับdefault(CancellationToken) ex.CancellationToken
SerG

25

ฉันพบว่าวิธีที่ดีที่สุดในการตรวจสอบว่าบริการโทรหมดเวลาคือการใช้โทเค็นการยกเลิกและไม่ใช่คุณสมบัติการหมดเวลาของ HttpClient:

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

และจัดการกับ CancellationException ระหว่างการเรียกใช้บริการ ...

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

แน่นอนหากการหมดเวลาเกิดขึ้นที่ด้านการบริการของสิ่งต่าง ๆ นั่นควรจะสามารถจัดการได้โดย WebException


1
อืมฉันเดาว่าควรลบตัวดำเนินการปฏิเสธ (ซึ่งเพิ่มในการแก้ไข) สำหรับตัวอย่างนี้เพื่อให้เหมาะสมหรือไม่ ถ้าcts.Token.IsCancellationRequestedเป็นtrueก็ต้องหมายความว่าหมดเวลาเกิดขึ้น?
Lasse Christiansen

9

จากhttp://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout.aspx

แบบสอบถามระบบชื่อโดเมน (DNS) อาจใช้เวลาสูงสุด 15 วินาทีในการส่งคืนหรือหมดเวลา หากคำขอของคุณมีชื่อโฮสต์ที่ต้องใช้ความละเอียดและคุณตั้งค่าหมดเวลาให้มีค่าน้อยกว่า 15 วินาทีมันอาจใช้เวลา 15 วินาทีหรือมากกว่านั้นก่อนWebException ถูกโยนเพื่อบ่งชี้หมดเวลาตามคำขอของคุณ

จากนั้นคุณสามารถเข้าถึงStatusคุณสมบัติดูWebExceptionStatus


3
หืมมมฉันกลับมาAggregateExceptionพร้อมกับTaskCancelledExceptionข้างใน ผมจะต้องทำอะไรผิดพลาด ...
Benjol

คุณกำลังใช้catch(WebException e)?
user247702

ไม่และถ้าฉันลองAggregateExceptionมันจะจัดการไม่ได้ หากคุณสร้างโปรเจ็กต์ VS console ให้เพิ่มการอ้างอิงSystem.Net.Httpและวางโค้ดลงไปmainคุณสามารถเห็นตัวคุณเอง (ถ้าคุณต้องการ)
Benjol

5
TaskCanceledExceptionหากระยะเวลาการรอคอยเกินกว่าระยะเวลาหมดเวลางานของคุณจะได้รับ นี้ดูเหมือนว่าจะถูกโยนมาจากการหมดเวลาภายใน TPL HttpWebClientของในระดับที่สูงกว่า มีไม่ได้ดูเหมือนจะเป็นวิธีที่ดีที่จะแยกแยะระหว่างการยกเลิกหมดเวลาและการยกเลิกผู้ใช้ ผลที่สุดของที่นี่คือที่คุณอาจไม่ได้รับภายในของคุณWebException AggregateException
JT

1
อย่างที่คนอื่น ๆ พูดคุณต้องสมมติว่า TaskCanceledException หมดเวลา ฉันใช้ลอง {// Code here} catch (AggregateException exception) {if (exception.InnerExceptions.OfType <TaskCanceledException> () .Any ()) {// จัดการการหมดเวลาที่นี่}}
Vdex

8

โดยทั่วไปคุณจะต้องOperationCanceledExceptionตรวจจับและตรวจสอบสถานะของโทเค็นการยกเลิกที่ส่งผ่านไปยังSendAsync(หรือวิธีการGetAsyncใดก็ตามที่HttpClientคุณใช้):

  • ถ้ามันถูกยกเลิก ( IsCancellationRequestedเป็นจริง) ก็หมายความว่าคำขอนั้นถูกยกเลิกจริง ๆ
  • หากไม่แสดงว่าคำขอนั้นหมดเวลา

แน่นอนว่ามันไม่สะดวกมาก ... มันจะดีกว่าถ้าได้รับTimeoutExceptionในกรณีที่หมดเวลา ฉันเสนอวิธีแก้ปัญหาที่นี่ตามตัวจัดการข้อความ HTTP แบบกำหนดเอง: จัดการการหมดเวลาได้ดีขึ้นด้วย HttpClient


อา! เป็นคุณนั้นเอง! ฉันเขียนความคิดเห็นในบล็อกของคุณก่อนหน้านี้วันนี้ แต่ให้คำตอบนี้ฉันคิดว่าประเด็นของคุณเกี่ยวกับ IsCancellationRequested นั้นไม่เป็นความจริงเพราะดูเหมือนว่ามันเป็นเรื่องจริงสำหรับฉันเสมอเมื่อฉันไม่ได้ยกเลิกด้วยตัวเอง
knocte

@ Knocte มันแปลก ... แต่ในกรณีนั้นการแก้ปัญหาจากโพสต์บล็อกของฉันจะไม่ช่วยคุณได้เพราะมันต้องอาศัยสิ่งนี้
Thomas Levesque

1
ในปัญหา GitHub เกี่ยวกับเรื่องนี้หลายคนอ้างว่าฉันพูดว่า: IsCancellationRequested เป็นจริงเมื่อมีการหมดเวลา; ดังนั้นฉันจึงถูกล่อลวงให้ลดคำตอบของคุณ;)
39819

@ Knocte ฉันไม่รู้จะบอกอะไรคุณ ... ฉันใช้มันมานานแล้วและมันก็ใช้ได้ผลสำหรับฉันเสมอ คุณตั้งค่าเป็นHttpClient.Timeoutอินฟินิตี้หรือไม่?
โทมัสเลวิส

ไม่ฉันไม่ได้เพราะฉันไม่สามารถควบคุม HttpClient ได้ด้วยตัวเองมันเป็นห้องสมุดบุคคลที่สามที่ฉันใช้อยู่ห้องสมุดที่ใช้มัน
knocte

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

เป็นสิ่งที่ฉันมักจะทำดูเหมือนว่าจะทำงานออกมาได้ดีสำหรับฉันมันดีโดยเฉพาะอย่างยิ่งเมื่อใช้พร็อกซี่


1
นี่คือวิธีที่คุณตั้งค่าการหมดเวลาของ httpclient นี้ไม่ได้ตอบคำถามซึ่งเป็นวิธีที่คุณบอกเมื่อ httpclient หมดเวลา
Ethan Fischer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.