Add StatusCode to HttpRequestException (#32455)
authorYaakov <yaakov-h@users.noreply.github.com>
Mon, 24 Feb 2020 21:41:18 +0000 (08:41 +1100)
committerGitHub <noreply@github.com>
Mon, 24 Feb 2020 21:41:18 +0000 (13:41 -0800)
Adds HttpRequestException.StatusCode property, to be set by EnsureSuccessStatusCode and convenience methods when an exception is generated by a status code.

src/libraries/System.Net.Http/ref/System.Net.Http.cs
src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestException.cs
src/libraries/System.Net.Http/src/System/Net/Http/HttpResponseMessage.cs
src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientTest.cs
src/libraries/System.Net.Http/tests/FunctionalTests/HttpResponseMessageTest.cs
src/libraries/System.Net.Http/tests/UnitTests/HttpRequestExceptionTests.cs [new file with mode: 0644]
src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj

index ebbc2c0..b1c02bb 100644 (file)
@@ -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<System.IO.Stream> CreateContentReadStreamAsync() { throw null; }
         protected virtual System.Threading.Tasks.Task<System.IO.Stream> 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
     {
index 68662c0..da0bdf6 100644 (file)
@@ -28,6 +28,26 @@ namespace System.Net.Http
             }
         }
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="HttpRequestException" /> class with a specific message that describes the current exception, an inner exception, and an HTTP status code.
+        /// </summary>
+        /// <param name="message">A message that describes the current exception.</param>
+        /// <param name="inner">The inner exception.</param>
+        /// <param name="statusCode">The HTTP status code.</param>
+        public HttpRequestException(string message, Exception inner, HttpStatusCode? statusCode)
+            : this(message, inner)
+        {
+            StatusCode = statusCode;
+        }
+
+        /// <summary>
+        /// Gets the HTTP status code to be returned with the exception.
+        /// </summary>
+        /// <value>
+        /// An HTTP status code if the exception represents a non-successful result, otherwise <c>null</c>.
+        /// </value>
+        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)
index 0b2e4ae..d52f14a 100644 (file)
@@ -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;
index ad642fc..ef0257e 100644 (file)
@@ -192,9 +192,16 @@ namespace System.Net.Http.Functional.Tests
                     Content = withResponseContent ? new ByteArrayContent(new byte[1]) : null
                 }))))
             {
-                await Assert.ThrowsAsync<HttpRequestException>(() => client.GetStringAsync(CreateFakeUri()));
-                await Assert.ThrowsAsync<HttpRequestException>(() => client.GetByteArrayAsync(CreateFakeUri()));
-                await Assert.ThrowsAsync<HttpRequestException>(() => client.GetStreamAsync(CreateFakeUri()));
+                HttpRequestException ex;
+
+                ex = await Assert.ThrowsAsync<HttpRequestException>(() => client.GetStringAsync(CreateFakeUri()));
+                Assert.Equal(HttpStatusCode.BadRequest, ex.StatusCode);
+
+                ex = await Assert.ThrowsAsync<HttpRequestException>(() => client.GetByteArrayAsync(CreateFakeUri()));
+                Assert.Equal(HttpStatusCode.BadRequest, ex.StatusCode);
+
+                ex = await Assert.ThrowsAsync<HttpRequestException>(() => client.GetStreamAsync(CreateFakeUri()));
+                Assert.Equal(HttpStatusCode.BadRequest, ex.StatusCode);
             }
         }
 
index 549a33c..fdeeeb0 100644 (file)
@@ -105,12 +105,14 @@ namespace System.Net.Http.Functional.Tests
         {
             using (var m = new HttpResponseMessage(HttpStatusCode.MultipleChoices))
             {
-                Assert.Throws<HttpRequestException>(() => m.EnsureSuccessStatusCode());
+                var ex = Assert.Throws<HttpRequestException>(() => m.EnsureSuccessStatusCode());
+                Assert.Equal(HttpStatusCode.MultipleChoices, ex.StatusCode);
             }
 
             using (var m = new HttpResponseMessage(HttpStatusCode.BadGateway))
             {
-                Assert.Throws<HttpRequestException>(() => m.EnsureSuccessStatusCode());
+                var ex = Assert.Throws<HttpRequestException>(() => 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 (file)
index 0000000..4500e51
--- /dev/null
@@ -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);
+        }
+    }
+}
index 29595a2..fff618f 100644 (file)
     <Compile Include="MockContent.cs" />
     <Compile Include="StreamToStreamCopyTest.cs" />
     <Compile Include="HttpEnvironmentProxyTest.cs" />
+    <Compile Include="HttpRequestExceptionTests.cs" />
     <Compile Include="HttpWindowsProxyTest.cs" />
     <Compile Include="SystemProxyInfoTest.cs" />
     <Compile Include="..\..\src\System\Net\Http\SocketsHttpHandler\HttpNoProxy.cs">