diff --git a/src/libraries/System.Net.Http.Json/ref/System.Net.Http.Json.cs b/src/libraries/System.Net.Http.Json/ref/System.Net.Http.Json.cs index 0010ea20d123af..4cd8fcd0569ac1 100644 --- a/src/libraries/System.Net.Http.Json/ref/System.Net.Http.Json.cs +++ b/src/libraries/System.Net.Http.Json/ref/System.Net.Http.Json.cs @@ -21,12 +21,16 @@ public static partial class HttpClientJsonExtensions public static System.Threading.Tasks.Task GetFromJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task GetFromJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task PostAsJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Threading.Tasks.Task PostAsJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task PostAsJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Threading.CancellationToken cancellationToken) { throw null; } public static System.Threading.Tasks.Task PostAsJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, TValue value, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Threading.Tasks.Task PostAsJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task PostAsJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, TValue value, System.Threading.CancellationToken cancellationToken) { throw null; } public static System.Threading.Tasks.Task PutAsJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Threading.Tasks.Task PutAsJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task PutAsJsonAsync(this System.Net.Http.HttpClient client, string? requestUri, TValue value, System.Threading.CancellationToken cancellationToken) { throw null; } public static System.Threading.Tasks.Task PutAsJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, TValue value, System.Text.Json.JsonSerializerOptions? options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public static System.Threading.Tasks.Task PutAsJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, TValue value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task PutAsJsonAsync(this System.Net.Http.HttpClient client, System.Uri? requestUri, TValue value, System.Threading.CancellationToken cancellationToken) { throw null; } } public static partial class HttpContentJsonExtensions diff --git a/src/libraries/System.Net.Http.Json/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Net.Http.Json/src/ILLink/ILLink.Suppressions.xml index 540a559a8ee756..accf0f2438a6b0 100644 --- a/src/libraries/System.Net.Http.Json/src/ILLink/ILLink.Suppressions.xml +++ b/src/libraries/System.Net.Http.Json/src/ILLink/ILLink.Suppressions.xml @@ -5,7 +5,7 @@ ILLink IL2072 member - M:System.Net.Http.Json.JsonContent.<SerializeToStreamAsyncCore>d__18.MoveNext + M:System.Net.Http.Json.JsonContent.<SerializeToStreamAsyncCore>d__13.MoveNext ILLink diff --git a/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj b/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj index e9accb07aa992f..a0b6916073f665 100644 --- a/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj +++ b/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj @@ -1,22 +1,24 @@ - + $(NetCoreAppCurrent);net5.0;netstandard2.0;net461 true enable + + + - + @@ -30,8 +32,7 @@ - + diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Post.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Post.cs index 86d91add941cb0..324fa61f76c7e1 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Post.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Post.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; +using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; @@ -36,5 +37,27 @@ public static Task PostAsJsonAsync(this HttpClient public static Task PostAsJsonAsync(this HttpClient client, Uri? requestUri, TValue value, CancellationToken cancellationToken) => client.PostAsJsonAsync(requestUri, value, options: null, cancellationToken); + + public static Task PostAsJsonAsync(this HttpClient client, string? requestUri, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) + { + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + JsonContent content = new(value, jsonTypeInfo); + return client.PostAsync(requestUri, content, cancellationToken); + } + + public static Task PostAsJsonAsync(this HttpClient client, Uri? requestUri, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) + { + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + JsonContent content = new(value, jsonTypeInfo); + return client.PostAsync(requestUri, content, cancellationToken); + } } } diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Put.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Put.cs index 0550254318a233..e04d6ec027f221 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Put.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpClientJsonExtensions.Put.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; +using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; @@ -36,5 +37,27 @@ public static Task PutAsJsonAsync(this HttpClient c public static Task PutAsJsonAsync(this HttpClient client, Uri? requestUri, TValue value, CancellationToken cancellationToken) => client.PutAsJsonAsync(requestUri, value, options: null, cancellationToken); + + public static Task PutAsJsonAsync(this HttpClient client, string? requestUri, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) + { + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + JsonContent content = new(value, jsonTypeInfo); + return client.PutAsync(requestUri, content, cancellationToken); + } + + public static Task PutAsJsonAsync(this HttpClient client, Uri? requestUri, TValue value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) + { + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + JsonContent content = new(value, jsonTypeInfo); + return client.PutAsync(requestUri, content, cancellationToken); + } } } diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpContentJsonExtensions.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpContentJsonExtensions.cs index ec0c7562776599..503b4eedf22994 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpContentJsonExtensions.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/HttpContentJsonExtensions.cs @@ -20,7 +20,7 @@ public static partial class HttpContentJsonExtensions throw new ArgumentNullException(nameof(content)); } - Encoding? sourceEncoding = JsonContent.GetEncoding(content.Headers.ContentType?.CharSet); + Encoding? sourceEncoding = JsonHelpers.GetEncoding(content.Headers.ContentType?.CharSet); return ReadFromJsonAsyncCore(content, type, sourceEncoding, options, cancellationToken); } @@ -32,7 +32,7 @@ public static partial class HttpContentJsonExtensions throw new ArgumentNullException(nameof(content)); } - Encoding? sourceEncoding = JsonContent.GetEncoding(content.Headers.ContentType?.CharSet); + Encoding? sourceEncoding = JsonHelpers.GetEncoding(content.Headers.ContentType?.CharSet); return ReadFromJsonAsyncCore(content, sourceEncoding, options, cancellationToken); } @@ -60,7 +60,7 @@ public static partial class HttpContentJsonExtensions throw new ArgumentNullException(nameof(content)); } - Encoding? sourceEncoding = JsonContent.GetEncoding(content.Headers.ContentType?.CharSet); + Encoding? sourceEncoding = JsonHelpers.GetEncoding(content.Headers.ContentType?.CharSet); return ReadFromJsonAsyncCore(content, type, sourceEncoding, context, cancellationToken); } @@ -72,7 +72,7 @@ public static partial class HttpContentJsonExtensions throw new ArgumentNullException(nameof(content)); } - Encoding? sourceEncoding = JsonContent.GetEncoding(content.Headers.ContentType?.CharSet); + Encoding? sourceEncoding = JsonHelpers.GetEncoding(content.Headers.ContentType?.CharSet); return ReadFromJsonAsyncCore(content, sourceEncoding, jsonTypeInfo, cancellationToken); } diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs index cdd1472cd4e42d..fdbae1b51cae33 100644 --- a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContent.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if !NETCOREAPP using System.Diagnostics; +#endif using System.IO; using System.Net.Http.Headers; using System.Text; @@ -13,12 +15,6 @@ namespace System.Net.Http.Json { public sealed partial class JsonContent : HttpContent { - internal const string JsonMediaType = "application/json"; - internal const string JsonType = "application"; - internal const string JsonSubtype = "json"; - private static MediaTypeHeaderValue DefaultMediaType - => new MediaTypeHeaderValue(JsonMediaType) { CharSet = "utf-8" }; - internal static readonly JsonSerializerOptions s_defaultSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); private readonly JsonSerializerOptions? _jsonSerializerOptions; @@ -39,7 +35,7 @@ private JsonContent(object? inputValue, Type inputType, MediaTypeHeaderValue? me Value = inputValue; ObjectType = inputType; - Headers.ContentType = mediaType ?? DefaultMediaType; + Headers.ContentType = mediaType ?? JsonHelpers.GetDefaultMediaType(); _jsonSerializerOptions = options ?? s_defaultSerializerOptions; } @@ -60,7 +56,7 @@ protected override bool TryComputeLength(out long length) private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, CancellationToken cancellationToken) { - Encoding? targetEncoding = GetEncoding(Headers.ContentType?.CharSet); + Encoding? targetEncoding = JsonHelpers.GetEncoding(Headers.ContentType?.CharSet); // Wrap provided stream into a transcoding stream that buffers the data transcoded from utf-8 to the targetEncoding. if (targetEncoding != null && targetEncoding != Encoding.UTF8) @@ -126,34 +122,5 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C } } } - - internal static Encoding? GetEncoding(string? charset) - { - Encoding? encoding = null; - - if (charset != null) - { - try - { - // Remove at most a single set of quotes. - if (charset.Length > 2 && charset[0] == '\"' && charset[charset.Length - 1] == '\"') - { - encoding = Encoding.GetEncoding(charset.Substring(1, charset.Length - 2)); - } - else - { - encoding = Encoding.GetEncoding(charset); - } - } - catch (ArgumentException e) - { - throw new InvalidOperationException(SR.CharSetInvalid, e); - } - - Debug.Assert(encoding != null); - } - - return encoding; - } } } diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.cs new file mode 100644 index 00000000000000..5f786f8607af16 --- /dev/null +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.cs @@ -0,0 +1,113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#if !NETCOREAPP +using System.Diagnostics; +#endif +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http.Json +{ + internal sealed partial class JsonContent : HttpContent + { + private readonly JsonTypeInfo _typeInfo; + + private readonly TValue _typedValue; + + public JsonContent(TValue inputValue, JsonTypeInfo jsonTypeInfo) + { + _typeInfo = jsonTypeInfo ?? throw new ArgumentNullException(nameof(jsonTypeInfo)); + _typedValue = inputValue; + Headers.ContentType = JsonHelpers.GetDefaultMediaType(); + } + + protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context) + => SerializeToStreamAsyncCore(stream, async: true, CancellationToken.None); + + protected override bool TryComputeLength(out long length) + { + length = 0; + return false; + } + + /// + /// Based on . + /// The difference is that this implementation calls overloads of that take type metadata directly. + /// This is done to avoid rooting unused, built-in s and reflection-based + /// warm-up logic (to reduce app size and be ILLinker-friendly), post ILLinker trimming. + /// + private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, CancellationToken cancellationToken) + { + Encoding? targetEncoding = JsonHelpers.GetEncoding(Headers.ContentType?.CharSet); + + // Wrap provided stream into a transcoding stream that buffers the data transcoded from utf-8 to the targetEncoding. + if (targetEncoding != null && targetEncoding != Encoding.UTF8) + { +#if NETCOREAPP + Stream transcodingStream = Encoding.CreateTranscodingStream(targetStream, targetEncoding, Encoding.UTF8, leaveOpen: true); + try + { + if (async) + { + await JsonSerializer.SerializeAsync(transcodingStream, _typedValue, _typeInfo, cancellationToken).ConfigureAwait(false); + } + else + { + // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly. + // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574 + using var writer = new Utf8JsonWriter(transcodingStream); + JsonSerializer.Serialize(writer, _typedValue, _typeInfo); + } + } + finally + { + // Dispose/DisposeAsync will flush any partial write buffers. In practice our partial write + // buffers should be empty as we expect JsonSerializer to emit only well-formed UTF-8 data. + if (async) + { + await transcodingStream.DisposeAsync().ConfigureAwait(false); + } + else + { + transcodingStream.Dispose(); + } + } +#else + Debug.Assert(async); + + using (TranscodingWriteStream transcodingStream = new TranscodingWriteStream(targetStream, targetEncoding)) + { + await JsonSerializer.SerializeAsync(transcodingStream, _typedValue, _typeInfo, cancellationToken).ConfigureAwait(false); + // The transcoding streams use Encoders and Decoders that have internal buffers. We need to flush these + // when there is no more data to be written. Stream.FlushAsync isn't suitable since it's + // acceptable to Flush a Stream (multiple times) prior to completion. + await transcodingStream.FinalWriteAsync(cancellationToken).ConfigureAwait(false); + } +#endif + } + else + { + if (async) + { + await JsonSerializer.SerializeAsync(targetStream, _typedValue, _typeInfo, cancellationToken).ConfigureAwait(false); + } + else + { +#if NETCOREAPP + // Have to use Utf8JsonWriter because JsonSerializer doesn't support sync serialization into stream directly. + // ToDo: Remove Utf8JsonWriter usage after https://github.com/dotnet/runtime/issues/1574 + using var writer = new Utf8JsonWriter(targetStream); + JsonSerializer.Serialize(writer, _typedValue, _typeInfo); +#else + Debug.Fail("Synchronous serialization is only supported since .NET 5.0"); +#endif + } + } + } + } +} diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.netcoreapp.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.netcoreapp.cs new file mode 100644 index 00000000000000..bd6bb97da22304 --- /dev/null +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonContentOfT.netcoreapp.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Net.Http.Json +{ + internal sealed partial class JsonContent + { + protected override void SerializeToStream(Stream stream, TransportContext? context, CancellationToken cancellationToken) + => SerializeToStreamAsyncCore(stream, async: false, cancellationToken).GetAwaiter().GetResult(); + + protected override Task SerializeToStreamAsync(Stream stream, TransportContext? context, CancellationToken cancellationToken) + => SerializeToStreamAsyncCore(stream, async: true, cancellationToken); + } +} diff --git a/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonHelpers.cs b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonHelpers.cs new file mode 100644 index 00000000000000..145340b0079ffc --- /dev/null +++ b/src/libraries/System.Net.Http.Json/src/System/Net/Http/Json/JsonHelpers.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Net.Http.Headers; +using System.Text; + +namespace System.Net.Http.Json +{ + internal static class JsonHelpers + { + internal static MediaTypeHeaderValue GetDefaultMediaType() => new("application/json") { CharSet = "utf-8" }; + + internal static Encoding? GetEncoding(string? charset) + { + Encoding? encoding = null; + + if (charset != null) + { + try + { + // Remove at most a single set of quotes. + if (charset.Length > 2 && charset[0] == '\"' && charset[charset.Length - 1] == '\"') + { + encoding = Encoding.GetEncoding(charset.Substring(1, charset.Length - 2)); + } + else + { + encoding = Encoding.GetEncoding(charset); + } + } + catch (ArgumentException e) + { + throw new InvalidOperationException(SR.CharSetInvalid, e); + } + + Debug.Assert(encoding != null); + } + + return encoding; + } + } +} diff --git a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/HttpClientJsonExtensionsTests.cs b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/HttpClientJsonExtensionsTests.cs index 680e42e2aaa331..580806a40aac31 100644 --- a/src/libraries/System.Net.Http.Json/tests/FunctionalTests/HttpClientJsonExtensionsTests.cs +++ b/src/libraries/System.Net.Http.Json/tests/FunctionalTests/HttpClientJsonExtensionsTests.cs @@ -74,7 +74,7 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync( await Assert.ThrowsAsync(() => client.GetFromJsonAsync(uri, typeof(Person))); await Assert.ThrowsAsync(() => client.GetFromJsonAsync(uri)); await Assert.ThrowsAsync(() => client.GetFromJsonAsync(uri, typeof(Person), JsonContext.Default)); - await Assert.ThrowsAsync(() => client.GetFromJsonAsync(uri, JsonContext.Default.Person)); + await Assert.ThrowsAsync(() => client.GetFromJsonAsync(uri, JsonContext.Default.Person)); } }, server => server.HandleRequestAsync(statusCode: HttpStatusCode.InternalServerError)); @@ -101,6 +101,18 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync( using HttpResponseMessage response4 = await client.PostAsJsonAsync(uri, person, CancellationToken.None); Assert.True(response4.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response5 = await client.PostAsJsonAsync(uri.ToString(), person, JsonContext.Default.Person); + Assert.True(response5.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response6 = await client.PostAsJsonAsync(uri, person, JsonContext.Default.Person); + Assert.True(response6.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response7 = await client.PostAsJsonAsync(uri.ToString(), person, JsonContext.Default.Person, CancellationToken.None); + Assert.True(response7.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response8 = await client.PostAsJsonAsync(uri, person, JsonContext.Default.Person, CancellationToken.None); + Assert.True(response8.StatusCode == HttpStatusCode.OK); } }, async server => { @@ -133,6 +145,18 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync( using HttpResponseMessage response4 = await client.PutAsJsonAsync(uri, person, CancellationToken.None); Assert.True(response4.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response5 = await client.PutAsJsonAsync(uri.ToString(), person, JsonContext.Default.Person); + Assert.True(response5.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response6 = await client.PutAsJsonAsync(uri, person, JsonContext.Default.Person); + Assert.True(response6.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response7 = await client.PutAsJsonAsync(uri.ToString(), person, JsonContext.Default.Person, CancellationToken.None); + Assert.True(response7.StatusCode == HttpStatusCode.OK); + + using HttpResponseMessage response8 = await client.PutAsJsonAsync(uri, person, JsonContext.Default.Person, CancellationToken.None); + Assert.True(response8.StatusCode == HttpStatusCode.OK); } }, async server => { @@ -153,20 +177,52 @@ await HttpMessageHandlerLoopbackServer.CreateClientAndServerAsync( [Fact] public void TestHttpClientIsNullAsync() { + const string uriString = "http://example.com"; + const string clientParamName = "client"; + + HttpClient client = null; + Uri uri = new Uri(uriString); + + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uriString, typeof(Person))); + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uri, typeof(Person))); + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uriString)); + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uri)); + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uriString, typeof(Person), JsonContext.Default)); + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uri, typeof(Person), JsonContext.Default)); + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uriString, JsonContext.Default.Person)); + AssertExtensions.Throws(clientParamName, () => client.GetFromJsonAsync(uri, JsonContext.Default.Person)); + + AssertExtensions.Throws(clientParamName, () => client.PostAsJsonAsync(uriString, null)); + AssertExtensions.Throws(clientParamName, () => client.PostAsJsonAsync(uri, null)); + AssertExtensions.Throws(clientParamName, () => client.PostAsJsonAsync(uriString, null, JsonContext.Default.Person)); + AssertExtensions.Throws(clientParamName, () => client.PostAsJsonAsync(uri, null, JsonContext.Default.Person)); + + AssertExtensions.Throws(clientParamName, () => client.PutAsJsonAsync(uriString, null)); + AssertExtensions.Throws(clientParamName, () => client.PutAsJsonAsync(uri, null)); + AssertExtensions.Throws(clientParamName, () => client.PutAsJsonAsync(uriString, null, JsonContext.Default.Person)); + AssertExtensions.Throws(clientParamName, () => client.PutAsJsonAsync(uri, null, JsonContext.Default.Person)); + } + + [Fact] + public void TestTypeMetadataIsNull() + { + const string uriString = "http://example.com"; + const string jsonTypeInfoParamName = "client"; + const string contextParamName = "client"; + HttpClient client = null; - string uriString = "http://example.com"; Uri uri = new Uri(uriString); - AssertExtensions.Throws("client", () => client.GetFromJsonAsync(uriString, typeof(Person))); - AssertExtensions.Throws("client", () => client.GetFromJsonAsync(uri, typeof(Person))); - AssertExtensions.Throws("client", () => client.GetFromJsonAsync(uriString)); - AssertExtensions.Throws("client", () => client.GetFromJsonAsync(uri)); + AssertExtensions.Throws(contextParamName, () => client.GetFromJsonAsync(uriString, typeof(Person), JsonContext.Default)); + AssertExtensions.Throws(contextParamName, () => client.GetFromJsonAsync(uri, typeof(Person), JsonContext.Default)); + AssertExtensions.Throws(jsonTypeInfoParamName, () => client.GetFromJsonAsync(uriString, JsonContext.Default.Person)); + AssertExtensions.Throws(jsonTypeInfoParamName, () => client.GetFromJsonAsync(uri, JsonContext.Default.Person)); - AssertExtensions.Throws("client", () => client.PostAsJsonAsync(uriString, null)); - AssertExtensions.Throws("client", () => client.PostAsJsonAsync(uri, null)); + AssertExtensions.Throws(jsonTypeInfoParamName, () => client.PostAsJsonAsync(uriString, null, JsonContext.Default.Person)); + AssertExtensions.Throws(jsonTypeInfoParamName, () => client.PostAsJsonAsync(uri, null, JsonContext.Default.Person)); - AssertExtensions.Throws("client", () => client.PutAsJsonAsync(uriString, null)); - AssertExtensions.Throws("client", () => client.PutAsJsonAsync(uri, null)); + AssertExtensions.Throws(jsonTypeInfoParamName, () => client.PutAsJsonAsync(uriString, null, JsonContext.Default.Person)); + AssertExtensions.Throws(jsonTypeInfoParamName, () => client.PutAsJsonAsync(uri, null, JsonContext.Default.Person)); } private void ValidateRequest(HttpRequestData requestData)