using System.Net; using System.Net.Http; using System.Net.Http.Headers; namespace Refit { /// /// Represents an error that occured while sending an API request. /// [Serializable] public class ApiException : Exception { /// /// HTTP response status code. /// public HttpStatusCode StatusCode { get; } /// /// The reason phrase which typically is sent by the server together with the status code. /// public string? ReasonPhrase { get; } /// /// HTTP response headers. /// public HttpResponseHeaders Headers { get; } /// /// The HTTP method used to send the request. /// public HttpMethod HttpMethod { get; } /// /// The used to send the HTTP request. /// public Uri? Uri => RequestMessage.RequestUri; /// /// The HTTP Request message used to send the request. /// public HttpRequestMessage RequestMessage { get; } /// /// HTTP response content headers as defined in RFC 2616. /// public HttpContentHeaders? ContentHeaders { get; private set; } /// /// HTTP Response content as string. /// public string? Content { get; private set; } /// /// Does the response have content? /// public bool HasContent => !string.IsNullOrWhiteSpace(Content); /// /// Refit settings used to send the request. /// public RefitSettings RefitSettings { get; } /// /// Initializes a new instance of the class. /// /// The message. /// The HTTP method. /// The content. /// The status code. /// The reason phrase. /// The headers. /// The refit settings. /// The inner exception. protected ApiException( HttpRequestMessage message, HttpMethod httpMethod, string? content, HttpStatusCode statusCode, string? reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings, Exception? innerException = null ) : this( CreateMessage(statusCode, reasonPhrase), message, httpMethod, content, statusCode, reasonPhrase, headers, refitSettings, innerException ) { } /// /// Initializes a new instance of the class. /// /// The exception message. /// The message. /// The HTTP method. /// The content. /// The status code. /// The reason phrase. /// The headers. /// The refit settings. /// The inner exception. protected ApiException( string exceptionMessage, HttpRequestMessage message, HttpMethod httpMethod, string? content, HttpStatusCode statusCode, string? reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings, Exception? innerException = null ) : base(exceptionMessage, innerException) { RequestMessage = message; HttpMethod = httpMethod; StatusCode = statusCode; ReasonPhrase = reasonPhrase; Headers = headers; RefitSettings = refitSettings; Content = content; } /// /// Get the deserialized response content as nullable . /// /// Type to deserialize the content to /// The response content deserialized as public async Task GetContentAsAsync() => HasContent ? await RefitSettings.ContentSerializer .FromHttpContentAsync(new StringContent(Content!)) .ConfigureAwait(false) : default; /// /// Create an instance of . /// /// The HTTP Request message used to send the request. /// The HTTP method used to send the request. /// The HTTP Response message. /// Refit settings used to sent the request. /// Add an inner exception to the . /// A newly created . #pragma warning disable VSTHRD200 // Use "Async" suffix for async methods public static Task Create( HttpRequestMessage message, HttpMethod httpMethod, HttpResponseMessage response, RefitSettings refitSettings, Exception? innerException = null ) #pragma warning restore VSTHRD200 // Use "Async" suffix for async methods { var exceptionMessage = CreateMessage(response.StatusCode, response.ReasonPhrase); return Create( exceptionMessage, message, httpMethod, response, refitSettings, innerException ); } /// /// Create an instance of with a custom exception message. /// /// A custom exception message. /// The HTTP Request message used to send the request. /// The HTTP method used to send the request. /// The HTTP Response message. /// Refit settings used to sent the request. /// Add an inner exception to the . /// A newly created . #pragma warning disable VSTHRD200 // Use "Async" suffix for async methods public static async Task Create( string exceptionMessage, HttpRequestMessage message, HttpMethod httpMethod, HttpResponseMessage response, RefitSettings refitSettings, Exception? innerException = null ) #pragma warning restore VSTHRD200 // Use "Async" suffix for async methods { var exception = new ApiException( exceptionMessage, message, httpMethod, null, response.StatusCode, response.ReasonPhrase, response.Headers, refitSettings, innerException ); if (response.Content == null) { return exception; } try { exception.ContentHeaders = response.Content.Headers; var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); exception.Content = content; if ( response.Content.Headers?.ContentType?.MediaType?.Equals( "application/problem+json" ) ?? false ) { exception = ValidationApiException.Create(exception); } response.Content.Dispose(); } catch { // NB: We're already handling an exception at this point, // so we want to make sure we don't throw another one // that hides the real error. } return exception; } static string CreateMessage(HttpStatusCode statusCode, string? reasonPhrase) => $"Response status code does not indicate success: {(int)statusCode} ({reasonPhrase})."; } }