Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -634,3 +634,25 @@ public class HomeController : Controller
}
}
```

### Handling exceptions

To encapsulate any exceptions that may come from a service, you can catch an `ApiException` which contains request- and response information. Refit also supports the catching of validation exceptions that are thrown by a service implementing the RFC 7807 specification for problem details due to bad requests. For specific information on the problem details of the validation exception, simply catch `ValidationApiException`:

```csharp
// ...
try
{
var result = await awesomeApi.GetFooAsync("bar");
}
catch (ValidationApiException validationException)
{
// handle validation here by using validationException.Content,
// which is type of ProblemDetails according to RFC 7807
}
catch (ApiException exception)
{
// other exception handling
}
// ...
```
32 changes: 32 additions & 0 deletions Refit.Tests/ResponseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,37 @@ public async Task AliasAsCannotBeUsedToAliasFieldNamesInResponses()
Assert.Null(result.ShortNameForAlias);
}

/// <summary>
/// Test to verify if a ValidationException is thrown for a Bad Request in terms of RFC 7807
/// </summary>
[Fact]
public async Task ThrowsValidationException()
{
var expectedContent = new ProblemDetails
{
Detail = "detail",
Errors = { { "Field1", new string[] { "Problem1" } }, { "Field2", new string[] { "Problem2" } } },
Instance = "instance",
Status = 1,
Title = "title",
Type = "type"
};
var expectedResponse = new HttpResponseMessage(HttpStatusCode.BadRequest);
expectedResponse.Content = new StringContent(JsonConvert.SerializeObject(expectedContent));
expectedResponse.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/problem+json");
mockHandler.Expect(HttpMethod.Get, "http://api/aliasTest")
.Respond(req => expectedResponse);

var actualException = await Assert.ThrowsAsync<ValidationApiException>(() => fixture.GetTestObject());
Assert.NotNull(actualException.Content);
Assert.Equal("detail", actualException.Content.Detail);
Assert.Equal("Problem1", actualException.Content.Errors["Field1"][0]);
Assert.Equal("Problem2", actualException.Content.Errors["Field2"][0]);
Assert.Equal("instance", actualException.Content.Instance);
Assert.Equal(1, actualException.Content.Status);
Assert.Equal("title", actualException.Content.Title);
Assert.Equal("type", actualException.Content.Type);

}
}
}
8 changes: 7 additions & 1 deletion Refit/ApiException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ApiException : Exception
public bool HasContent => !string.IsNullOrWhiteSpace(Content);
public RefitSettings RefitSettings { get; set; }

ApiException(HttpRequestMessage message, HttpMethod httpMethod, HttpStatusCode statusCode, string reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings = null) :
protected ApiException(HttpRequestMessage message, HttpMethod httpMethod, HttpStatusCode statusCode, string reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings = null) :
base(CreateMessage(statusCode, reasonPhrase))
{
RequestMessage = message;
Expand All @@ -47,10 +47,16 @@ public static async Task<ApiException> Create(HttpRequestMessage message, HttpMe
var exception = new ApiException(message, httpMethod, response.StatusCode, response.ReasonPhrase, response.Headers, refitSettings);

if (response.Content == null)
{
return exception;
}

try
{
if (response.Content.Headers.ContentType.MediaType.Equals("application/problem+json"))
{
exception = ValidationApiException.Create(exception);
}
exception.ContentHeaders = response.Content.Headers;
exception.Content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
response.Content.Dispose();
Expand Down
41 changes: 41 additions & 0 deletions Refit/ProblemDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Collections.Generic;

namespace Refit
{
/// <summary>
/// The object representing the details about a ValidationException caught by a service implementing RFC 7807.
/// </summary>
public class ProblemDetails
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a doc comment to the type itself

{
/// <summary>
/// Collection of resulting errors for the request.
/// </summary>
public Dictionary<string, string[]> Errors { get; set; } = new Dictionary<string, string[]>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulcbetts any thoughts on using the concrete type here vs an IDictionary<string,string> or IReadOnlyDictionary<string, string>?


/// <summary>
/// A URI reference that identifies the problem type.
/// </summary>
public string Type { get; set; } = "about:blank";

/// <summary>
/// A short, human-readable summary of the problem type.
/// </summary>
public string Title { get; set; }

/// <summary>
/// The HTTP status code generated by the origin server for this occurrence of the problem.
/// </summary>
public int Status { get; set; }

/// <summary>
/// A human-readable explanation specific to this occurrence of the problem.
/// </summary>
public string Detail { get; set; }

/// <summary>
/// A URI reference that identifies the specific occurrence of the problem.
/// </summary>
public string Instance { get; set; }
}

}
33 changes: 33 additions & 0 deletions Refit/ValidationApiException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;

namespace Refit
{
/// <summary>
/// An ApiException that is raised according to RFC 7807, which contains problem details for validation exceptions.
/// </summary>
[Serializable]
public class ValidationApiException : ApiException
{

ValidationApiException(ApiException apiException) :
base(apiException.RequestMessage, apiException.HttpMethod, apiException.StatusCode, apiException.ReasonPhrase, apiException.Headers, apiException.RefitSettings)
{
}

/// <summary>
/// Creates a new instance of a ValidationException from an existing ApiException.
/// </summary>
/// <param name="exception">An instance of an ApiException to use to build a ValidationException.</param>
/// <returns>ValidationApiException</returns>
public static ValidationApiException Create(ApiException exception)
{
return new ValidationApiException(exception);
}

/// <summary>
/// The problem details of the RFC 7807 validation exception.
/// </summary>
public new ProblemDetails Content => GetContentAs<ProblemDetails>();

}
}