Skip to content

Commit

Permalink
Enable using URL segment parameters without encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeyzimarev committed Jul 15, 2021
1 parent 922bf07 commit 94e578a
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 68 deletions.
2 changes: 1 addition & 1 deletion src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflow workfl
// if this change causes trouble we need to introduce a flag indicating the specific OAuth implementation level,
// or implement a separate class for each OAuth version
static bool BaseQuery(Parameter x)
=> x.Type is ParameterType.GetOrPost or ParameterType.QueryString or ParameterType.QueryStringWithoutEncode;
=> x.Type is ParameterType.GetOrPost or ParameterType.QueryString;

var query =
request.AlwaysMultipartFormData || request.Files.Count > 0
Expand Down
16 changes: 5 additions & 11 deletions src/RestSharp/Enum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

namespace RestSharp
{
namespace RestSharp {
/// <summary>
/// Types of parameters that can be added to requests
/// </summary>
public enum ParameterType
{
public enum ParameterType {
/// <summary>
/// Cookie parameter
/// </summary>
Cookie,
GetOrPost, UrlSegment, HttpHeader, RequestBody, QueryString,
QueryStringWithoutEncode
Cookie, GetOrPost, UrlSegment, HttpHeader, RequestBody, QueryString, QueryStringWithoutEncode
}

/// <summary>
Expand All @@ -35,17 +31,15 @@ public enum DataFormat { Json, Xml, None }
/// <summary>
/// HTTP method to use when making requests
/// </summary>
public enum Method
{
public enum Method {
GET, POST, PUT, DELETE, HEAD, OPTIONS,
PATCH, MERGE, COPY
}

/// <summary>
/// Format strings for commonly-used date formats
/// </summary>
public struct DateFormat
{
public struct DateFormat {
/// <summary>
/// .NET format string for ISO 8601 date format
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/RestSharp/IRestRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,15 @@ public interface IRestRequest
/// <returns></returns>
IRestRequest AddUrlSegment(string name, string value);

/// <summary>
/// Shortcut to AddParameter(name, value, UrlSegment) overload
/// </summary>
/// <param name="name">Name of the segment to add</param>
/// <param name="value">Value of the segment to add</param>
/// <param name="encode">Specify false if the value should not be encoded</param>
/// <returns></returns>
IRestRequest AddUrlSegment(string name, string value, bool encode);

/// <summary>
/// Shortcut to AddParameter(name, value, UrlSegment) overload
/// </summary>
Expand Down
64 changes: 28 additions & 36 deletions src/RestSharp/Parameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,24 @@
using JetBrains.Annotations;
using RestSharp.Validation;

namespace RestSharp
{
namespace RestSharp {
/// <summary>
/// Parameter container for REST requests
/// </summary>
[Obsolete("Use Add[XXX]Parameter methods of IRestRequest instead of instantiating the Parameter class.")]
public class Parameter : IEquatable<Parameter>
{
public Parameter(string name, object value, ParameterType type)
{
public class Parameter : IEquatable<Parameter> {
public Parameter(string name, object? value, ParameterType type, bool encode = true) {
if (type != ParameterType.RequestBody)
Ensure.NotEmpty(name, nameof(name));

Name = name;
Value = type != ParameterType.UrlSegment ? value : value?.ToString().Replace("%2F", "/").Replace("%2f", "/");
Type = type;
Name = name;
Value = type != ParameterType.UrlSegment ? value : value?.ToString().Replace("%2F", "/").Replace("%2f", "/");
Type = type == ParameterType.QueryStringWithoutEncode ? ParameterType.QueryString : type;
Encode = type != ParameterType.QueryStringWithoutEncode && encode;
}

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

/// <summary>
/// Name of the parameter
Expand All @@ -65,49 +64,45 @@ public Parameter(string name, object value, ParameterType type)
/// </summary>
public string? ContentType { get; set; }

internal bool Encode { get; }

/// <summary>
/// Return a human-readable representation of this parameter
/// </summary>
/// <returns>String</returns>
public override string ToString() => $"{Name}={Value}";

public bool Equals(Parameter other)
{
public bool Equals(Parameter? other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;

return Name == other.Name
&& Equals(Value, other.Value)
&& Type == other.Type
&& DataFormat == other.DataFormat
&& ContentType == other.ContentType;
return Name == other.Name &&
Equals(Value, other.Value) &&
Type == other.Type &&
DataFormat == other.DataFormat &&
ContentType == other.ContentType;
}

public override bool Equals(object obj)
=> !ReferenceEquals(null, obj)
&& (ReferenceEquals(this, obj) || obj.GetType() == this.GetType() && Equals((Parameter) obj));
public override bool Equals(object? obj)
=> !ReferenceEquals(null, obj) && (ReferenceEquals(this, obj) || obj.GetType() == GetType() && Equals((Parameter)obj));

// ReSharper disable NonReadonlyMemberInGetHashCode
public override int GetHashCode()
{
unchecked
{
public override int GetHashCode() {
unchecked {
var hashCode = Name != null ? Name.GetHashCode() : 0;

hashCode = (hashCode * 397) ^ (Value != null ? Value.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (int) Type;
hashCode = (hashCode * 397) ^ (int) DataFormat;
hashCode = (hashCode * 397) ^ (int)Type;
hashCode = (hashCode * 397) ^ (int)DataFormat;
hashCode = (hashCode * 397) ^ (ContentType != null ? ContentType.GetHashCode() : 0);
return hashCode;
}
}
// ReSharper enable NonReadonlyMemberInGetHashCode
}

public class XmlParameter : Parameter
{
public XmlParameter(string name, object value, string? xmlNamespace = null) : base(name, value, ParameterType.RequestBody)
{
public class XmlParameter : Parameter {
public XmlParameter(string name, object value, string? xmlNamespace = null) : base(name, value, ParameterType.RequestBody) {
XmlNamespace = xmlNamespace;
DataFormat = DataFormat.Xml;
ContentType = Serialization.ContentType.Xml;
Expand All @@ -116,16 +111,13 @@ public XmlParameter(string name, object value, string? xmlNamespace = null) : ba
public string? XmlNamespace { get; }
}

public class JsonParameter : Parameter
{
public JsonParameter(string name, object value) : base(name, value, ParameterType.RequestBody)
{
public class JsonParameter : Parameter {
public JsonParameter(string name, object value) : base(name, value, ParameterType.RequestBody) {
DataFormat = DataFormat.Json;
ContentType = Serialization.ContentType.Json;
}

public JsonParameter(string name, object value, string contentType) : base(name, value, ParameterType.RequestBody)
{
public JsonParameter(string name, object value, string contentType) : base(name, value, ParameterType.RequestBody) {
DataFormat = DataFormat.Json;
ContentType = contentType ?? Serialization.ContentType.Json;
}
Expand Down
26 changes: 10 additions & 16 deletions src/RestSharp/RestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ public Uri BuildUri(IRestRequest request)
return new Uri(finalUri);
}

string IRestClient.BuildUriWithoutQueryParameters(IRestRequest request)
string? IRestClient.BuildUriWithoutQueryParameters(IRestRequest request)
{
DoBuildUriValidations(request);

Expand Down Expand Up @@ -303,7 +303,7 @@ UrlSegmentParamsValues GetUrlSegmentParamsValues(IRestRequest request)
foreach (var parameter in parameters)
{
var paramPlaceHolder = $"{{{parameter.Name}}}";
var paramValue = Encode(parameter.Value!.ToString());
var paramValue = parameter.Encode ? Encode(parameter.Value!.ToString()) : parameter.Value!.ToString();

if (hasResource) assembled = assembled.Replace(paramPlaceHolder, paramValue);

Expand All @@ -313,11 +313,11 @@ UrlSegmentParamsValues GetUrlSegmentParamsValues(IRestRequest request)
return new UrlSegmentParamsValues(builder.Uri, assembled);
}

static string MergeBaseUrlAndResource(Uri baseUrl, string resource)
static string? MergeBaseUrlAndResource(Uri? baseUrl, string? resource)
{
var assembled = resource;

if (!IsNullOrEmpty(assembled) && assembled.StartsWith("/")) assembled = assembled.Substring(1);
if (!IsNullOrEmpty(assembled) && assembled!.StartsWith("/")) assembled = assembled.Substring(1);

if (baseUrl == null || IsNullOrEmpty(baseUrl.AbsoluteUri)) return assembled;

Expand All @@ -326,7 +326,7 @@ static string MergeBaseUrlAndResource(Uri baseUrl, string resource)
return assembled != null ? new Uri(usingBaseUri, assembled).AbsoluteUri : baseUrl.AbsoluteUri;
}

string ApplyQueryStringParamsValuesToUri(string mergedUri, IRestRequest request)
string? ApplyQueryStringParamsValuesToUri(string? mergedUri, IRestRequest request)
{
var parameters = GetQueryStringParameters(request).ToList();
parameters.AddRange(GetDefaultQueryStringParameters(request));
Expand All @@ -342,28 +342,22 @@ IEnumerable<Parameter> GetDefaultQueryStringParameters(IRestRequest request)
=> request.Method != Method.POST && request.Method != Method.PUT && request.Method != Method.PATCH
? DefaultParameters
.Where(
p => p.Type == ParameterType.GetOrPost ||
p.Type == ParameterType.QueryString ||
p.Type == ParameterType.QueryStringWithoutEncode
p => p.Type is ParameterType.GetOrPost or ParameterType.QueryString
)
: DefaultParameters
.Where(
p => p.Type == ParameterType.QueryString ||
p.Type == ParameterType.QueryStringWithoutEncode
p => p.Type is ParameterType.QueryString
);

static IEnumerable<Parameter> GetQueryStringParameters(IRestRequest request)
=> request.Method != Method.POST && request.Method != Method.PUT && request.Method != Method.PATCH
? request.Parameters
.Where(
p => p.Type == ParameterType.GetOrPost ||
p.Type == ParameterType.QueryString ||
p.Type == ParameterType.QueryStringWithoutEncode
p => p.Type is ParameterType.GetOrPost or ParameterType.QueryString
)
: request.Parameters
.Where(
p => p.Type == ParameterType.QueryString ||
p.Type == ParameterType.QueryStringWithoutEncode
p => p.Type is ParameterType.QueryString
);

Func<IDeserializer>? GetHandler(string contentType)
Expand Down Expand Up @@ -402,7 +396,7 @@ string EncodeParameters(IEnumerable<Parameter> parameters, Encoding encoding)
string EncodeParameter(Parameter parameter, Encoding encoding)
{
return
parameter.Type == ParameterType.QueryStringWithoutEncode
!parameter.Encode
? $"{parameter.Name}={StringOrEmpty(parameter.Value)}"
: $"{EncodeQuery(parameter.Name!, encoding)}={EncodeQuery(StringOrEmpty(parameter.Value), encoding)}";

Expand Down
12 changes: 10 additions & 2 deletions src/RestSharp/RestRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,13 +426,21 @@ public IRestRequest AddOrUpdateHeaders(ICollection<KeyValuePair<string, string>>

/// <inheritdoc />
public IRestRequest AddUrlSegment(string name, string value) => AddParameter(name, value, ParameterType.UrlSegment);

/// <inheritdoc />
public IRestRequest AddUrlSegment(string name, string value, bool encode) {
var parameter = new Parameter(name, value, ParameterType.UrlSegment, encode);
return AddParameter(parameter);
}

/// <inheritdoc />
public IRestRequest AddQueryParameter(string name, string value) => AddParameter(name, value, ParameterType.QueryString);

/// <inheritdoc />
public IRestRequest AddQueryParameter(string name, string value, bool encode)
=> AddParameter(name, value, encode ? ParameterType.QueryString : ParameterType.QueryStringWithoutEncode);
public IRestRequest AddQueryParameter(string name, string value, bool encode) {
var parameter = new Parameter(name, value, ParameterType.QueryString, encode);
return AddParameter(parameter);
}

/// <inheritdoc />
public IRestRequest AddDecompressionMethod(DecompressionMethods decompressionMethod)
Expand Down
6 changes: 4 additions & 2 deletions test/RestSharp.Tests/RestRequestTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ public void RestRequest_Test_Already_Encoded()
Assert.AreEqual(2, request.Parameters.Count);
Assert.AreEqual("query", request.Parameters[0].Name);
Assert.AreEqual("Id%3d198", request.Parameters[0].Value);
Assert.AreEqual(ParameterType.QueryStringWithoutEncode, request.Parameters[0].Type);
Assert.AreEqual(ParameterType.QueryString, request.Parameters[0].Type);
Assert.AreEqual(false, request.Parameters[0].Encode);
Assert.AreEqual("another", request.Parameters[1].Name);
Assert.AreEqual("notencoded", request.Parameters[1].Value);
Assert.AreEqual(ParameterType.QueryStringWithoutEncode, request.Parameters[1].Type);
Assert.AreEqual(ParameterType.QueryString, request.Parameters[1].Type);
Assert.AreEqual(false, request.Parameters[1].Encode);
}

[Test]
Expand Down

0 comments on commit 94e578a

Please sign in to comment.