Skip to content

Commit

Permalink
Support for adding request header and body to the message for HttpApi…
Browse files Browse the repository at this point in the history
…ResponseException (#196)
  • Loading branch information
mburumaxwell authored Dec 20, 2023
1 parent 382c01e commit dc95831
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 14 deletions.
38 changes: 35 additions & 3 deletions src/Tingle.Extensions.Http/AbstractHttpApiClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,44 @@ public abstract class AbstractHttpApiClientOptions
/// or <see cref="ResourceResponse{TResource, TProblem}.EnsureHasResource"/>.
/// Defaults to false.
/// </summary>
public virtual bool IncludeHeadersInExceptionMessage { get; set; } = false;
[Obsolete("Use '" + nameof(IncludeResponseHeadersInExceptionMessage) + "' instead.")]
public virtual bool IncludeHeadersInExceptionMessage { get => IncludeResponseHeadersInExceptionMessage; set => IncludeResponseHeadersInExceptionMessage = value; }

/// <summary>
/// Determines if the raw body should be included in the message when creating <see cref="HttpApiResponseException"/>
/// Determines if the response body should be included in the message when creating <see cref="HttpApiResponseException"/>
/// via <see cref="ResourceResponse{TResource, TProblem}.EnsureSuccess"/>.
/// Defaults to false.
/// </summary>
public virtual bool IncludeRawBodyInExceptionMessage { get; set; } = false;
[Obsolete("Use '" + nameof(IncludeResponseBodyInExceptionMessage) + "' instead.")]
public virtual bool IncludeRawBodyInExceptionMessage { get => IncludeResponseBodyInExceptionMessage; set => IncludeResponseBodyInExceptionMessage = value; }

/// <summary>
/// Determines if the request headers should be included in the message when creating <see cref="HttpApiResponseException"/>
/// via <see cref="ResourceResponse{TResource, TProblem}.EnsureSuccess"/>
/// or <see cref="ResourceResponse{TResource, TProblem}.EnsureHasResource"/>.
/// Defaults to false.
/// </summary>
public virtual bool IncludeRequestHeadersInExceptionMessage { get; set; } = false;

/// <summary>
/// Determines if the request body should be included in the message when creating <see cref="HttpApiResponseException"/>
/// via <see cref="ResourceResponse{TResource, TProblem}.EnsureSuccess"/>.
/// Defaults to false.
/// </summary>
public virtual bool IncludeRequestBodyInExceptionMessage { get; set; } = false;

/// <summary>
/// Determines if the response headers should be included in the message when creating <see cref="HttpApiResponseException"/>
/// via <see cref="ResourceResponse{TResource, TProblem}.EnsureSuccess"/>
/// or <see cref="ResourceResponse{TResource, TProblem}.EnsureHasResource"/>.
/// Defaults to false.
/// </summary>
public virtual bool IncludeResponseHeadersInExceptionMessage { get; set; } = false;

/// <summary>
/// Determines if the response body should be included in the message when creating <see cref="HttpApiResponseException"/>
/// via <see cref="ResourceResponse{TResource, TProblem}.EnsureSuccess"/>.
/// Defaults to false.
/// </summary>
public virtual bool IncludeResponseBodyInExceptionMessage { get; set; } = false;
}
18 changes: 14 additions & 4 deletions src/Tingle.Extensions.Http/ResourceResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,27 @@ protected HttpApiResponseException CreateException(string messagePrefix, bool ap
/// <returns>The appended message.</returns>
protected string AppendHeaders(string message)
{
string serialize() => System.Text.Json.JsonSerializer.Serialize(Headers, SC.Default.ResourceResponseHeaders);
return AppendIf(message, o => o.IncludeHeadersInExceptionMessage, serialize, "Headers:\n{0}");
static string serialize(ResourceResponseHeaders headers) => System.Text.Json.JsonSerializer.Serialize(headers, SC.Default.ResourceResponseHeaders);
string serializeRequest() => serialize(new(Response.RequestMessage));
string serializeResponse() => serialize(Headers);

message = AppendIf(message, o => o.IncludeRequestHeadersInExceptionMessage, serializeRequest, "Request Headers:\n{0}");
message = AppendIf(message, o => o.IncludeResponseHeadersInExceptionMessage, serializeResponse, "Response Headers:\n{0}");
return message;
}

/// <summary>Append the raw body if present to an error message.</summary>
/// <param name="message">The message to append to.</param>
/// <returns>The appended message.</returns>
protected string AppendRawBody(string message)
{
string serialize() => Response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return AppendIf(message, o => o.IncludeRawBodyInExceptionMessage, serialize, "Body:\n{0}");
static string serialize(HttpContent content) => content.ReadAsStringAsync().GetAwaiter().GetResult();
string serializeRequest() => serialize(Response.RequestMessage.Content);
string serializeResponse() => serialize(Response.Content);

message = AppendIf(message, o => o.IncludeRequestBodyInExceptionMessage, serializeRequest, "Request Body:\n{0}");
message = AppendIf(message, o => o.IncludeResponseBodyInExceptionMessage, serializeResponse, "Response Body:\n{0}");
return message;
}

private string AppendIf(string message, Func<AbstractHttpApiClientOptions, bool> evaluator, Func<string> serialize, string format)
Expand Down
4 changes: 4 additions & 0 deletions src/Tingle.Extensions.Http/ResourceResponseHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ public class ResourceResponseHeaders : Dictionary<string, IEnumerable<string>>
/// <param name="response">The original HTTP response.</param>
public ResourceResponseHeaders(HttpResponseMessage response) : this(response.Headers.Concat(response.Content.Headers)) { }

/// <summary>Creates an instance of <see cref="ResourceResponseHeaders"/>.</summary>
/// <param name="response">The original HTTP request.</param>
public ResourceResponseHeaders(HttpRequestMessage response) : this(response.Headers.Concat(response.Content.Headers)) { }

/// <summary>Creates an instance of <see cref="ResourceResponseHeaders"/>.</summary>
/// <param name="data">The combined headers.</param>
public ResourceResponseHeaders(IEnumerable<KeyValuePair<string, IEnumerable<string>>> data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ private static IHttpClientBuilder AddNotifier<TNotifier, TOptions>(this IService
return services.AddHttpApiClient<TNotifier, TOptions>(options =>
{
// include error details in the exception
options.IncludeHeadersInExceptionMessage = true;
options.IncludeRawBodyInExceptionMessage = true;
options.IncludeResponseHeadersInExceptionMessage = true;
options.IncludeResponseBodyInExceptionMessage = true;

configure?.Invoke(options);
});
Expand Down
16 changes: 11 additions & 5 deletions tests/Tingle.Extensions.Http.Tests/ResourceResponseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,26 @@ public void EnsureSuccess_Throws_Exception()
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound);
response.Headers.Date = new DateTimeOffset(DateTimeOffset.UtcNow.Date, TimeSpan.Zero);
response.Content = new StringContent("{\"status\":404}");
response.Content = new StringContent("{\"status\":404}", System.Text.Encoding.UTF8, "application/json");
response.RequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://localhost") { Content = new StringContent("{\"action\":\"write\"}"), };

var options = new DummyHttpApiClientOptions
{
IncludeHeadersInExceptionMessage = true,
IncludeRawBodyInExceptionMessage = true,
IncludeRequestHeadersInExceptionMessage = true,
IncludeRequestBodyInExceptionMessage = true,

IncludeResponseHeadersInExceptionMessage = true,
IncludeResponseBodyInExceptionMessage = true,
};

var rr = new ResourceResponse<object>(response, options);
Assert.Equal(HttpStatusCode.NotFound, rr.StatusCode);

var message = "The HTTP request failed with code 404 (NotFound)\n"
+ $"\nHeaders:\n{{\"Date\":[\"{response.Headers.Date:r}\"],\"Content-Type\":[\"text/plain; charset=utf-8\"]}}\n"
+ "\nBody:\n{\"status\":404}";
+ "\nRequest Headers:\n{\"Content-Type\":[\"text/plain; charset=utf-8\"]}\n"
+ $"\nResponse Headers:\n{{\"Date\":[\"{response.Headers.Date:r}\"],\"Content-Type\":[\"application/json; charset=utf-8\"]}}\n"
+ "\nRequest Body:\n{\"action\":\"write\"}\n"
+ "\nResponse Body:\n{\"status\":404}";
var ex = Assert.Throws<HttpApiResponseException>(rr.EnsureSuccess);
Assert.Equal(message, ex.Message);

Expand Down

0 comments on commit dc95831

Please sign in to comment.