From 3a95ea16bfd464dee520b09d9ffeaca62a62304d Mon Sep 17 00:00:00 2001 From: Yaakov Date: Tue, 25 Feb 2020 08:41:18 +1100 Subject: [PATCH] Add StatusCode to HttpRequestException (#32455) Adds HttpRequestException.StatusCode property, to be set by EnsureSuccessStatusCode and convenience methods when an exception is generated by a status code. --- .../System.Net.Http/ref/System.Net.Http.cs | 4 ++- .../src/System/Net/Http/HttpRequestException.cs | 20 +++++++++++ .../src/System/Net/Http/HttpResponseMessage.cs | 13 ++++--- .../tests/FunctionalTests/HttpClientTest.cs | 13 +++++-- .../FunctionalTests/HttpResponseMessageTest.cs | 6 ++-- .../tests/UnitTests/HttpRequestExceptionTests.cs | 40 ++++++++++++++++++++++ .../UnitTests/System.Net.Http.Unit.Tests.csproj | 1 + 7 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs diff --git a/src/libraries/System.Net.Http/ref/System.Net.Http.cs b/src/libraries/System.Net.Http/ref/System.Net.Http.cs index ebbc2c0..b1c02bb 100644 --- a/src/libraries/System.Net.Http/ref/System.Net.Http.cs +++ b/src/libraries/System.Net.Http/ref/System.Net.Http.cs @@ -128,9 +128,9 @@ namespace System.Net.Http protected HttpContent() { } public System.Net.Http.Headers.HttpContentHeaders Headers { get { throw null; } } public System.Threading.Tasks.Task CopyToAsync(System.IO.Stream stream) { throw null; } - public System.Threading.Tasks.Task CopyToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) { throw null; } public System.Threading.Tasks.Task CopyToAsync(System.IO.Stream stream, System.Net.TransportContext context) { throw null; } public System.Threading.Tasks.Task CopyToAsync(System.IO.Stream stream, System.Net.TransportContext context, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task CopyToAsync(System.IO.Stream stream, System.Threading.CancellationToken cancellationToken) { throw null; } protected virtual System.Threading.Tasks.Task CreateContentReadStreamAsync() { throw null; } protected virtual System.Threading.Tasks.Task CreateContentReadStreamAsync(System.Threading.CancellationToken cancellationToken) { throw null; } public void Dispose() { } @@ -186,6 +186,8 @@ namespace System.Net.Http public HttpRequestException() { } public HttpRequestException(string message) { } public HttpRequestException(string message, System.Exception inner) { } + public HttpRequestException(string message, System.Exception inner, System.Net.HttpStatusCode? statusCode) { } + public System.Net.HttpStatusCode? StatusCode { get { throw null; } } } public partial class HttpRequestMessage : System.IDisposable { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs index 68662c0..da0bdf6 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs @@ -28,6 +28,26 @@ namespace System.Net.Http } } + /// + /// Initializes a new instance of the class with a specific message that describes the current exception, an inner exception, and an HTTP status code. + /// + /// A message that describes the current exception. + /// The inner exception. + /// The HTTP status code. + public HttpRequestException(string message, Exception inner, HttpStatusCode? statusCode) + : this(message, inner) + { + StatusCode = statusCode; + } + + /// + /// Gets the HTTP status code to be returned with the exception. + /// + /// + /// An HTTP status code if the exception represents a non-successful result, otherwise null. + /// + public HttpStatusCode? StatusCode { get; } + // This constructor is used internally to indicate that a request was not successfully sent due to an IOException, // and the exception occurred early enough so that the request may be retried on another connection. internal HttpRequestException(string message, Exception inner, RequestRetryType allowRetry) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs index 0b2e4ae..d52f14a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs @@ -169,11 +169,14 @@ namespace System.Net.Http { if (!IsSuccessStatusCode) { - throw new HttpRequestException(SR.Format( - System.Globalization.CultureInfo.InvariantCulture, - SR.net_http_message_not_success_statuscode, - (int)_statusCode, - ReasonPhrase)); + throw new HttpRequestException( + SR.Format( + System.Globalization.CultureInfo.InvariantCulture, + SR.net_http_message_not_success_statuscode, + (int)_statusCode, + ReasonPhrase), + inner: null, + _statusCode); } return this; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs index ad642fc..ef0257e 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs @@ -192,9 +192,16 @@ namespace System.Net.Http.Functional.Tests Content = withResponseContent ? new ByteArrayContent(new byte[1]) : null })))) { - await Assert.ThrowsAsync(() => client.GetStringAsync(CreateFakeUri())); - await Assert.ThrowsAsync(() => client.GetByteArrayAsync(CreateFakeUri())); - await Assert.ThrowsAsync(() => client.GetStreamAsync(CreateFakeUri())); + HttpRequestException ex; + + ex = await Assert.ThrowsAsync(() => client.GetStringAsync(CreateFakeUri())); + Assert.Equal(HttpStatusCode.BadRequest, ex.StatusCode); + + ex = await Assert.ThrowsAsync(() => client.GetByteArrayAsync(CreateFakeUri())); + Assert.Equal(HttpStatusCode.BadRequest, ex.StatusCode); + + ex = await Assert.ThrowsAsync(() => client.GetStreamAsync(CreateFakeUri())); + Assert.Equal(HttpStatusCode.BadRequest, ex.StatusCode); } } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpResponseMessageTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpResponseMessageTest.cs index 549a33c..fdeeeb0 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpResponseMessageTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpResponseMessageTest.cs @@ -105,12 +105,14 @@ namespace System.Net.Http.Functional.Tests { using (var m = new HttpResponseMessage(HttpStatusCode.MultipleChoices)) { - Assert.Throws(() => m.EnsureSuccessStatusCode()); + var ex = Assert.Throws(() => m.EnsureSuccessStatusCode()); + Assert.Equal(HttpStatusCode.MultipleChoices, ex.StatusCode); } using (var m = new HttpResponseMessage(HttpStatusCode.BadGateway)) { - Assert.Throws(() => m.EnsureSuccessStatusCode()); + var ex = Assert.Throws(() => m.EnsureSuccessStatusCode()); + Assert.Equal(HttpStatusCode.BadGateway, ex.StatusCode); } using (var response = new HttpResponseMessage(HttpStatusCode.OK)) diff --git a/src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs b/src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs new file mode 100644 index 0000000..4500e51 --- /dev/null +++ b/src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Xunit; + +namespace System.Net.Http.Tests +{ + public class HttpRequestExceptionTests + { + [Fact] + public void DefaultConstructors_HasNoStatusCode() + { + var exception = new HttpRequestException(); + Assert.Null(exception.StatusCode); + + exception = new HttpRequestException("message"); + Assert.Null(exception.StatusCode); + + exception = new HttpRequestException("message", new InvalidOperationException()); + Assert.Null(exception.StatusCode); + } + + [Fact] + public void StoresStatusCode() + { + var exception = new HttpRequestException("message", null, HttpStatusCode.InternalServerError); + Assert.Equal(HttpStatusCode.InternalServerError, exception.StatusCode); + } + + [Fact] + public void StoresNonStandardStatusCode() + { + var statusCode = (HttpStatusCode)999; + + var exception = new HttpRequestException("message", null, statusCode); + Assert.Equal(statusCode, exception.StatusCode); + } + } +} diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 29595a2..fff618f 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -415,6 +415,7 @@ + -- 2.7.4