Skip to content

Commit 8e96bba

Browse files
Model binding (#1681)
* Fixing the way how body is added * Content fixes * Wrong multipart form without a need * Fix the parameter test
1 parent fc995f9 commit 8e96bba

21 files changed

+353
-329
lines changed

src/RestSharp/Extensions/MiscExtensions.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,39 @@ public static async Task<byte[]> ReadAsBytes(this Stream input, CancellationToke
3939

4040
return ms.ToArray();
4141
}
42+
43+
internal static IEnumerable<(string Name, object Value)> GetProperties(this object obj, params string[] includedProperties) {
44+
// automatically create parameters from object props
45+
var type = obj.GetType();
46+
var props = type.GetProperties();
47+
48+
foreach (var prop in props) {
49+
if (!IsAllowedProperty(prop.Name))
50+
continue;
51+
52+
var val = prop.GetValue(obj, null);
53+
54+
if (val == null)
55+
continue;
56+
57+
var propType = prop.PropertyType;
58+
59+
if (propType.IsArray) {
60+
var elementType = propType.GetElementType();
61+
var array = (Array)val;
62+
63+
if (array.Length > 0 && elementType != null) {
64+
// convert the array to an array of strings
65+
var values = array.Cast<object>().Select(item => item.ToString());
66+
67+
val = string.Join(",", values);
68+
}
69+
}
70+
71+
yield return(prop.Name, val);
72+
}
73+
74+
bool IsAllowedProperty(string propertyName)
75+
=> includedProperties.Length == 0 || includedProperties.Length > 0 && includedProperties.Contains(propertyName);
76+
}
4277
}

src/RestSharp/Parameters/BodyParameter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace RestSharp;
1717

