diff --git a/src/Components/src/Microsoft.AspNetCore.Components/HttpClientJsonExtensions.cs b/src/Components/src/Microsoft.AspNetCore.Components/HttpClientJsonExtensions.cs index aa376f6c01..7ef07497a7 100644 --- a/src/Components/src/Microsoft.AspNetCore.Components/HttpClientJsonExtensions.cs +++ b/src/Components/src/Microsoft.AspNetCore.Components/HttpClientJsonExtensions.cs @@ -101,6 +101,10 @@ namespace Microsoft.AspNetCore.Components Content = new StringContent(requestJson, Encoding.UTF8, "application/json") }); + // Make sure the call was successful before we + // attempt to process the response content + response.EnsureSuccessStatusCode(); + if (typeof(T) == typeof(IgnoreResponse)) { return default; diff --git a/src/Components/test/Microsoft.AspNetCore.Components.Test/HttpClientJsonExtensionsTest.cs b/src/Components/test/Microsoft.AspNetCore.Components.Test/HttpClientJsonExtensionsTest.cs new file mode 100644 index 0000000000..16494b795b --- /dev/null +++ b/src/Components/test/Microsoft.AspNetCore.Components.Test/HttpClientJsonExtensionsTest.cs @@ -0,0 +1,159 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.JSInterop; +using System; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Components.Test +{ + public class HttpClientJsonExtensionsTest + { + const string TestUri = "http://example.com/some/uri"; + + [Fact] + public async Task GetJson_Success() + { + // Arrange + var httpClient = new HttpClient(new TestHttpMessageHandler(req => + { + Assert.Equal(TestUri, req.RequestUri.AbsoluteUri); + return Task.FromResult(CreateJsonResponse(HttpStatusCode.OK, new Person + { + Name = "Abc", + Age = 123 + })); + })); + + // Act + var result = await httpClient.GetJsonAsync(TestUri); + + // Assert + Assert.Equal("Abc", result.Name); + Assert.Equal(123, result.Age); + } + + [Fact] + public async Task GetJson_Failure() + { + // Arrange + var httpClient = new HttpClient(new TestHttpMessageHandler(req => + { + Assert.Equal(TestUri, req.RequestUri.AbsoluteUri); + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound)); + })); + + // Act/Assert + var ex = await Assert.ThrowsAsync( + () => httpClient.GetJsonAsync(TestUri)); + Assert.Contains("404 (Not Found)", ex.Message); + } + + [Theory] + [InlineData("Put")] + [InlineData("Post")] + [InlineData("Patch")] + [InlineData("Delete")] + [InlineData("MyArtificialMethod")] + public async Task SendJson_Success(string httpMethodString) + { + var httpMethod = new HttpMethod(httpMethodString); + var requestContent = new { MyProp = true, OtherProp = "Hello" }; + + // Arrange + var httpClient = new HttpClient(new TestHttpMessageHandler(async req => + { + Assert.Equal(httpMethod, req.Method); + Assert.Equal(TestUri, req.RequestUri.AbsoluteUri); + Assert.Equal(Json.Serialize(requestContent), await ((StringContent)req.Content).ReadAsStringAsync()); + return CreateJsonResponse(HttpStatusCode.OK, new Person + { + Name = "Abc", + Age = 123 + }); + })); + + // Act + var result = await Send(httpClient, httpMethodString, requestContent); + + // Assert + Assert.Equal("Abc", result.Name); + Assert.Equal(123, result.Age); + } + + [Theory] + [InlineData("Put")] + [InlineData("Post")] + [InlineData("Patch")] + [InlineData("Delete")] + [InlineData("MyArtificialMethod")] + public async Task SendJson_Failure(string httpMethodString) + { + var httpMethod = new HttpMethod(httpMethodString); + var requestContent = new { MyProp = true, OtherProp = "Hello" }; + + // Arrange + var httpClient = new HttpClient(new TestHttpMessageHandler(async req => + { + Assert.Equal(httpMethod, req.Method); + Assert.Equal(TestUri, req.RequestUri.AbsoluteUri); + Assert.Equal(Json.Serialize(requestContent), await ((StringContent)req.Content).ReadAsStringAsync()); + return new HttpResponseMessage(HttpStatusCode.BadGateway); + })); + + // Act/Assert + var ex = await Assert.ThrowsAsync( + () => Send(httpClient, httpMethodString, requestContent)); + Assert.Contains("502 (Bad Gateway)", ex.Message); + } + + HttpResponseMessage CreateJsonResponse(HttpStatusCode statusCode, object content) + { + return new HttpResponseMessage(statusCode) + { + Content = new StringContent(Json.Serialize(content)) + }; + } + + Task Send(HttpClient httpClient, string httpMethodString, object requestContent) + { + // For methods with convenience overloads, show those overloads work + switch (httpMethodString) + { + case "post": + return httpClient.PostJsonAsync(TestUri, requestContent); + case "put": + return httpClient.PutJsonAsync(TestUri, requestContent); + default: + return httpClient.SendJsonAsync(new HttpMethod(httpMethodString), TestUri, requestContent); + } + } + + class Person + { + public string Name { get; set; } + public int Age { get; set; } + } + + class TestHttpMessageHandler : HttpMessageHandler + { + private readonly Func> _sendDelegate; + + public TestHttpMessageHandler(Func> sendDelegate) + { + _sendDelegate = sendDelegate; + } + + protected override void Dispose(bool disposing) + => base.Dispose(disposing); + + protected override Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) + => _sendDelegate(request); + } + } +}