diff --git a/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs b/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs index a4641ff5a..bd7f2da78 100644 --- a/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs +++ b/src/RestSharp/Authenticators/OAuth/OAuth1Authenticator.cs @@ -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 diff --git a/src/RestSharp/Enum.cs b/src/RestSharp/Enum.cs index 7044888b7..0958e9a35 100644 --- a/src/RestSharp/Enum.cs +++ b/src/RestSharp/Enum.cs @@ -12,19 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -namespace RestSharp -{ +namespace RestSharp { /// /// Types of parameters that can be added to requests /// - public enum ParameterType - { + public enum ParameterType { /// /// Cookie parameter /// - Cookie, - GetOrPost, UrlSegment, HttpHeader, RequestBody, QueryString, - QueryStringWithoutEncode + Cookie, GetOrPost, UrlSegment, HttpHeader, RequestBody, QueryString, QueryStringWithoutEncode } /// @@ -35,8 +31,7 @@ public enum DataFormat { Json, Xml, None } /// /// HTTP method to use when making requests /// - public enum Method - { + public enum Method { GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH, MERGE, COPY } @@ -44,8 +39,7 @@ public enum Method /// /// Format strings for commonly-used date formats /// - public struct DateFormat - { + public struct DateFormat { /// /// .NET format string for ISO 8601 date format /// diff --git a/src/RestSharp/IRestRequest.cs b/src/RestSharp/IRestRequest.cs index f03c12e9a..0bf2d1541 100644 --- a/src/RestSharp/IRestRequest.cs +++ b/src/RestSharp/IRestRequest.cs @@ -417,6 +417,15 @@ public interface IRestRequest /// IRestRequest AddUrlSegment(string name, string value); + /// + /// Shortcut to AddParameter(name, value, UrlSegment) overload + /// + /// Name of the segment to add + /// Value of the segment to add + /// Specify false if the value should not be encoded + /// + IRestRequest AddUrlSegment(string name, string value, bool encode); + /// /// Shortcut to AddParameter(name, value, UrlSegment) overload /// diff --git a/src/RestSharp/Parameter.cs b/src/RestSharp/Parameter.cs index 6fe6efbf5..e6dab8e1b 100644 --- a/src/RestSharp/Parameter.cs +++ b/src/RestSharp/Parameter.cs @@ -20,25 +20,24 @@ using JetBrains.Annotations; using RestSharp.Validation; -namespace RestSharp -{ +namespace RestSharp { /// /// Parameter container for REST requests /// [Obsolete("Use Add[XXX]Parameter methods of IRestRequest instead of instantiating the Parameter class.")] - public class Parameter : IEquatable - { - public Parameter(string name, object value, ParameterType type) - { + public class Parameter : IEquatable { + 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; /// /// Name of the parameter @@ -65,38 +64,36 @@ public Parameter(string name, object value, ParameterType type) /// public string? ContentType { get; set; } + internal bool Encode { get; } + /// /// Return a human-readable representation of this parameter /// /// String 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; } @@ -104,10 +101,8 @@ public override int GetHashCode() // 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; @@ -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; } diff --git a/src/RestSharp/RestClient.cs b/src/RestSharp/RestClient.cs index bfc84eb72..8b5d3de0c 100644 --- a/src/RestSharp/RestClient.cs +++ b/src/RestSharp/RestClient.cs @@ -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); @@ -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); @@ -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; @@ -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)); @@ -342,28 +342,22 @@ IEnumerable 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 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? GetHandler(string contentType) @@ -402,7 +396,7 @@ string EncodeParameters(IEnumerable 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)}"; diff --git a/src/RestSharp/RestRequest.cs b/src/RestSharp/RestRequest.cs index eb38c6132..f2a70b176 100644 --- a/src/RestSharp/RestRequest.cs +++ b/src/RestSharp/RestRequest.cs @@ -426,13 +426,21 @@ public IRestRequest AddOrUpdateHeaders(ICollection> /// public IRestRequest AddUrlSegment(string name, string value) => AddParameter(name, value, ParameterType.UrlSegment); + + /// + public IRestRequest AddUrlSegment(string name, string value, bool encode) { + var parameter = new Parameter(name, value, ParameterType.UrlSegment, encode); + return AddParameter(parameter); + } /// public IRestRequest AddQueryParameter(string name, string value) => AddParameter(name, value, ParameterType.QueryString); /// - 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); + } /// public IRestRequest AddDecompressionMethod(DecompressionMethods decompressionMethod) diff --git a/test/RestSharp.Tests/RestRequestTests.cs b/test/RestSharp.Tests/RestRequestTests.cs index 915c4efef..3f3b0e299 100644 --- a/test/RestSharp.Tests/RestRequestTests.cs +++ b/test/RestSharp.Tests/RestRequestTests.cs @@ -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]