1818
public record BodyParameter : Parameter {
1919
public BodyParameter(string? name, object value, string contentType, DataFormat dataFormat = DataFormat.None)
20-
: base(name, Ensure.NotNull(value, nameof(value)), ParameterType.RequestBody) {
20+
: base(name, Ensure.NotNull(value, nameof(value)), ParameterType.RequestBody, false) {
2121
ContentType = contentType;
2222
DataFormat = dataFormat;
2323
}

src/RestSharp/Parameters/FileParameter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Stream GetFile() {
7777
/// <param name="getFile">Delegate that will be called with the request stream so you can write to it..</param>
7878
/// <param name="contentLength">The length of the data that will be written by te writer.</param>
7979
/// <param name="fileName">The filename to use in the request.</param>
80-
/// <param name="contentType">Optional: parameter content type</param>
80+
/// <param name="contentType">Optional: parameter content type, default is "application/g-zip"</param>
8181
/// <returns>The <see cref="FileParameter" /> using the default content type.</returns>
8282
public static FileParameter Create(
8383
string name,
@@ -86,7 +86,7 @@ public static FileParameter Create(
8686
string fileName,
8787
string? contentType = null
8888
)
89-
=> new(name, fileName, contentLength, getFile, contentType);
89+
=> new(name, fileName, contentLength, getFile, contentType ?? Serializers.ContentType.File);
9090

9191
public static FileParameter FromFile(string fullPath, string? name = null, string? contentType = null) {
9292
if (!File.Exists(Ensure.NotEmptyString(fullPath, nameof(fullPath))))

src/RestSharp/Parameters/HeaderParameter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@
1616
namespace RestSharp;
1717

1818
public record HeaderParameter : Parameter {
19-
public HeaderParameter(string? name, object? value, bool encode = false) : base(name, value, ParameterType.HttpHeader, encode) { }
19+
public HeaderParameter(string? name, object? value) : base(name, value, ParameterType.HttpHeader, false) { }
2020
}

src/RestSharp/Parameters/Parameter.cs

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,7 @@ namespace RestSharp;
1717
/// <summary>
1818
/// Parameter container for REST requests
1919
/// </summary>
20-
public record Parameter {
21-
protected Parameter(string? name, object? value, ParameterType type, bool encode = true) {
22-
Name = name;
23-
Value = value;
24-
Type = type;
25-
Encode = encode;
26-
}
27-
28-
// Parameter(string name, object value, string contentType, ParameterType type, bool encode = true) : this(name, value, type, encode)
29-
// => ContentType = contentType;
30-
31-
/// <summary>
32-
/// Name of the parameter
33-
/// </summary>
34-
public string? Name { get; }
35-
36-
/// <summary>
37-
/// Value of the parameter
38-
/// </summary>
39-
public object? Value { get; }
40-
41-
/// <summary>
42-
/// Type of the parameter
43-
/// </summary>
44-
public ParameterType Type { get; }
45-
46-
internal bool Encode { get; }
47-
20+
public abstract record Parameter(string? Name, object? Value, ParameterType Type, bool Encode) {
4821
/// <summary>
4922
/// MIME content type of the parameter
5023
/// </summary>
@@ -60,7 +33,7 @@ public static Parameter CreateParameter(string? name, object value, ParameterTyp
6033
=> type switch {
6134
ParameterType.GetOrPost => new GetOrPostParameter(name!, value, encode),
6235
ParameterType.UrlSegment => new UrlSegmentParameter(name!, value, encode),
63-
ParameterType.HttpHeader => new HeaderParameter(name, value, encode),
36+
ParameterType.HttpHeader => new HeaderParameter(name, value),
6437
ParameterType.RequestBody => new BodyParameter(name, value, Serializers.ContentType.Plain),
6538
ParameterType.QueryString => new QueryParameter(name!, value, encode),
6639
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)

src/RestSharp/Parameters/ParametersCollection.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,21 @@ public bool Exists(Parameter parameter)
5050

5151
internal ParametersCollection GetParameters(ParameterType parameterType) => new(_parameters.Where(x => x.Type == parameterType));
5252

53-
internal ParametersCollection GetQueryParameters(Method method)
54-
=> new(
55-
method is not Method.Post and not Method.Put and not Method.Patch
56-
? _parameters
57-
.Where(
58-
p => p.Type is ParameterType.GetOrPost or ParameterType.QueryString
59-
)
60-
: _parameters
61-
.Where(
62-
p => p.Type is ParameterType.QueryString
63-
)
64-
);
53+
internal ParametersCollection GetParameters<T>() => new(_parameters.Where(x => x is T));
54+
55+
internal ParametersCollection GetQueryParameters(Method method) {
56+
Func<Parameter, bool> condition =
57+
!IsPostStyle(method)
58+
? p => p.Type is ParameterType.GetOrPost or ParameterType.QueryString
59+
: p => p.Type is ParameterType.QueryString;
60+
61+
return new ParametersCollection(_parameters.Where(p => condition(p)));
62+
}
63+
64+
internal ParametersCollection? GetContentParameters(Method method)
65+
=> !IsPostStyle(method) ? null : new ParametersCollection(GetParameters<GetOrPostParameter>());
66+
67+
static bool IsPostStyle(Method method) => method is Method.Post or Method.Put or Method.Patch;
6568

6669
public IEnumerator<Parameter> GetEnumerator() => _parameters.GetEnumerator();
6770

src/RestSharp/Parameters/UrlSegmentParameter.cs

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

16-
namespace RestSharp;
16+
namespace RestSharp;
1717

1818
public record UrlSegmentParameter : NamedParameter {
19-
public UrlSegmentParameter(string name, object? value, bool encode = true)
20-
: base(name, value?.ToString()?.Replace("%2F", "/").Replace("%2f", "/"), ParameterType.UrlSegment, encode) { }
19+
public UrlSegmentParameter(string name, object value, bool encode = true)
20+
: base(name, Ensure.NotEmptyString(value, nameof(value)).Replace("%2F", "/").Replace("%2f", "/"), ParameterType.UrlSegment, encode) { }
2121
}

src/RestSharp/Request/BodyExtensions.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ public static bool TryGetBodyParameter(this RestRequest request, out BodyParamet
2121
return bodyParameter != null;
2222
}
2323

24-
public static Parameter[] GetPostParameters(this RestRequest request)
25-
=> request.Parameters.Where(x => x.Type == ParameterType.GetOrPost).ToArray();
26-
27-
public static bool HasPostParameters(this RestRequest request) => request.Parameters.Any(x => x.Type == ParameterType.GetOrPost);
28-
2924
public static bool HasFiles(this RestRequest request) => request.Files.Count > 0;
25+
26+
public static bool IsEmpty(this ParametersCollection? parameters) => parameters == null || parameters.Count == 0;
3027
}

src/RestSharp/Request/HttpRequestMessageExtensions.cs

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

2020
static class HttpRequestMessageExtensions {
21-
public static void AddHeaders(this HttpRequestMessage message, ParametersCollection parameters, Func<string, string> encode) {
22-
var headerParameters = parameters
23-
.GetParameters(ParameterType.HttpHeader)
24-
.Where(x => !RequestContent.ContentHeaders.Contains(x.Name));
21+
public static void AddHeaders(this HttpRequestMessage message, RequestHeaders headers) {
22+
var headerParameters = headers.Parameters.Where(x => !RequestContent.ContentHeaders.Contains(x.Name));
2523

2624
headerParameters.ForEach(AddHeader);
2725

2826
void AddHeader(Parameter parameter) {
2927
var parameterStringValue = parameter.Value!.ToString();
3028

31-
if (parameter.Encode) parameterStringValue = encode(parameterStringValue!);
32-
3329
message.Headers.Remove(parameter.Name!);
3430
message.Headers.TryAddWithoutValidation(parameter.Name!, parameterStringValue);
3531
}

src/RestSharp/Request/RequestContent.cs

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ public RequestContent(RestClient client, RestRequest request) {
3939

4040
public HttpContent BuildContent() {
4141
AddFiles();
42-
AddBody();
43-
AddPostParameters();
42+
var postParameters = _request.Parameters.GetContentParameters(_request.Method);
43+
AddBody(!postParameters.IsEmpty());
44+
AddPostParameters(postParameters);
4445
AddHeaders();
4546
return Content!;
4647
}
@@ -95,13 +96,13 @@ static bool BodyShouldBeMultipartForm(BodyParameter bodyParameter) {
9596
return bodyParameter.Name.IsNotEmpty() && bodyParameter.Name != bodyContentType;
9697
}
9798

98-
void AddBody() {
99+
void AddBody(bool hasPostParameters) {
99100
if (!_request.TryGetBodyParameter(out var bodyParameter)) return;
100101

101102
var bodyContent = Serialize(bodyParameter!);
102103

103104
// we need to send the body
104-
if (_request.HasPostParameters() || _request.HasFiles() || BodyShouldBeMultipartForm(bodyParameter!)) {
105+
if (hasPostParameters || _request.HasFiles() || BodyShouldBeMultipartForm(bodyParameter!)) {
105106
// here we must use multipart form data
106107
var mpContent = Content as MultipartFormDataContent ?? new MultipartFormDataContent();
107108

@@ -117,29 +118,20 @@ void AddBody() {
117118
}
118119
}
119120

120-
void AddPostParameters() {
121-
var postParameters = _request.GetPostParameters();
122-
if (postParameters.Length <= 0) return;
123-
124-
// it's a form
125-
if (Content is MultipartFormDataContent mpContent) {
126-
// we got the multipart form already instantiated, just add parameters to it
127-
foreach (var postParameter in postParameters) {
128-
mpContent.Add(
129-
new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType),
130-
postParameter.Name!
131-
);
132-
}
133-
}
134-
else {
135-
// we should not have anything else except the parameters, so we send them as form URL encoded
136-
var formContent = new FormUrlEncodedContent(
137-
_request.Parameters
138-
.Where(x => x.Type == ParameterType.GetOrPost)
139-
.Select(x => new KeyValuePair<string, string>(x.Name!, x.Value!.ToString()!))!
121+
void AddPostParameters(ParametersCollection? postParameters) {
122+
if (postParameters.IsEmpty()) return;
123+
124+
var mpContent = Content as MultipartFormDataContent ?? new MultipartFormDataContent();
125+
126+
// we got the multipart form already instantiated, just add parameters to it
127+
foreach (var postParameter in postParameters!) {
128+
mpContent.Add(
129+
new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType),
130+
postParameter.Name!
140131
);
141-
Content = formContent;
142132
}
133+
134+
Content = mpContent;
143135
}
144136

145137
void AddHeaders() {

src/RestSharp/Request/RequestParameters.cs renamed to src/RestSharp/Request/RequestHeaders.cs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,16 @@
1515

1616
namespace RestSharp;
1717

18-
class RequestParameters {
19-
static readonly ParameterType[] MultiParameterTypes = { ParameterType.QueryString, ParameterType.GetOrPost };
20-
18+
class RequestHeaders {
2119
public ParametersCollection Parameters { get; } = new();
2220

23-
public RequestParameters AddParameters(ParametersCollection parameters, bool allowSameName) {
24-
Parameters.AddParameters(GetParameters(parameters, allowSameName));
21+
public RequestHeaders AddHeaders(ParametersCollection parameters) {
22+
Parameters.AddParameters(parameters.GetParameters<HeaderParameter>());
2523
return this;
2624
}
2725

28-
IEnumerable<Parameter> GetParameters(ParametersCollection parametersCollection, bool allowSameName) {
29-
foreach (var parameter in parametersCollection) {
30-
var parameterExists = Parameters.Exists(parameter);
31-
32-
if (allowSameName) {
33-
var isMultiParameter = MultiParameterTypes.Any(pt => pt == parameter.Type);
34-
parameterExists = !isMultiParameter && parameterExists;
35-
}
36-
37-
if (!parameterExists) yield return parameter;
38-
}
39-
}
40-
4126
// Add Accept header based on registered deserializers if none has been set by the caller.
42-
public RequestParameters AddAcceptHeader(string[] acceptedContentTypes) {
27+
public RequestHeaders AddAcceptHeader(string[] acceptedContentTypes) {
4328
if (Parameters.TryFind(KnownHeaders.Accept) == null) {
4429
var accepts = string.Join(", ", acceptedContentTypes);
4530
Parameters.AddParameter(new HeaderParameter(KnownHeaders.Accept, accepts));

src/RestSharp/Request/RestRequest.cs

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

1515
using RestSharp.Extensions;
16+
// ReSharper disable UnusedAutoPropertyAccessor.Global
1617

1718
namespace RestSharp;
1819

@@ -26,15 +27,11 @@ public class RestRequest {
2627
/// <summary>
2728
/// Default constructor
2829
/// </summary>
29-
public RestRequest() {
30-
RequestFormat = DataFormat.Json;
31-
Method = Method.Get;
32-
}
30+
public RestRequest() => Method = Method.Get;
3331

34-
public RestRequest(string? resource, Method method = Method.Get, DataFormat dataFormat = DataFormat.Json) : this() {
35-
Resource = resource ?? "";
36-
Method = method;
37-
RequestFormat = dataFormat;
32+
public RestRequest(string? resource, Method method = Method.Get) : this() {
33+
Resource = resource ?? "";
34+
Method = method;
3835

3936
if (string.IsNullOrWhiteSpace(resource)) return;
4037

@@ -61,17 +58,11 @@ static IEnumerable<KeyValuePair<string, string>> ParseQuery(string query)
6158
);
6259
}
6360

64-
public RestRequest(Uri resource, Method method = Method.Get, DataFormat dataFormat = DataFormat.Json)
65-
: this(
66-
resource.IsAbsoluteUri
67-
? resource.AbsoluteUri
68-
: resource.OriginalString,
69-
method,
70-
dataFormat
71-
) { }
61+
public RestRequest(Uri resource, Method method = Method.Get)
62+
: this(resource.IsAbsoluteUri ? resource.AbsoluteUri : resource.OriginalString, method) { }
7263

7364
// readonly List<Parameter> _parameters = new();
74-
readonly List<FileParameter> _files = new();
65+
readonly List<FileParameter> _files = new();
7566

7667
/// <summary>
7768
/// Always send a multipart/form-data request - even when no Files are present.

0 commit comments

Comments
 (0)