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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>InterfaceStubGeneratorV1</AssemblyName>
<RootNamespace>Refit.Generator</RootNamespace>
<IsPackable>false</IsPackable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>InterfaceStubGeneratorV2</AssemblyName>
<RootNamespace>Refit.Generator</RootNamespace>
<IsPackable>false</IsPackable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>InterfaceStubGeneratorV3</AssemblyName>
<RootNamespace>Refit.Generator</RootNamespace>
<IsPackable>false</IsPackable>
Expand Down
81 changes: 61 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ services

* [Where does this work?](#where-does-this-work)
* [Breaking changes in 6.x](#breaking-changes-in-6x)
* [Breaking changes in 11.x](#breaking-changes-in-11x)
* [API Attributes](#api-attributes)
* [Querystrings](#querystrings)
* [Dynamic Querystring Parameters](#dynamic-querystring-parameters)
Expand Down Expand Up @@ -112,6 +113,24 @@ Refit 6.3 splits out the XML serialization via `XmlContentSerializer` into a sep
is to reduce the dependency size when using Refit with Web Assembly (WASM) applications. If you require XML, add a reference
to `Refit.Xml`.

### V11.x.x

#### Breaking changes in 11.x

Refit 10 introduces `ApiRequestException` to represent requests that fail before receiving a response from the server.
This exception will now wrap previous exceptions such as `HttpRequestException` and `TaskCanceledException` when they occur during request execution.

* If you were not wrapping responses with `IApiResponse` and were catching these exceptions directly, you will need to update your code to catch `ApiRequestException` instead.
* If you were wrapping responses with `IApiResponse`, these exceptions will no longer be thrown and will instead be captured in the `IApiResponse.Error` property.
You can use the new `IApiResponse.HasRequestError(out var apiRequestException)` method to safely check and retrieve the `ApiRequestException` instance.

The `IApiResponse.Error` property's type has also changed to `ApiExceptionBase`, which is the new base class for `ApiException` and `ApiRequestException`.
If your code accessed members specific to `ApiException` (i.e. anything related to the response from the server), you can use the new `IApiResponse.HasResponseError(out var apiException)` method to safely check and retrieve the `ApiException` instance.

All response-related properties of `IApiResponse` are now nullable.
The new `IApiResponse.IsReceived` property can be used to check if a response was received from the server, and will mark those properties as non-null.
The original `IApiResponse.IsSuccessful` and `IApiResponse.IsSuccessStatusCode` properties can still be used to check if the response was received and is successful.

### API Attributes

Every method must have an HTTP attribute that provides the request method and
Expand Down Expand Up @@ -1110,31 +1129,34 @@ Task<ApiResponse<User>> GetUser(string user);
//Calling the API
var response = await gitHubApi.GetUser("octocat");

//Getting the status code (returns a value from the System.Net.HttpStatusCode enumeration)
var httpStatus = response.StatusCode;

//Determining if a success status code was received and there wasn't any other error
//(for example, during content deserialization)
if(response.IsSuccessful)
{
//YAY! Do the thing...
}

//Retrieving a well-known header value (e.g. "Server" header)
var serverHeaderValue = response.Headers.Server != null ? response.Headers.Server.ToString() : string.Empty;
if (response.IsReceived)
{
//Getting the status code (returns a value from the System.Net.HttpStatusCode enumeration)
var httpStatus = response.StatusCode;

//Retrieving a custom header value
var customHeaderValue = string.Join(',', response.Headers.GetValues("A-Custom-Header"));
//Retrieving a well-known header value (e.g. "Server" header)
var serverHeaderValue = response.Headers.Server != null ? response.Headers.Server.ToString() : string.Empty;

//Looping through all the headers
foreach(var header in response.Headers)
{
var headerName = header.Key;
var headerValue = string.Join(',', header.Value);
}
//Retrieving a custom header value
var customHeaderValue = string.Join(',', response.Headers.GetValues("A-Custom-Header"));

//Finally, retrieving the content in the response body as a strongly-typed object
var user = response.Content;
//Looping through all the headers
foreach(var header in response.Headers)
{
var headerName = header.Key;
var headerValue = string.Join(',', header.Value);
}

//Finally, retrieving the content in the response body as a strongly-typed object
var user = response.Content;
}
```

### Using generic interfaces
Expand Down Expand Up @@ -1402,7 +1424,9 @@ Refit also ships analyzers for newer Roslyn toolchains, including a Roslyn 5.0 b
Refit has different exception handling behavior depending on if your Refit interface methods return `Task<T>` or if they return `Task<IApiResponse>`, `Task<IApiResponse<T>>`, or `Task<ApiResponse<T>>`.

#### <a id="when-returning-taskapiresponset"></a>When returning `Task<IApiResponse>`, `Task<IApiResponse<T>>`, or `Task<ApiResponse<T>>`
Refit traps any `ApiException` raised by the `ExceptionFactory` when processing the response, and any errors that occur when attempting to deserialize the response to `ApiResponse<T>`, and populates the exception into the `Error` property on `ApiResponse<T>` without throwing the exception.
Refit traps any `HttpRequestException` or `TaskCanceledException` raised by the `HttpClient` in an `ApiRequestException`.
Refit also traps any `ApiException` raised by the `ExceptionFactory` when processing the response, and any errors that occur when attempting to deserialize the response to `ApiResponse<T>`.
In both cases, it will populate the exception into the `Error` property on `ApiResponse<T>` without throwing the exception.

You can then decide what to do like so:

Expand All @@ -1414,25 +1438,42 @@ if(response.IsSuccessful)
}
else
{
_logger.LogError(response.Error, response.Error.Content);
// If you want to distinguish between request and response errors
if (response.HasRequestError(out var requestError))
_logger.LogError(requestError, "An error occurred while sending the request.");
else if (response.HasResponseError(out var responseError))
_logger.LogError(responseError, responseError.Content);

// Or just log the error directly
_logger.LogError(response.Error, "An error occurred while calling the API.");
}
```

> [!NOTE]
> The `IsSuccessful` property checks whether the response status code is in the range 200-299 and there wasn't any other error (for example, during content deserialization). If you just want to check the HTTP response status code, you can use the `IsSuccessStatusCode` property.

#### When returning `Task<T>`
Refit throws any `ApiException` raised by the `ExceptionFactory` when processing the response and any errors that occur when attempting to deserialize the response to `Task<T>`.
Refit throws any exception raised by the `HttpClient` and wraps it in an `ApiRequestException`.
It also throws any `ApiException` raised by the `ExceptionFactory` when processing the response and any errors that occur when attempting to deserialize the response to `Task<T>`.

```csharp
// ...
try
{
var result = await awesomeApi.GetFooAsync("bar");
}
catch (ApiRequestException exception)
{
//exception handling for when a response was not received from the server
}
catch (ApiException exception)
{
//exception handling
//exception handling for when a response was received from the server
}
// Or to not distinguish between request/response exceptions
catch (ApiExceptionBase exception)
{
//exception handling for when an error occurs during the request/response
}
// ...
```
Expand Down Expand Up @@ -1464,7 +1505,7 @@ catch (ApiException exception)

#### Providing a custom `ExceptionFactory`

You can also override default exceptions behavior that are raised by the `ExceptionFactory` when processing the result by providing a custom exception factory in `RefitSettings`. For example, you can suppress all exceptions with the following:
You can also override default exceptions behavior that are raised by the `ExceptionFactory` when processing the result by providing a custom exception factory in `RefitSettings`. For example, you can suppress all `ApiException`s with the following:

```csharp
var nullTask = Task.FromResult<Exception>(null);
Expand Down
Loading
Loading