การใช้งาน SureSuccessStatusCode และการจัดการ HttpRequestException ที่พ่น


107

รูปแบบการใช้งานHttpResponseMessage.EnsureSuccessStatusCode()คืออะไร? มันพ้นเนื้อหาของข้อความและพ่นHttpRequestExceptionแต่ฉันไม่เห็นวิธีการที่จะจัดการกับมันโปรแกรมใด ๆ Exceptionที่แตกต่างกว่าทั่วไป ตัวอย่างเช่นไม่รวมถึงสิ่งHttpStatusCodeที่น่าจะเป็นประโยชน์

มีวิธีใดบ้างในการดึงข้อมูลเพิ่มเติมออกมา ใครสามารถแสดงรูปแบบการใช้งานที่เกี่ยวข้องของทั้งสองEnsureSuccessStatusCode()และ HttpRequestException

คำตอบ:


158

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

เมื่อคุณตัดสินใจว่าต้องการจัดการกรณีความล้มเหลวในลักษณะเฉพาะอย่าทำสิ่งต่อไปนี้

var response = await client.GetAsync(...);
try
{
    response.EnsureSuccessStatusCode();
    // Handle success
}
catch (HttpRequestException)
{
    // Handle failure
}

สิ่งนี้ทำให้เกิดข้อยกเว้นเพียงเพื่อจับมันทันทีซึ่งไม่สมเหตุสมผล IsSuccessStatusCodeทรัพย์สินของHttpResponseMessageมีเพื่อวัตถุประสงค์นี้ ทำสิ่งต่อไปนี้แทน

var response = await client.GetAsync(...);
if (response.IsSuccessStatusCode)
{
    // Handle success
}
else
{
    // Handle failure
}

1
มีวิธีใดบ้างในการรับรหัสสถานะจำนวนเต็มจริง เมื่อฉันลองสิ่งนี้ฉันได้รับสตริงเช่น "NotFound" แทนรหัสสถานะ 404
NickG

12
@NickG (int)response.StatusCode(ดูmsdn.microsoft.com/en-us/library/… )
Timothy Shields

1
หมายเหตุค่าเริ่มต้น HttpRequestException ที่เกิดจากการตรวจสอบให้แน่ใจ แต่คุณสามารถเข้าถึงคุณสมบัตินั้นได้ในการตอบสนองหากไม่สำเร็จ
Stefan Zvonar

@TimothyShields ฉันขอบคุณสีของคุณที่อนุญาตให้สร้างต้นแบบได้อย่างรวดเร็ว ทำไมคุณไม่อ่านresponse.Contentค่าก่อนที่จะกระโดดเข้าไปใน// Handle successและ// Handle failureบล็อก? ด้วยวิธีนี้คุณจะอ่านresponse.Contentคุณสมบัติเพียงครั้งเดียว ข้อเสียเพียงอย่างเดียวที่ฉันเห็นในการทำเช่นนี้คือหากคุณสมบัติเนื้อหาเป็นสตริงที่ยาวคุณจะทำให้ไคลเอนต์ช้าลงโดยทั่วไป แต่ถ้าคุณกังวลเรื่องความเร็วทำไมไม่ลองใช้response.EnsureSuccessStatusCode();ล่ะ
John Zabroski

ฉันเขียน SureSuccessStatusCode เวอร์ชันของฉันเองด้านล่าง stackoverflow.com/a/63476616/1040437 จะมอบหมายให้ผู้โทรรับผิดชอบContentก่อนที่จะตรวจสอบสถานะ
John Zabroski

97

ฉันไม่ชอบ SureSuccessStatusCode เพราะมันไม่ส่งคืนอะไรที่มีความหมาย นั่นคือเหตุผลที่ฉันสร้างส่วนขยายของตัวเอง:

public static class HttpResponseMessageExtensions
{
    public static async Task EnsureSuccessStatusCodeAsync(this HttpResponseMessage response)
    {
        if (response.IsSuccessStatusCode)
        {
            return;
        }

        var content = await response.Content.ReadAsStringAsync();

        if (response.Content != null)
            response.Content.Dispose();

        throw new SimpleHttpResponseException(response.StatusCode, content);
    }
}

public class SimpleHttpResponseException : Exception
{
    public HttpStatusCode StatusCode { get; private set; }

    public SimpleHttpResponseException(HttpStatusCode statusCode, string content) : base(content)
    {
        StatusCode = statusCode;
    }
}

ซอร์สโค้ดสำหรับรหัส SureSuccessStatusCode ของ Microsoft สามารถพบได้ที่นี่

เวอร์ชันซิงโครนัสตามลิงค์ SO :

public static void EnsureSuccessStatusCode(this HttpResponseMessage response)
{
    if (response.IsSuccessStatusCode)
    {
        return;
    }

    var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

    if (response.Content != null)
        response.Content.Dispose();

    throw new SimpleHttpResponseException(response.StatusCode, content);
}

สิ่งที่ฉันไม่ชอบเกี่ยวกับ IsSuccessStatusCode ก็คือมันไม่สามารถใช้ซ้ำได้ "อย่างดี" ตัวอย่างเช่นคุณสามารถใช้ไลบรารีเช่นpollyเพื่อทำคำขอซ้ำในกรณีที่มีปัญหาเครือข่าย ในกรณีนี้คุณต้องใช้รหัสของคุณเพื่อเพิ่มข้อยกเว้นเพื่อให้พอลลี่หรือไลบรารีอื่น ๆ สามารถจัดการได้ ...


1
เห็นด้วยรหัสเริ่มต้นไม่มีคุณสมบัติในการรับข้อความที่มีความหมายจากการส่งคืน
LT

