From fdd7366627c4640366dd44c2f381f72fb7a17bf8 Mon Sep 17 00:00:00 2001 From: David Eriksson Date: Tue, 28 Oct 2025 09:43:26 +0100 Subject: [PATCH 1/4] Handle nullable in RestClientException --- Activout.RestClient/RestClientException.cs | 55 +++++++++++----------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/Activout.RestClient/RestClientException.cs b/Activout.RestClient/RestClientException.cs index 66084aa..a2711d1 100644 --- a/Activout.RestClient/RestClientException.cs +++ b/Activout.RestClient/RestClientException.cs @@ -1,39 +1,40 @@ -#nullable disable using System; using System.Net; -namespace Activout.RestClient +namespace Activout.RestClient; + +public class RestClientException : Exception { - public class RestClientException : Exception + public RestClientException(Uri? requestUri, HttpStatusCode statusCode, object? errorResponse) + : base(errorResponse?.ToString()) { - public RestClientException(Uri requestUri, HttpStatusCode statusCode, object errorResponse) : base(errorResponse?.ToString()) - { - RequestUri = requestUri; - StatusCode = statusCode; - ErrorResponse = errorResponse; - } + RequestUri = requestUri; + StatusCode = statusCode; + ErrorResponse = errorResponse; + } - public RestClientException(Uri requestUri, HttpStatusCode statusCode, string errorResponse, Exception innerException) : base( - errorResponse, innerException) - { - RequestUri = requestUri; - StatusCode = statusCode; - ErrorResponse = errorResponse; - } + public RestClientException(Uri? requestUri, HttpStatusCode statusCode, string? errorResponse, + Exception? innerException) + : base(errorResponse, innerException) + { + RequestUri = requestUri; + StatusCode = statusCode; + ErrorResponse = errorResponse; + } - public Uri RequestUri { get; } - public HttpStatusCode StatusCode { get; } + public Uri? RequestUri { get; } + public HttpStatusCode StatusCode { get; } - public object ErrorResponse { get; } + public object? ErrorResponse { get; } - public T GetErrorResponse() - { - return (T)ErrorResponse; - } + public T? GetErrorResponse() + { + return (T?)ErrorResponse; + } - public override string ToString() - { - return $"{base.ToString()}, {nameof(RequestUri)}: {RequestUri}, {nameof(StatusCode)}: {StatusCode}, {nameof(ErrorResponse)}: {ErrorResponse}"; - } + public override string ToString() + { + return + $"{base.ToString()}, {nameof(RequestUri)}: {RequestUri}, {nameof(StatusCode)}: {StatusCode}, {nameof(ErrorResponse)}: {ErrorResponse}"; } } \ No newline at end of file From 8b8356b77f1527507d660b43568d8e91be92303c Mon Sep 17 00:00:00 2001 From: David Eriksson Date: Tue, 28 Oct 2025 09:55:33 +0100 Subject: [PATCH 2/4] Fix test cases --- .../RestClientTests.cs | 2 ++ .../ErrorResponseXmlTest.cs | 1 + Activout.RestClient/Part.cs | 20 ++----------------- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Activout.RestClient.Test.Json/RestClientTests.cs b/Activout.RestClient.Test.Json/RestClientTests.cs index c642a41..0bf9324 100644 --- a/Activout.RestClient.Test.Json/RestClientTests.cs +++ b/Activout.RestClient.Test.Json/RestClientTests.cs @@ -112,6 +112,7 @@ public async Task TestErrorAsync(JsonImplementation jsonImplementation) Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); var error = exception.GetErrorResponse(); + Assert.NotNull(error); Assert.Equal(34, error.Errors[0].Code); Assert.Equal("Sorry, that page does not exist", error.Errors[0].Message); } @@ -165,6 +166,7 @@ public void TestErrorSync(JsonImplementation jsonImplementation) Assert.Equal("Sorry, that page does not exist", message); var error = exception.GetErrorResponse(); + Assert.NotNull(error); Assert.Equal(34, error.Errors[0].Code); Assert.Equal("Sorry, that page does not exist", error.Errors[0].Message); } diff --git a/Activout.RestClient.Xml.Test/ErrorResponseXmlTest.cs b/Activout.RestClient.Xml.Test/ErrorResponseXmlTest.cs index 8b84a26..74d0ff9 100644 --- a/Activout.RestClient.Xml.Test/ErrorResponseXmlTest.cs +++ b/Activout.RestClient.Xml.Test/ErrorResponseXmlTest.cs @@ -51,6 +51,7 @@ public async Task TestErrorResponse_Xml_BadRequest(string mediaType) Assert.NotNull(exception.ErrorResponse); Assert.IsType(exception.ErrorResponse); var errorResponse = exception.GetErrorResponse(); + Assert.NotNull(errorResponse); Assert.Equal(400, errorResponse.Code); Assert.Equal("Invalid request parameter", errorResponse.Message); } diff --git a/Activout.RestClient/Part.cs b/Activout.RestClient/Part.cs index 8a8db93..2beaccc 100644 --- a/Activout.RestClient/Part.cs +++ b/Activout.RestClient/Part.cs @@ -1,19 +1,3 @@ -#nullable disable -namespace Activout.RestClient -{ - public class Part - { - internal object InternalContent { get; set; } - public string Name { get; set; } - public string FileName { get; set; } - } +namespace Activout.RestClient; - public class Part : Part - { - public T Content - { - get => (T)InternalContent; - set => InternalContent = value; - } - } -} \ No newline at end of file +public record Part(object Content, string? Name, string? FileName = null); From e83b850154ea79e3319a58fd99a8b8f62d0bf5f9 Mon Sep 17 00:00:00 2001 From: David Eriksson Date: Tue, 28 Oct 2025 09:59:34 +0100 Subject: [PATCH 3/4] nullable ParamConverters --- .../ParamConverter/IParamConverter.cs | 3 +-- .../DateTimeEpochParamConverter.cs | 3 +-- .../DateTimeIso8601ParamConverter.cs | 3 +-- .../Implementation/ToStringParamConverter.cs | 20 +++++++++---------- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/Activout.RestClient/ParamConverter/IParamConverter.cs b/Activout.RestClient/ParamConverter/IParamConverter.cs index 2a4ab11..712fbf8 100644 --- a/Activout.RestClient/ParamConverter/IParamConverter.cs +++ b/Activout.RestClient/ParamConverter/IParamConverter.cs @@ -1,4 +1,3 @@ -#nullable disable using System; using System.Reflection; @@ -7,6 +6,6 @@ namespace Activout.RestClient.ParamConverter public interface IParamConverter { bool CanConvert(Type type, ParameterInfo parameterInfo); - string ToString(object value); + string ToString(object? value); } } \ No newline at end of file diff --git a/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs b/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs index e6ee928..6bf1fa3 100644 --- a/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs +++ b/Activout.RestClient/ParamConverter/Implementation/DateTimeEpochParamConverter.cs @@ -1,4 +1,3 @@ -#nullable disable using System; using System.Reflection; @@ -20,7 +19,7 @@ public bool CanConvert(Type type, ParameterInfo parameterInfo) return type == typeof(DateTime); } - public string ToString(object value) + public string ToString(object? value) { return value == null ? "" : ((DateTime)value).ToUnixTime().ToString(); } diff --git a/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs b/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs index 891ec58..400b608 100644 --- a/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs +++ b/Activout.RestClient/ParamConverter/Implementation/DateTimeIso8601ParamConverter.cs @@ -1,4 +1,3 @@ -#nullable disable using System; using System.Reflection; @@ -11,7 +10,7 @@ public bool CanConvert(Type type, ParameterInfo parameterInfo) return type == typeof(DateTime); } - public string ToString(object value) + public string ToString(object? value) { return value == null ? "" : ((DateTime)value).ToString("o"); } diff --git a/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs b/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs index f71858c..e86ccb9 100644 --- a/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs +++ b/Activout.RestClient/ParamConverter/Implementation/ToStringParamConverter.cs @@ -1,19 +1,17 @@ -#nullable disable using System; using System.Reflection; -namespace Activout.RestClient.ParamConverter.Implementation +namespace Activout.RestClient.ParamConverter.Implementation; + +public class ToStringParamConverter : IParamConverter { - public class ToStringParamConverter : IParamConverter + public bool CanConvert(Type type, ParameterInfo parameterInfo) { - public bool CanConvert(Type type, ParameterInfo parameterInfo) - { - return true; - } + return true; + } - public string ToString(object value) - { - return value == null ? "" : value.ToString(); - } + public string ToString(object? value) + { + return value?.ToString() ?? ""; } } \ No newline at end of file From 72bff3431ab5d02bc73e8a36fff53e95b73d433e Mon Sep 17 00:00:00 2001 From: David Eriksson Date: Tue, 28 Oct 2025 10:01:06 +0100 Subject: [PATCH 4/4] Nullable Part and some remaining code --- .../MultipartFormDataContentTest.cs | 12 ++------ Activout.RestClient/IRestClientFactory.cs | 10 +++---- .../Implementation/RequestHandler.cs | 28 +++++++++---------- Activout.RestClient/Part.cs | 2 +- 4 files changed, 21 insertions(+), 31 deletions(-) diff --git a/Activout.RestClient.Test/MultipartFormDataContentTest.cs b/Activout.RestClient.Test/MultipartFormDataContentTest.cs index dd40394..d381093 100644 --- a/Activout.RestClient.Test/MultipartFormDataContentTest.cs +++ b/Activout.RestClient.Test/MultipartFormDataContentTest.cs @@ -82,16 +82,8 @@ await client.SendFormInForm(new FormModel MyString = "foobar" }, new[] { - new Part - { - Content = "foo", - FileName = "foo.txt" - }, - new Part - { - Content = "bar", - FileName = "bar.txt" - } + new Part(Content: "foo", FileName: "foo.txt"), + new Part(Content: "bar", FileName: "bar.txt") }); // Assert diff --git a/Activout.RestClient/IRestClientFactory.cs b/Activout.RestClient/IRestClientFactory.cs index 98a0efd..141bd6b 100644 --- a/Activout.RestClient/IRestClientFactory.cs +++ b/Activout.RestClient/IRestClientFactory.cs @@ -1,8 +1,6 @@ -#nullable disable -namespace Activout.RestClient +namespace Activout.RestClient; + +public interface IRestClientFactory { - public interface IRestClientFactory - { - IRestClientBuilder CreateBuilder(); - } + IRestClientBuilder CreateBuilder(); } \ No newline at end of file diff --git a/Activout.RestClient/Implementation/RequestHandler.cs b/Activout.RestClient/Implementation/RequestHandler.cs index 2b5ae82..6db511b 100644 --- a/Activout.RestClient/Implementation/RequestHandler.cs +++ b/Activout.RestClient/Implementation/RequestHandler.cs @@ -1,7 +1,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; @@ -16,6 +15,8 @@ namespace Activout.RestClient.Implementation; +internal record HttpContentPart(HttpContent Content, string Name, string? FileName); + internal class RequestHandler { // https://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1 @@ -210,7 +211,7 @@ private void PrepareRequestMessage(HttpRequestMessage request) var routeParams = new Dictionary(); var queryParams = new List(); var formParams = new List>(); - var partParams = new List>(); + var partParams = new List(); var cancellationToken = GetParams(args, routeParams, queryParams, formParams, headers, partParams); var requestUriString = ExpandTemplate(routeParams); @@ -251,7 +252,7 @@ private void PrepareRequestMessage(HttpRequestMessage request) } private static MultipartFormDataContent CreateMultipartFormDataContent( - IEnumerable> partParams) + IEnumerable partParams) { var content = new MultipartFormDataContent(); foreach (var part in partParams) @@ -293,7 +294,7 @@ private CancellationToken GetParams( List queryParams, List> formParams, List> headers, - List> parts) + List parts) { var cancellationToken = CancellationToken.None; @@ -430,7 +431,7 @@ private CancellationToken GetParams( return cancellationToken; } - private IEnumerable> GetPartNameAndHttpContent(PartParamAttribute partAttribute, + private IEnumerable GetPartNameAndHttpContent(PartParamAttribute partAttribute, string parameterName, object? rawValue) { @@ -439,19 +440,17 @@ private IEnumerable> GetPartNameAndHttpContent(PartParamAttrib if (rawValue is Part part) { - rawValue = part.InternalContent; + rawValue = part.Content; partName = part.Name; fileName = part.FileName; } - if (rawValue is { }) + if (rawValue is not null) { - yield return new Part - { - Content = GetPartHttpContent(partAttribute, rawValue), - Name = partName ?? partAttribute.Name ?? parameterName, - FileName = fileName ?? partAttribute.FileName - }; + yield return new HttpContentPart( + Content: GetPartHttpContent(partAttribute, rawValue), + Name: partName ?? partAttribute.Name ?? parameterName, + FileName: fileName ?? partAttribute.FileName); } } @@ -481,7 +480,8 @@ private static HttpContent GetHttpContent(ISerializer serializer, object? value, } - private async Task SendRequestAndHandleResponse(HttpRequestMessage request, CancellationToken cancellationToken) + private async Task SendRequestAndHandleResponse(HttpRequestMessage request, + CancellationToken cancellationToken) { var response = await SendRequest(request, cancellationToken); return await HandleResponse(request, response); diff --git a/Activout.RestClient/Part.cs b/Activout.RestClient/Part.cs index 2beaccc..fffafff 100644 --- a/Activout.RestClient/Part.cs +++ b/Activout.RestClient/Part.cs @@ -1,3 +1,3 @@ namespace Activout.RestClient; -public record Part(object Content, string? Name, string? FileName = null); +public record Part(object Content, string? Name = null, string? FileName = null);