Repository: dotnet/aspnetcore
File: src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs (C#)

Code before:
    [RequiresDynamicCode(RequiresDynamicCodeMessage)]
    public static async ValueTask<TValue?> ReadFromJsonAsync<TValue>(
        this HttpRequest request,
        JsonSerializerOptions? options,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(request);

        if (!request.HasJsonContentType(out var charset))
        {
            ThrowContentTypeError(request);
        }

        options ??= ResolveSerializerOptions(request.HttpContext);

        var encoding = GetEncodingFromCharset(charset);
        var (inputStream, usesTranscodingStream) = GetInputStream(request.HttpContext, encoding);

        try
        {
            return await JsonSerializer.DeserializeAsync<TValue>(inputStream, options, cancellationToken);
        }
        finally
        {
            if (usesTranscodingStream)
            {
                await inputStream.DisposeAsync();
            }
        }
    }

    /// <summary>
    /// Read JSON from the request and deserialize to the specified type.
    /// If the request's content-type is not a known JSON type then an error will be thrown.
    /// </summary>
    /// <param name="request">The request to read from.</param>
    /// <param name="jsonTypeInfo">Metadata about the type to convert.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
    /// <returns>The deserialized value.</returns>
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
    public static async ValueTask<TValue?> ReadFromJsonAsync<TValue>(
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
        this HttpRequest request,
        JsonTypeInfo<TValue> jsonTypeInfo,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(request);

        if (!request.HasJsonContentType(out var charset))
        {
            ThrowContentTypeError(request);

Review comment (refactor):
Nit: Is there anyway for us to dedupe this logic across the different extension methods? Maybe a helper that figures out what serialization invocation to use based on the context switch and return the associated task?

Code after:
    /// <param name="options">The serializer options to use when deserializing the content.</param>
    /// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
    /// <returns>The task object representing the asynchronous operation.</returns>
    [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)]
    [RequiresDynamicCode(RequiresDynamicCodeMessage)]
    public static async ValueTask<TValue?> ReadFromJsonAsync<TValue>(
        this HttpRequest request,
        JsonSerializerOptions? options,
        CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(request);

        if (!request.HasJsonContentType(out var charset))
        {
            ThrowContentTypeError(request);
        }

        options ??= ResolveSerializerOptions(request.HttpContext);

        var encoding = GetEncodingFromCharset(charset);
        Stream? inputStream = null;
        ValueTask<TValue?> deserializeTask;

        try
        {
            if (encoding == null || encoding.CodePage == Encoding.UTF8.CodePage)
            {
                if (_useStreamJsonOverload)
                {
                    deserializeTask = JsonSerializer.DeserializeAsync<TValue>(request.Body, options, cancellationToken);
                }
                else
                {
                    deserializeTask = JsonSerializer.DeserializeAsync<TValue>(request.BodyReader, options, cancellationToken);
                }
            }
            else
            {
                inputStream = Encoding.CreateTranscodingStream(request.Body, encoding, Encoding.UTF8, leaveOpen: true);
                deserializeTask = JsonSerializer.DeserializeAsync<TValue>(inputStream, options, cancellationToken);
            }

            return await deserializeTask;
        }
        finally
        {
            if (inputStream is not null)
            {
                await inputStream.DisposeAsync();
            }
        }