Skip to content

Commit f8e891a

Browse files
committed
Allow treating unsuccessful status code as non-error, avoid setting exception
1 parent 5487cfd commit f8e891a

File tree

6 files changed

+34
-17
lines changed

6 files changed

+34
-17
lines changed

src/RestSharp/Extensions/HttpResponseExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
namespace RestSharp.Extensions;
1919

2020
static class HttpResponseExtensions {
21-
public static Exception? MaybeException(this HttpResponseMessage httpResponse)
22-
=> httpResponse.IsSuccessStatusCode
21+
public static Exception? MaybeException(this HttpResponseMessage httpResponse, bool throwOnUnsuccessfulStatusCode)
22+
=> httpResponse.IsSuccessStatusCode || !throwOnUnsuccessfulStatusCode
2323
? null
2424
#if NET
2525
: new HttpRequestException($"Request failed with status code {httpResponse.StatusCode}", null, httpResponse.StatusCode);

src/RestSharp/Options/ReadOnlyRestClientOptions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ namespace RestSharp;
2121
public partial class ReadOnlyRestClientOptions {
2222
public IReadOnlyCollection<Interceptor>? Interceptors { get; private set; }
2323

24-
// partial void CopyAdditionalProperties(RestClientOptions inner);
2524
partial void CopyAdditionalProperties(RestClientOptions inner) => Interceptors = GetInterceptors(inner);
2625

2726
static ReadOnlyCollection<Interceptor>? GetInterceptors(RestClientOptions? options) {

src/RestSharp/Options/RestClientOptions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ public RestClientOptions(string baseUrl) : this(new Uri(Ensure.NotEmptyString(ba
208208
/// </summary>
209209
public bool ThrowOnAnyError { get; set; }
210210

211+
/// <summary>
212+
/// When set to false, the client doesn't throw an exception when the response status code is not successful.
213+
/// Default is true.
214+
/// </summary>
215+
public bool ErrorWhenUnsuccessfulStatusCode { get; set; } = true;
216+
211217
/// <summary>
212218
/// Set to true to allow multiple default parameters with the same name. Default is false.
213219
/// This setting doesn't apply to headers as multiple header values for the same key is allowed.

src/RestSharp/Response/RestResponse.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
// limitations under the License.
1414

1515
using System.Diagnostics;
16-
using System.Text;
1716
using RestSharp.Extensions;
1817

1918
// ReSharper disable SuggestBaseTypeForParameter
@@ -39,12 +38,11 @@ public partial class RestResponse<T>(RestRequest request) : RestResponse(request
3938
[DebuggerDisplay($"{{{nameof(DebuggerDisplay)}()}}")]
4039
public class RestResponse(RestRequest request) : RestResponseBase(request) {
4140
internal static async Task<RestResponse> FromHttpResponse(
42-
HttpResponseMessage httpResponse,
43-
RestRequest request,
44-
Encoding encoding,
45-
CookieCollection? cookieCollection,
46-
CalculateResponseStatus calculateResponseStatus,
47-
CancellationToken cancellationToken
41+
HttpResponseMessage httpResponse,
42+
RestRequest request,
43+
ReadOnlyRestClientOptions options,
44+
CookieCollection? cookieCollection,
45+
CancellationToken cancellationToken
4846
) {
4947
return request.AdvancedResponseWriter?.Invoke(httpResponse, request) ?? await GetDefaultResponse().ConfigureAwait(false);
5048

@@ -56,7 +54,7 @@ async Task<RestResponse> GetDefaultResponse() {
5654
#endif
5755

5856
var bytes = stream == null ? null : await stream.ReadAsBytes(cancellationToken).ConfigureAwait(false);
59-
var content = bytes == null ? null : await httpResponse.GetResponseString(bytes, encoding);
57+
var content = bytes == null ? null : await httpResponse.GetResponseString(bytes, options.Encoding);
6058

6159
return new(request) {
6260
Content = content,
@@ -65,11 +63,11 @@ async Task<RestResponse> GetDefaultResponse() {
6563
ContentLength = httpResponse.Content?.Headers.ContentLength,
6664
ContentType = httpResponse.Content?.Headers.ContentType?.MediaType,
6765
Cookies = cookieCollection,
68-
ErrorException = httpResponse.MaybeException(),
66+
ErrorException = httpResponse.MaybeException(options.ErrorWhenUnsuccessfulStatusCode),
6967
Headers = httpResponse.Headers.GetHeaderParameters(),
7068
IsSuccessStatusCode = httpResponse.IsSuccessStatusCode,
7169
RawBytes = bytes,
72-
ResponseStatus = calculateResponseStatus(httpResponse),
70+
ResponseStatus = options.CalculateResponseStatus(httpResponse),
7371
ResponseUri = httpResponse.RequestMessage?.RequestUri,
7472
RootElement = request.RootElement,
7573
Server = httpResponse.Headers.Server.ToString(),
@@ -83,4 +81,4 @@ async Task<RestResponse> GetDefaultResponse() {
8381
public RestResponse() : this(new()) { }
8482
}
8583

86-
public delegate ResponseStatus CalculateResponseStatus(HttpResponseMessage httpResponse);
84+
public delegate ResponseStatus CalculateResponseStatus(HttpResponseMessage httpResponse);

src/RestSharp/RestClient.Async.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationTo
3030
? await RestResponse.FromHttpResponse(
3131
internalResponse.ResponseMessage!,
3232
request,
33-
Options.Encoding,
33+
Options,
3434
internalResponse.CookieContainer?.GetCookies(internalResponse.Url),
35-
Options.CalculateResponseStatus,
3635
cancellationToken
3736
)
3837
.ConfigureAwait(false)
@@ -49,7 +48,7 @@ public async Task<RestResponse> ExecuteAsync(RestRequest request, CancellationTo
4948
request.CompletionOption = HttpCompletionOption.ResponseHeadersRead;
5049
var response = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);
5150

52-
var exception = response.Exception ?? response.ResponseMessage?.MaybeException();
51+
var exception = response.Exception ?? response.ResponseMessage?.MaybeException(Options.ErrorWhenUnsuccessfulStatusCode);
5352

5453
if (exception != null) {
5554
return Options.ThrowOnAnyError ? throw exception : null;

test/RestSharp.Tests.Integrated/RequestFailureTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ public async Task Handles_GET_Request_Errors_With_Response_Type() {
2121
var response = await _client.ExecuteAsync<SuccessResponse>(request);
2222

2323
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
24+
response.IsSuccessStatusCode.Should().BeFalse();
25+
response.ErrorException.Should().NotBeNull();
26+
response.Data.Should().Be(null);
27+
}
28+
29+
[Fact]
30+
public async Task Does_not_throw_on_unsuccessful_status_code_with_option() {
31+
using var client = new RestClient(new RestClientOptions(server.Url!) { ErrorWhenUnsuccessfulStatusCode = false });
32+
var request = new RestRequest("status?code=404");
33+
var response = await client.ExecuteAsync<SuccessResponse>(request);
34+
35+
response.StatusCode.Should().Be(HttpStatusCode.NotFound);
36+
response.IsSuccessStatusCode.Should().BeFalse();
37+
response.ErrorException.Should().BeNull();
2438
response.Data.Should().Be(null);
2539
}
2640

@@ -29,6 +43,7 @@ public async Task Throws_on_unsuccessful_call() {
2943
using var client = new RestClient(new RestClientOptions(server.Url!) { ThrowOnAnyError = true });
3044
var request = new RestRequest("status?code=500");
3145

46+
// ReSharper disable once AccessToDisposedClosure
3247
var task = () => client.ExecuteAsync<SuccessResponse>(request);
3348
await task.Should().ThrowExactlyAsync<HttpRequestException>();
3449
}

0 commit comments

Comments
 (0)