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
35 changes: 35 additions & 0 deletions src/RestSharp/Extensions/MiscExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,39 @@ public static async Task<byte[]> ReadAsBytes(this Stream input, CancellationToke

return ms.ToArray();
}

internal static IEnumerable<(string Name, object Value)> GetProperties(this object obj, params string[] includedProperties) {
// automatically create parameters from object props
var type = obj.GetType();
var props = type.GetProperties();

foreach (var prop in props) {
if (!IsAllowedProperty(prop.Name))
continue;

var val = prop.GetValue(obj, null);

if (val == null)
continue;

var propType = prop.PropertyType;

if (propType.IsArray) {
var elementType = propType.GetElementType();
var array = (Array)val;

if (array.Length > 0 && elementType != null) {
// convert the array to an array of strings
var values = array.Cast<object>().Select(item => item.ToString());

val = string.Join(",", values);
}
}

yield return(prop.Name, val);
}

bool IsAllowedProperty(string propertyName)
=> includedProperties.Length == 0 || includedProperties.Length > 0 && includedProperties.Contains(propertyName);
}
}
2 changes: 1 addition & 1 deletion src/RestSharp/Parameters/BodyParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace RestSharp;

public record BodyParameter : Parameter {
public BodyParameter(string? name, object value, string contentType, DataFormat dataFormat = DataFormat.None)
: base(name, Ensure.NotNull(value, nameof(value)), ParameterType.RequestBody) {
: base(name, Ensure.NotNull(value, nameof(value)), ParameterType.RequestBody, false) {
ContentType = contentType;
DataFormat = dataFormat;
}
Expand Down
4 changes: 2 additions & 2 deletions src/RestSharp/Parameters/FileParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Stream GetFile() {
/// <param name="getFile">Delegate that will be called with the request stream so you can write to it..</param>
/// <param name="contentLength">The length of the data that will be written by te writer.</param>
/// <param name="fileName">The filename to use in the request.</param>
/// <param name="contentType">Optional: parameter content type</param>
/// <param name="contentType">Optional: parameter content type, default is "application/g-zip"</param>
/// <returns>The <see cref="FileParameter" /> using the default content type.</returns>
public static FileParameter Create(
string name,
Expand All @@ -86,7 +86,7 @@ public static FileParameter Create(
string fileName,
string? contentType = null
)
=> new(name, fileName, contentLength, getFile, contentType);
=> new(name, fileName, contentLength, getFile, contentType ?? Serializers.ContentType.File);

public static FileParameter FromFile(string fullPath, string? name = null, string? contentType = null) {
if (!File.Exists(Ensure.NotEmptyString(fullPath, nameof(fullPath))))
Expand Down
2 changes: 1 addition & 1 deletion src/RestSharp/Parameters/HeaderParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
namespace RestSharp;

public record HeaderParameter : Parameter {
public HeaderParameter(string? name, object? value, bool encode = false) : base(name, value, ParameterType.HttpHeader, encode) { }
public HeaderParameter(string? name, object? value) : base(name, value, ParameterType.HttpHeader, false) { }
}
31 changes: 2 additions & 29 deletions src/RestSharp/Parameters/Parameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,7 @@ namespace RestSharp;
/// <summary>
/// Parameter container for REST requests
/// </summary>
public record Parameter {
protected Parameter(string? name, object? value, ParameterType type, bool encode = true) {
Name = name;
Value = value;
Type = type;
Encode = encode;
}

// Parameter(string name, object value, string contentType, ParameterType type, bool encode = true) : this(name, value, type, encode)
// => ContentType = contentType;

/// <summary>
/// Name of the parameter
/// </summary>
public string? Name { get; }

/// <summary>
/// Value of the parameter
/// </summary>
public object? Value { get; }

/// <summary>
/// Type of the parameter
/// </summary>
public ParameterType Type { get; }

internal bool Encode { get; }

public abstract record Parameter(string? Name, object? Value, ParameterType Type, bool Encode) {
/// <summary>
/// MIME content type of the parameter
/// </summary>
Expand All @@ -60,7 +33,7 @@ public static Parameter CreateParameter(string? name, object value, ParameterTyp
=> type switch {
ParameterType.GetOrPost => new GetOrPostParameter(name!, value, encode),
ParameterType.UrlSegment => new UrlSegmentParameter(name!, value, encode),
ParameterType.HttpHeader => new HeaderParameter(name, value, encode),
ParameterType.HttpHeader => new HeaderParameter(name, value),
ParameterType.RequestBody => new BodyParameter(name, value, Serializers.ContentType.Plain),
ParameterType.QueryString => new QueryParameter(name!, value, encode),
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
Expand Down
27 changes: 15 additions & 12 deletions src/RestSharp/Parameters/ParametersCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,21 @@ public bool Exists(Parameter parameter)

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

internal ParametersCollection GetQueryParameters(Method method)
=> new(
method is not Method.Post and not Method.Put and not Method.Patch
? _parameters
.Where(
p => p.Type is ParameterType.GetOrPost or ParameterType.QueryString
)
: _parameters
.Where(
p => p.Type is ParameterType.QueryString
)
);
internal ParametersCollection GetParameters<T>() => new(_parameters.Where(x => x is T));

internal ParametersCollection GetQueryParameters(Method method) {
Func<Parameter, bool> condition =
!IsPostStyle(method)
? p => p.Type is ParameterType.GetOrPost or ParameterType.QueryString
: p => p.Type is ParameterType.QueryString;

return new ParametersCollection(_parameters.Where(p => condition(p)));
}

internal ParametersCollection? GetContentParameters(Method method)
=> !IsPostStyle(method) ? null : new ParametersCollection(GetParameters<GetOrPostParameter>());

static bool IsPostStyle(Method method) => method is Method.Post or Method.Put or Method.Patch;

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

Expand Down
6 changes: 3 additions & 3 deletions src/RestSharp/Parameters/UrlSegmentParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
// limitations under the License.
//

namespace RestSharp;
namespace RestSharp;

public record UrlSegmentParameter : NamedParameter {
public UrlSegmentParameter(string name, object? value, bool encode = true)
: base(name, value?.ToString()?.Replace("%2F", "/").Replace("%2f", "/"), ParameterType.UrlSegment, encode) { }
public UrlSegmentParameter(string name, object value, bool encode = true)
: base(name, Ensure.NotEmptyString(value, nameof(value)).Replace("%2F", "/").Replace("%2f", "/"), ParameterType.UrlSegment, encode) { }
}
7 changes: 2 additions & 5 deletions src/RestSharp/Request/BodyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ public static bool TryGetBodyParameter(this RestRequest request, out BodyParamet
return bodyParameter != null;
}

public static Parameter[] GetPostParameters(this RestRequest request)
=> request.Parameters.Where(x => x.Type == ParameterType.GetOrPost).ToArray();

public static bool HasPostParameters(this RestRequest request) => request.Parameters.Any(x => x.Type == ParameterType.GetOrPost);

public static bool HasFiles(this RestRequest request) => request.Files.Count > 0;

public static bool IsEmpty(this ParametersCollection? parameters) => parameters == null || parameters.Count == 0;
}
8 changes: 2 additions & 6 deletions src/RestSharp/Request/HttpRequestMessageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,14 @@
namespace RestSharp;

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

headerParameters.ForEach(AddHeader);

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

if (parameter.Encode) parameterStringValue = encode(parameterStringValue!);

message.Headers.Remove(parameter.Name!);
message.Headers.TryAddWithoutValidation(parameter.Name!, parameterStringValue);
}
Expand Down
42 changes: 17 additions & 25 deletions src/RestSharp/Request/RequestContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ public RequestContent(RestClient client, RestRequest request) {

public HttpContent BuildContent() {
AddFiles();
AddBody();
AddPostParameters();
var postParameters = _request.Parameters.GetContentParameters(_request.Method);
AddBody(!postParameters.IsEmpty());
AddPostParameters(postParameters);
AddHeaders();
return Content!;
}
Expand Down Expand Up @@ -95,13 +96,13 @@ static bool BodyShouldBeMultipartForm(BodyParameter bodyParameter) {
return bodyParameter.Name.IsNotEmpty() && bodyParameter.Name != bodyContentType;
}

void AddBody() {
void AddBody(bool hasPostParameters) {
if (!_request.TryGetBodyParameter(out var bodyParameter)) return;

var bodyContent = Serialize(bodyParameter!);

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

Expand All @@ -117,29 +118,20 @@ void AddBody() {
}
}

void AddPostParameters() {
var postParameters = _request.GetPostParameters();
if (postParameters.Length <= 0) return;

// it's a form
if (Content is MultipartFormDataContent mpContent) {
// we got the multipart form already instantiated, just add parameters to it
foreach (var postParameter in postParameters) {
mpContent.Add(
new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType),
postParameter.Name!
);
}
}
else {
// we should not have anything else except the parameters, so we send them as form URL encoded
var formContent = new FormUrlEncodedContent(
_request.Parameters
.Where(x => x.Type == ParameterType.GetOrPost)
.Select(x => new KeyValuePair<string, string>(x.Name!, x.Value!.ToString()!))!
void AddPostParameters(ParametersCollection? postParameters) {
if (postParameters.IsEmpty()) return;

var mpContent = Content as MultipartFormDataContent ?? new MultipartFormDataContent();

// we got the multipart form already instantiated, just add parameters to it
foreach (var postParameter in postParameters!) {
mpContent.Add(
new StringContent(postParameter.Value!.ToString()!, _client.Options.Encoding, postParameter.ContentType),
postParameter.Name!
);
Content = formContent;
}

Content = mpContent;
}

void AddHeaders() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,16 @@

namespace RestSharp;

class RequestParameters {
static readonly ParameterType[] MultiParameterTypes = { ParameterType.QueryString, ParameterType.GetOrPost };

class RequestHeaders {
public ParametersCollection Parameters { get; } = new();

public RequestParameters AddParameters(ParametersCollection parameters, bool allowSameName) {
Parameters.AddParameters(GetParameters(parameters, allowSameName));
public RequestHeaders AddHeaders(ParametersCollection parameters) {
Parameters.AddParameters(parameters.GetParameters<HeaderParameter>());
return this;
}

IEnumerable<Parameter> GetParameters(ParametersCollection parametersCollection, bool allowSameName) {
foreach (var parameter in parametersCollection) {
var parameterExists = Parameters.Exists(parameter);

if (allowSameName) {
var isMultiParameter = MultiParameterTypes.Any(pt => pt == parameter.Type);
parameterExists = !isMultiParameter && parameterExists;
}

if (!parameterExists) yield return parameter;
}
}

// Add Accept header based on registered deserializers if none has been set by the caller.
public RequestParameters AddAcceptHeader(string[] acceptedContentTypes) {
public RequestHeaders AddAcceptHeader(string[] acceptedContentTypes) {
if (Parameters.TryFind(KnownHeaders.Accept) == null) {
var accepts = string.Join(", ", acceptedContentTypes);
Parameters.AddParameter(new HeaderParameter(KnownHeaders.Accept, accepts));
Expand Down
25 changes: 8 additions & 17 deletions src/RestSharp/Request/RestRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

using RestSharp.Extensions;
// ReSharper disable UnusedAutoPropertyAccessor.Global

namespace RestSharp;

Expand All @@ -26,15 +27,11 @@ public class RestRequest {
/// <summary>
/// Default constructor
/// </summary>
public RestRequest() {
RequestFormat = DataFormat.Json;
Method = Method.Get;
}
public RestRequest() => Method = Method.Get;

public RestRequest(string? resource, Method method = Method.Get, DataFormat dataFormat = DataFormat.Json) : this() {
Resource = resource ?? "";
Method = method;
RequestFormat = dataFormat;
public RestRequest(string? resource, Method method = Method.Get) : this() {
Resource = resource ?? "";
Method = method;

if (string.IsNullOrWhiteSpace(resource)) return;

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

public RestRequest(Uri resource, Method method = Method.Get, DataFormat dataFormat = DataFormat.Json)
: this(
resource.IsAbsoluteUri
? resource.AbsoluteUri
: resource.OriginalString,
method,
dataFormat
) { }
public RestRequest(Uri resource, Method method = Method.Get)
: this(resource.IsAbsoluteUri ? resource.AbsoluteUri : resource.OriginalString, method) { }

// readonly List<Parameter> _parameters = new();
readonly List<FileParameter> _files = new();
readonly List<FileParameter> _files = new();

/// <summary>
/// Always send a multipart/form-data request - even when no Files are present.
Expand Down
Loading