Skip to content

Commit 8a9df6c

Browse files
HttpClientJsonExtensions better handling for failed requests (imported from Blazor PR 1660) (#4788)
1 parent 93127b3 commit 8a9df6c

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

src/Components/src/Microsoft.AspNetCore.Components/HttpClientJsonExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ public static async Task<T> SendJsonAsync<T>(this HttpClient httpClient, HttpMet
101101
Content = new StringContent(requestJson, Encoding.UTF8, "application/json")
102102
});
103103

104+
// Make sure the call was successful before we
105+
// attempt to process the response content
106+
response.EnsureSuccessStatusCode();
107+
104108
if (typeof(T) == typeof(IgnoreResponse))
105109
{
106110
return default;
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.JSInterop;
5+
using System;
6+
using System.Net;
7+
using System.Net.Http;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using Xunit;
11+
12+
namespace Microsoft.AspNetCore.Components.Test
13+
{
14+
public class HttpClientJsonExtensionsTest
15+
{
16+
const string TestUri = "http://example.com/some/uri";
17+
18+
[Fact]
19+
public async Task GetJson_Success()
20+
{
21+
// Arrange
22+
var httpClient = new HttpClient(new TestHttpMessageHandler(req =>
23+
{
24+
Assert.Equal(TestUri, req.RequestUri.AbsoluteUri);
25+
return Task.FromResult(CreateJsonResponse(HttpStatusCode.OK, new Person
26+
{
27+
Name = "Abc",
28+
Age = 123
29+
}));
30+
}));
31+
32+
// Act
33+
var result = await httpClient.GetJsonAsync<Person>(TestUri);
34+
35+
// Assert
36+
Assert.Equal("Abc", result.Name);
37+
Assert.Equal(123, result.Age);
38+
}
39+
40+
[Fact]
41+
public async Task GetJson_Failure()
42+
{
43+
// Arrange
44+
var httpClient = new HttpClient(new TestHttpMessageHandler(req =>
45+
{
46+
Assert.Equal(TestUri, req.RequestUri.AbsoluteUri);
47+
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
48+
}));
49+
50+
// Act/Assert
51+
var ex = await Assert.ThrowsAsync<HttpRequestException>(
52+
() => httpClient.GetJsonAsync<Person>(TestUri));
53+
Assert.Contains("404 (Not Found)", ex.Message);
54+
}
55+
56+
[Theory]
57+
[InlineData("Put")]
58+
[InlineData("Post")]
59+
[InlineData("Patch")]
60+
[InlineData("Delete")]
61+
[InlineData("MyArtificialMethod")]
62+
public async Task SendJson_Success(string httpMethodString)
63+
{
64+
var httpMethod = new HttpMethod(httpMethodString);
65+
var requestContent = new { MyProp = true, OtherProp = "Hello" };
66+
67+
// Arrange
68+
var httpClient = new HttpClient(new TestHttpMessageHandler(async req =>
69+
{
70+
Assert.Equal(httpMethod, req.Method);
71+
Assert.Equal(TestUri, req.RequestUri.AbsoluteUri);
72+
Assert.Equal(Json.Serialize(requestContent), await ((StringContent)req.Content).ReadAsStringAsync());
73+
return CreateJsonResponse(HttpStatusCode.OK, new Person
74+
{
75+
Name = "Abc",
76+
Age = 123
77+
});
78+
}));
79+
80+
// Act
81+
var result = await Send(httpClient, httpMethodString, requestContent);
82+
83+
// Assert
84+
Assert.Equal("Abc", result.Name);
85+
Assert.Equal(123, result.Age);
86+
}
87+
88+
[Theory]
89+
[InlineData("Put")]
90+
[InlineData("Post")]
91+
[InlineData("Patch")]
92+
[InlineData("Delete")]
93+
[InlineData("MyArtificialMethod")]
94+
public async Task SendJson_Failure(string httpMethodString)
95+
{
96+
var httpMethod = new HttpMethod(httpMethodString);
97+
var requestContent = new { MyProp = true, OtherProp = "Hello" };
98+
99+
// Arrange
100+
var httpClient = new HttpClient(new TestHttpMessageHandler(async req =>
101+
{
102+
Assert.Equal(httpMethod, req.Method);
103+
Assert.Equal(TestUri, req.RequestUri.AbsoluteUri);
104+
Assert.Equal(Json.Serialize(requestContent), await ((StringContent)req.Content).ReadAsStringAsync());
105+
return new HttpResponseMessage(HttpStatusCode.BadGateway);
106+
}));
107+
108+
// Act/Assert
109+
var ex = await Assert.ThrowsAsync<HttpRequestException>(
110+
() => Send(httpClient, httpMethodString, requestContent));
111+
Assert.Contains("502 (Bad Gateway)", ex.Message);
112+
}
113+
114+
HttpResponseMessage CreateJsonResponse(HttpStatusCode statusCode, object content)
115+
{
116+
return new HttpResponseMessage(statusCode)
117+
{
118+
Content = new StringContent(Json.Serialize(content))
119+
};
120+
}
121+
122+
Task<Person> Send(HttpClient httpClient, string httpMethodString, object requestContent)
123+
{
124+
// For methods with convenience overloads, show those overloads work
125+
switch (httpMethodString)
126+
{
127+
case "post":
128+
return httpClient.PostJsonAsync<Person>(TestUri, requestContent);
129+
case "put":
130+
return httpClient.PutJsonAsync<Person>(TestUri, requestContent);
131+
default:
132+
return httpClient.SendJsonAsync<Person>(new HttpMethod(httpMethodString), TestUri, requestContent);
133+
}
134+
}
135+
136+
class Person
137+
{
138+
public string Name { get; set; }
139+
public int Age { get; set; }
140+
}
141+
142+
class TestHttpMessageHandler : HttpMessageHandler
143+
{
144+
private readonly Func<HttpRequestMessage, Task<HttpResponseMessage>> _sendDelegate;
145+
146+
public TestHttpMessageHandler(Func<HttpRequestMessage, Task<HttpResponseMessage>> sendDelegate)
147+
{
148+
_sendDelegate = sendDelegate;
149+
}
150+
151+
protected override void Dispose(bool disposing)
152+
=> base.Dispose(disposing);
153+
154+
protected override Task<HttpResponseMessage> SendAsync(
155+
HttpRequestMessage request, CancellationToken cancellationToken)
156+
=> _sendDelegate(request);
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)