< Summary

Information
Class: ArturRios.Util.Http.HttpOutput<T>
Assembly: ArturRios.Util
File(s): /home/runner/work/dotnet-util/dotnet-util/src/Http/HttpOutput.cs
Line coverage
100%
Covered lines: 14
Uncovered lines: 0
Coverable lines: 14
Total lines: 73
Line coverage: 100%
Branch coverage
50%
Covered branches: 1
Total branches: 2
Branch coverage: 50%
Method coverage
100%
Covered methods: 8
Fully covered methods: 8
Total methods: 8
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_StatusCode()100%11100%
get_Headers()100%11100%
get_ContentHeaders()100%11100%
get_RawBody()100%11100%
get_Body()100%11100%
get_IsSuccess()50%22100%
ReadContentAsync()100%11100%

File(s)

/home/runner/work/dotnet-util/dotnet-util/src/Http/HttpOutput.cs

#LineLine coverage
 1using System.Net;
 2using System.Net.Http.Headers;
 3using System.Text.Json;
 4
 5namespace ArturRios.Util.Http;
 6
 7/// <summary>
 8/// Represents a typed HTTP response output, including status, headers, and a deserialized body.
 9/// </summary>
 10/// <typeparam name="TBody">The type of the deserialized response body.</typeparam>
 3611public class HttpOutput<TBody>(HttpResponseMessage responseMessage)
 12{
 13    /// <summary>
 14    /// Gets the HTTP status code returned by the server.
 15    /// </summary>
 6616    public HttpStatusCode StatusCode { get; } = responseMessage.StatusCode;
 17
 18    /// <summary>
 19    /// Gets the HTTP response headers. Content headers such as Content-Type are exposed separately by
 20    /// <see cref="ContentHeaders"/>.
 21    /// </summary>
 3822    public HttpResponseHeaders Headers { get; } = responseMessage.Headers;
 23
 24    /// <summary>
 25    /// Gets the HTTP content headers, such as Content-Type and Content-Length.
 26    /// </summary>
 3827    public HttpContentHeaders ContentHeaders { get; } = responseMessage.Content.Headers;
 28
 29    /// <summary>
 30    /// Gets the raw response body, exactly as the server sent it.
 31    /// </summary>
 32    /// <remarks>
 33    /// Populated by <see cref="ReadContentAsync"/>. Useful when <see cref="Body"/> is <c>null</c> because
 34    /// the payload could not be bound to <typeparamref name="TBody"/> — an HTML error page, for example.
 35    /// </remarks>
 7036    public string? RawBody { get; private set; }
 37
 38    /// <summary>
 39    /// Gets the deserialized response body, or <c>null</c> when the payload could not be bound to
 40    /// <typeparamref name="TBody"/>.
 41    /// </summary>
 6542    public TBody? Body { get; private set; }
 43
 44    /// <summary>
 45    /// Indicates whether the response carries a 2xx status code.
 46    /// </summary>
 1647    public bool IsSuccess => (int)StatusCode is >= 200 and <= 299;
 48
 49    /// <summary>
 50    /// Reads the response content as a string and deserializes it into <typeparamref name="TBody"/>.
 51    /// </summary>
 52    /// <param name="cancellationToken">Cancels reading the response body.</param>
 53    /// <exception cref="OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception>
 54    /// <remarks>
 55    /// Any malformed or mismatched payload leaves <see cref="Body"/> at its default and the response text
 56    /// in <see cref="RawBody"/>. Both a syntax error and a shape mismatch — an array where an object was
 57    /// expected, say — are handled: they are distinct exception types, and catching only the first used to
 58    /// let the second escape to the caller.
 59    /// </remarks>
 60    public async Task ReadContentAsync(CancellationToken cancellationToken = default)
 61    {
 3162        RawBody = await responseMessage.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
 63
 64        try
 65        {
 3166            Body = JsonSerializer.Deserialize<TBody>(RawBody, JsonDefaults.Options);
 2067        }
 1168        catch (JsonException)
 69        {
 1170            Body = default;
 1171        }
 3172    }
 73}