diff --git a/src/RestSharp/Request/RequestContent.cs b/src/RestSharp/Request/RequestContent.cs
index 9df44d6a9..9715ccb1e 100644
--- a/src/RestSharp/Request/RequestContent.cs
+++ b/src/RestSharp/Request/RequestContent.cs
@@ -16,6 +16,7 @@
using System.Runtime.Serialization;
using RestSharp.Extensions;
using static RestSharp.KnownHeaders;
+// ReSharper disable InvertIf
// ReSharper disable SuggestBaseTypeForParameter
@@ -153,9 +154,10 @@ void AddPostParameters(ParametersCollection? postParameters) {
if (Content is MultipartFormDataContent mpContent) {
// we got the multipart form already instantiated, just add parameters to it
foreach (var postParameter in postParameters!) {
+ var parameterName = postParameter.Name!;
mpContent.Add(
new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType),
- postParameter.Name!
+ _request.MultipartFormQuoteParameters ? $"\"{parameterName}\"" : parameterName
);
}
}
diff --git a/src/RestSharp/Request/RestRequest.cs b/src/RestSharp/Request/RestRequest.cs
index 9a0c0f45f..bf6fbb5c5 100644
--- a/src/RestSharp/Request/RestRequest.cs
+++ b/src/RestSharp/Request/RestRequest.cs
@@ -68,6 +68,13 @@ public RestRequest(Uri resource, Method method = Method.Get)
///
public bool AlwaysMultipartFormData { get; set; }
+ ///
+ /// When set to true, parameters in a multipart form data requests will be enclosed in
+ /// quotation marks. Default is false. Enable it if the remote endpoint requires parameters
+ /// to be in quotes (for example, FreshDesk API).
+ ///
+ public bool MultipartFormQuoteParameters { get; set; }
+
public string? FormBoundary { get; set; }
///
diff --git a/src/RestSharp/Response/RestResponse.cs b/src/RestSharp/Response/RestResponse.cs
index 2d97cf11c..b1b87bb10 100644
--- a/src/RestSharp/Response/RestResponse.cs
+++ b/src/RestSharp/Response/RestResponse.cs
@@ -61,11 +61,12 @@ public static RestResponse FromResponse(RestResponse response)
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + "()}")]
public class RestResponse : RestResponseBase {
internal static async Task FromHttpResponse(
- HttpResponseMessage httpResponse,
- RestRequest request,
- Encoding encoding,
- CookieCollection cookieCollection,
- CancellationToken cancellationToken
+ HttpResponseMessage httpResponse,
+ RestRequest request,
+ Encoding encoding,
+ CookieCollection cookieCollection,
+ CalculateResponseStatus calculateResponseStatus,
+ CancellationToken cancellationToken
) {
return request.AdvancedResponseWriter?.Invoke(httpResponse) ?? await GetDefaultResponse().ConfigureAwait(false);
@@ -78,7 +79,7 @@ async Task GetDefaultResponse() {
#endif
var bytes = stream == null ? null : await stream.ReadAsBytes(cancellationToken).ConfigureAwait(false);
- var content = bytes == null ? null : httpResponse.GetResponseString(bytes, encoding);
+ var content = bytes == null ? null : httpResponse.GetResponseString(bytes, encoding);
return new RestResponse {
Content = content,
@@ -87,7 +88,7 @@ async Task GetDefaultResponse() {
Version = httpResponse.RequestMessage?.Version,
ContentLength = httpResponse.Content.Headers.ContentLength,
ContentType = httpResponse.Content.Headers.ContentType?.MediaType,
- ResponseStatus = httpResponse.IsSuccessStatusCode ? ResponseStatus.Completed : ResponseStatus.Error,
+ ResponseStatus = calculateResponseStatus(httpResponse),
ErrorException = MaybeException(),
ResponseUri = httpResponse.RequestMessage!.RequestUri,
Server = httpResponse.Headers.Server.ToString(),
@@ -122,4 +123,6 @@ async Task GetDefaultResponse() {
}
}
}
-}
\ No newline at end of file
+}
+
+public delegate ResponseStatus CalculateResponseStatus(HttpResponseMessage httpResponse);
diff --git a/src/RestSharp/RestClient.Async.cs b/src/RestSharp/RestClient.Async.cs
index f49a48f46..3b162c42e 100644
--- a/src/RestSharp/RestClient.Async.cs
+++ b/src/RestSharp/RestClient.Async.cs
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using RestSharp.Extensions;
-
namespace RestSharp;
public partial class RestClient {
@@ -33,6 +31,7 @@ public async Task ExecuteAsync(RestRequest request, CancellationTo
request,
Options.Encoding,
CookieContainer.GetCookies(internalResponse.Url),
+ CalculateResponseStatus,
cancellationToken
)
.ConfigureAwait(false)
diff --git a/src/RestSharp/RestClient.cs b/src/RestSharp/RestClient.cs
index e55f63c3b..a2d0b44c2 100644
--- a/src/RestSharp/RestClient.cs
+++ b/src/RestSharp/RestClient.cs
@@ -35,6 +35,14 @@ public partial class RestClient : IDisposable {
///
public string[] AcceptedContentTypes { get; set; } = null!;
+ ///
+ /// Function to calculate the response status. By default, the status will be Completed if it was successful, or NotFound.
+ ///
+ public CalculateResponseStatus CalculateResponseStatus { get; set; } = httpResponse
+ => httpResponse.IsSuccessStatusCode || httpResponse.StatusCode == HttpStatusCode.NotFound
+ ? ResponseStatus.Completed
+ : ResponseStatus.Error;
+
HttpClient HttpClient { get; }
internal RestClientOptions Options { get; }
@@ -103,8 +111,7 @@ public RestClient(HttpClient httpClient, RestClientOptions? options = null, bool
public RestClient(HttpMessageHandler handler, bool disposeHandler = true) : this(new HttpClient(handler, disposeHandler), null, true) { }
void ConfigureHttpClient(HttpClient httpClient) {
- if (Options.Timeout > 0)
- httpClient.Timeout = TimeSpan.FromMilliseconds(Options.Timeout);
+ if (Options.Timeout > 0) httpClient.Timeout = TimeSpan.FromMilliseconds(Options.Timeout);
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Options.UserAgent);
}
@@ -126,8 +133,7 @@ void ConfigureHttpMessageHandler(HttpClientHandler handler) {
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
}
- if (Options.MaxRedirects.HasValue)
- handler.MaxAutomaticRedirections = Options.MaxRedirects.Value;
+ if (Options.MaxRedirects.HasValue) handler.MaxAutomaticRedirections = Options.MaxRedirects.Value;
}
internal Func Encode { get; set; } = s => s.UrlEncode();
@@ -153,7 +159,7 @@ public RestClient AddDefaultParameter(Parameter parameter) {
);
if (!Options.AllowMultipleDefaultParametersWithSameName &&
- !MultiParameterTypes.Contains(parameter.Type) &&
+ !MultiParameterTypes.Contains(parameter.Type) &&
DefaultParameters.Any(x => x.Name == parameter.Name)) {
throw new ArgumentException("A default parameters with the same name has already been added", nameof(parameter));
}
@@ -212,4 +218,4 @@ public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
-}
\ No newline at end of file
+}
diff --git a/test/RestSharp.Tests.Integrated/RequestFailureTests.cs b/test/RestSharp.Tests.Integrated/RequestFailureTests.cs
index e162138a5..3033a3607 100644
--- a/test/RestSharp.Tests.Integrated/RequestFailureTests.cs
+++ b/test/RestSharp.Tests.Integrated/RequestFailureTests.cs
@@ -34,7 +34,7 @@ public async Task Handles_GET_Request_Errors_With_Response_Type() {
[Fact]
public async Task Throws_on_unsuccessful_call() {
var client = new RestClient(new RestClientOptions(_fixture.Server.Url) { ThrowOnAnyError = true });
- var request = new RestRequest("status?code=404");
+ var request = new RestRequest("status?code=500");
var task = () => client.ExecuteAsync(request);
await task.Should().ThrowExactlyAsync();
@@ -42,20 +42,37 @@ public async Task Throws_on_unsuccessful_call() {
[Fact]
public async Task GetAsync_throws_on_unsuccessful_call() {
- var request = new RestRequest("status?code=404");
+ var request = new RestRequest("status?code=500");
var task = () => _client.GetAsync(request);
await task.Should().ThrowExactlyAsync();
}
[Fact]
- public async Task GetAsync_generic_throws_on_unsuccessful_call() {
+ public async Task GetAsync_completes_on_404() {
var request = new RestRequest("status?code=404");
+ var response = await _client.GetAsync(request);
+ response.StatusCode.Should().Be(HttpStatusCode.NotFound);
+ response.ResponseStatus.Should().Be(ResponseStatus.Completed);
+ }
+
+ [Fact]
+ public async Task GetAsync_generic_throws_on_unsuccessful_call() {
+ var request = new RestRequest("status?code=500");
+
var task = () => _client.GetAsync(request);
await task.Should().ThrowExactlyAsync();
}
+ [Fact]
+ public async Task GetAsync_returns_null_on_404() {
+ var request = new RestRequest("status?code=404");
+
+ var response = await _client.GetAsync(request);
+ response.Should().BeNull();
+ }
+
class Response {
public string Message { get; set; }
}
diff --git a/test/RestSharp.Tests/UrlBuilderTests.cs b/test/RestSharp.Tests/UrlBuilderTests.cs
index 9421d115c..bdd336c2d 100644
--- a/test/RestSharp.Tests/UrlBuilderTests.cs
+++ b/test/RestSharp.Tests/UrlBuilderTests.cs
@@ -357,4 +357,15 @@ public void Should_update_parameter_if_it_already_exists() {
Assert.Equal(expected, output);
}
-}
\ No newline at end of file
+
+ [Fact]
+ public void Should_use_ipv6_address() {
+ var baseUrl = new Uri("https://[fe80::290:e8ff:fe8b:2537%en10]:8443");
+ var client = new RestClient(baseUrl);
+ var request = new RestRequest("api/v1/auth");
+ var actual = client.BuildUri(request);
+
+ actual.HostNameType.Should().Be(UriHostNameType.IPv6);
+ actual.AbsoluteUri.Should().Be("https://[fe80::290:e8ff:fe8b:2537]:8443/api/v1/auth");
+ }
+}