2
EnsureSuccessStatusCodeรุ่นของคุณทำงานที่แตกต่างจากการดำเนินงานเดิมของ คุณมักจะทิ้งresponse.Content(เพราะสุดท้ายจะถูกเรียกเสมอแม้หลังจากreturn;คำสั่ง) และมันจะทำลายเนื้อหาเพื่อการอ่านต่อไป การใช้งานดั้งเดิมจะกำจัดเนื้อหาเฉพาะเมื่อรหัสสถานะไม่ได้ระบุว่าผลลัพธ์สำเร็จ
Lukas.Navratil

4
ฉันไม่เข้าใจว่าทำไมคุณถึงก่อนawait response.Content.ReadAsStringAsync()แล้วจึงตรวจสอบif (response.Content != null)
mafu

3
ตอนนี้พอลลี่จัดการผลลัพธ์ที่ส่งคืนและข้อยกเว้นอย่างแม่นยำเพื่อช่วยในสถานการณ์แบบนี้ คุณสามารถกำหนดค่า Polly เพื่อป้องกันการHttpRequestโทรและกำหนดค่านโยบายเพื่อจัดการกับข้อยกเว้นบางประการและบางHttpResponseCodeส่วน ดูตัวอย่างใน Polly readme ที่นี่
นักเดินทางบนภูเขา

2
จะresponse.Contentเป็นโมฆะได้อย่างไรเมื่อมีวิธีการเรียกใช้?
Ian Warburton

1

ฉันใช้ SureSuccessStatusCode เมื่อฉันไม่ต้องการจัดการกับ Exception ด้วยวิธีการเดียวกัน

public async Task DoSomethingAsync(User user)
{
    try
    {
        ...
        var userId = await GetUserIdAsync(user)
        ...
    }
    catch(Exception e)
    {
        throw;
    }
}

public async Task GetUserIdAsync(User user)
{
    using(var client = new HttpClient())
    {
        ...
        response = await client.PostAsync(_url, context);

        response.EnsureSuccesStatusCode();
        ...
    }
}

ข้อยกเว้นที่เกิดขึ้นใน GetUserIdAsync จะได้รับการจัดการบน DoSomethingAsync


1

ด้านล่างนี้คือโซลูชันที่ฉันเสนอ ข้อบกพร่องเพียงอย่างเดียวคือเนื่องจากตัวจัดการทรัพยากรกรอบงาน ASP.NET Core เป็นแบบภายในของกรอบงานฉันจึงไม่สามารถใช้สตริงข้อความสากลของ Microsoft ซ้ำได้โดยตรงดังนั้นฉันจึงใช้ข้อความภาษาอังกฤษแบบคำต่อคำที่นี่

ข้อดี

  • บันทึกเนื้อหาสำหรับข้อผิดพลาดของเซิร์ฟเวอร์ 5xx
    • บางครั้งข้อผิดพลาดของเซิร์ฟเวอร์เป็นข้อผิดพลาดของไคลเอ็นต์ในการปลอมตัวเช่นไคลเอนต์ที่ใช้ปลายทางที่เลิกใช้แล้วซึ่งในที่สุดก็ถูกปิด
  • ทำให้ง่ายต่อการเปิดเผยข้อผิดพลาดเมื่อเขียนการทดสอบการรวมโดยใช้ ConfigureTestContainer<T>

จุดด้อย

  • ไม่มีประสิทธิภาพ
    • หากคุณอ่านเนื้อหาตอบกลับและเนื้อหายาวมากคุณจะทำให้ลูกค้าทำงานช้าลง สำหรับลูกค้าบางรายที่มีข้อกำหนดในการตอบสนองแบบเรียลไทม์ที่นุ่มนวลการกระวนกระวายใจนี้อาจไม่สามารถยอมรับได้
  • ความรับผิดชอบที่ไม่ถูกต้องสำหรับการบันทึกและการตรวจสอบข้อผิดพลาด
    • หากนี่เป็นข้อผิดพลาดของเซิร์ฟเวอร์ 5xx เหตุใดไคลเอ็นต์จึงสนใจเนื่องจากไคลเอ็นต์ไม่ได้ทำอะไรผิด เพียงแค่โทรresponse.EnsureSuccessStatusCode();และปล่อยให้เซิร์ฟเวอร์จัดการกับมัน
    • ทำไมไม่ตรวจสอบบันทึกข้อผิดพลาดของเซิร์ฟเวอร์เมื่อมี Internal Server Error?
  • ต้องอ่านContentคุณสมบัติก่อนตรวจสอบสถานะ อาจมีสถานการณ์ที่ไม่พึงปรารถนาซึ่งหนึ่งในนั้นคือการขาดประสิทธิภาพ

การใช้งาน

using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, "controller/action"))
{
  using (var response = await HttpClient.SendAsync(requestMessage))
  {
    var content = await response.Content.ReadAsStringAsync();
    response.EnsureSuccessStatusCode2(content);
    var result = JsonConvert.DeserializeObject<ResponseClass>(content);
  }
}

API

    public static class HttpResponseMessageExtensions
    {
        public static void EnsureSuccessStatusCode2(this HttpResponseMessage message, string content = null)
        {
            if (message.IsSuccessStatusCode)
                return;
            var contentMessage = string.IsNullOrWhiteSpace(content) ? string.Empty : $"Content: {content}";
            throw new HttpRequestException(string.Format(
                System.Globalization.CultureInfo.InvariantCulture,
                "Response status code does not indicate success: {0} ({1}).{2}",
                (int)message.StatusCode,
                message.ReasonPhrase,
                contentMessage)
                );
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.