diff --git a/src/RestSharp/Parameters/Parameter.cs b/src/RestSharp/Parameters/Parameter.cs index fe260f3c6..6dda3ec71 100644 --- a/src/RestSharp/Parameters/Parameter.cs +++ b/src/RestSharp/Parameters/Parameter.cs @@ -30,11 +30,11 @@ public abstract record Parameter(string? Name, object? Value, ParameterType Type public override string ToString() => $"{Name}={Value}"; public static Parameter CreateParameter(string? name, object? value, ParameterType type, bool encode = true) + // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault => type switch { ParameterType.GetOrPost => new GetOrPostParameter(name!, value?.ToString(), encode), ParameterType.UrlSegment => new UrlSegmentParameter(name!, value?.ToString()!, encode), ParameterType.HttpHeader => new HeaderParameter(name, value?.ToString()), - ParameterType.RequestBody => new BodyParameter(name, value!, Serializers.ContentType.Plain), ParameterType.QueryString => new QueryParameter(name!, value?.ToString(), encode), _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; diff --git a/src/RestSharp/Request/RestRequest.cs b/src/RestSharp/Request/RestRequest.cs index bf6fbb5c5..b7360c867 100644 --- a/src/RestSharp/Request/RestRequest.cs +++ b/src/RestSharp/Request/RestRequest.cs @@ -193,7 +193,10 @@ public Func? AdvancedResponseWriter { /// Removes a parameter object from the request parameters /// /// Parameter to remove - public void RemoveParameter(Parameter parameter) => Parameters.RemoveParameter(parameter); + public RestRequest RemoveParameter(Parameter parameter) { + Parameters.RemoveParameter(parameter); + return this; + } internal RestRequest AddFile(FileParameter file) => this.With(x => x._files.Add(file)); } \ No newline at end of file diff --git a/src/RestSharp/Request/RestRequestExtensions.cs b/src/RestSharp/Request/RestRequestExtensions.cs index d38816262..15020a793 100644 --- a/src/RestSharp/Request/RestRequestExtensions.cs +++ b/src/RestSharp/Request/RestRequestExtensions.cs @@ -204,7 +204,14 @@ public static RestRequest AddOrUpdateHeaders(this RestRequest request, ICollecti /// Encode the value or not, default true /// public static RestRequest AddParameter(this RestRequest request, string? name, object value, ParameterType type, bool encode = true) - => request.AddParameter(Parameter.CreateParameter(name, value, type, encode)); + => type == ParameterType.RequestBody + ? request.AddBodyParameter(name, value) + : request.AddParameter(Parameter.CreateParameter(name, value, type, encode)); + + static RestRequest AddBodyParameter(this RestRequest request, string? name, object value) + => name != null && name.Contains("/") + ? request.AddBody(value, name) + : request.AddParameter(new BodyParameter(name, value, ContentType.Plain)); /// /// Adds or updates request parameter of a given type. It will create a typed parameter instance based on the type argument. @@ -218,8 +225,13 @@ public static RestRequest AddParameter(this RestRequest request, string? name, o /// Enum value specifying what kind of parameter is being added /// Encode the value or not, default true /// - public static RestRequest AddOrUpdateParameter(this RestRequest request, string name, object value, ParameterType type, bool encode = true) - => request.AddOrUpdateParameter(Parameter.CreateParameter(name, value, type, encode)); + public static RestRequest AddOrUpdateParameter(this RestRequest request, string name, object value, ParameterType type, bool encode = true) { + request.RemoveParameter(name, type); + + return type == ParameterType.RequestBody + ? request.AddBodyParameter(name, value) + : request.AddOrUpdateParameter(Parameter.CreateParameter(name, value, type, encode)); + } /// /// Adds or updates request parameter, given the parameter instance, for example or . @@ -228,13 +240,12 @@ public static RestRequest AddOrUpdateParameter(this RestRequest request, string /// Request instance /// Parameter instance /// - public static RestRequest AddOrUpdateParameter(this RestRequest request, Parameter parameter) { - var p = request.Parameters.FirstOrDefault(x => x.Name == parameter.Name && x.Type == parameter.Type); - - if (p != null) request.RemoveParameter(p); + public static RestRequest AddOrUpdateParameter(this RestRequest request, Parameter parameter) + => request.RemoveParameter(parameter.Name, parameter.Type).AddParameter(parameter); - request.AddParameter(parameter); - return request; + static RestRequest RemoveParameter(this RestRequest request, string? name, ParameterType type) { + var p = request.Parameters.FirstOrDefault(x => x.Name == name && x.Type == type); + return p != null ? request.RemoveParameter(p) : request; } /// @@ -245,8 +256,7 @@ public static RestRequest AddOrUpdateParameter(this RestRequest request, Paramet /// Collection of parameter instances /// public static RestRequest AddOrUpdateParameters(this RestRequest request, IEnumerable parameters) { - foreach (var parameter in parameters) - request.AddOrUpdateParameter(parameter); + foreach (var parameter in parameters) request.AddOrUpdateParameter(parameter); return request; } @@ -304,15 +314,15 @@ public static RestRequest AddFile( public static RestRequest AddBody(this RestRequest request, object obj, string? contentType = null) { if (contentType == null) { return request.RequestFormat switch { - DataFormat.Json => request.AddJsonBody(obj, contentType ?? ContentType.Json), - DataFormat.Xml => request.AddXmlBody(obj, contentType ?? ContentType.Xml), - DataFormat.Binary => request.AddParameter(new BodyParameter("", obj, contentType ?? ContentType.Binary)), - _ => request.AddParameter(new BodyParameter("", obj.ToString()!, contentType ?? ContentType.Plain)) + DataFormat.Json => request.AddJsonBody(obj), + DataFormat.Xml => request.AddXmlBody(obj), + DataFormat.Binary => request.AddParameter(new BodyParameter("", obj, ContentType.Binary)), + _ => request.AddParameter(new BodyParameter("", obj.ToString()!, ContentType.Plain)) }; } return - obj is string str ? request.AddParameter(new BodyParameter("", str, contentType)) : + obj is string str ? request.AddStringBody(str, contentType) : obj is byte[] bytes ? request.AddParameter(new BodyParameter("", bytes, contentType, DataFormat.Binary)) : contentType.Contains("xml") ? request.AddXmlBody(obj, contentType) : contentType.Contains("json") ? request.AddJsonBody(obj, contentType) : @@ -352,7 +362,7 @@ public static RestRequest AddStringBody(this RestRequest request, string body, s /// public static RestRequest AddJsonBody(this RestRequest request, T obj, string contentType = ContentType.Json) where T : class { request.RequestFormat = DataFormat.Json; - return request.AddParameter(new JsonParameter("", obj, contentType)); + return obj is string str ? request.AddStringBody(str, DataFormat.Json) : request.AddParameter(new JsonParameter("", obj, contentType)); } /// @@ -366,8 +376,10 @@ public static RestRequest AddJsonBody(this RestRequest request, T obj, string public static RestRequest AddXmlBody(this RestRequest request, T obj, string contentType = ContentType.Xml, string xmlNamespace = "") where T : class { request.RequestFormat = DataFormat.Xml; - request.AddParameter(new XmlParameter("", obj, xmlNamespace, contentType)); - return request; + + return obj is string str + ? request.AddStringBody(str, DataFormat.Xml) + : request.AddParameter(new XmlParameter("", obj, xmlNamespace, contentType)); } /// @@ -401,7 +413,6 @@ static void CheckAndThrowsDuplicateKeys(ICollection .Select(group => group.Key) .ToList(); - if (duplicateKeys.Any()) - throw new ArgumentException($"Duplicate header names exist: {string.Join(", ", duplicateKeys)}"); + if (duplicateKeys.Any()) throw new ArgumentException($"Duplicate header names exist: {string.Join(", ", duplicateKeys)}"); } -} \ No newline at end of file +} diff --git a/src/RestSharp/RestClient.Async.cs b/src/RestSharp/RestClient.Async.cs index 8eadd5bb3..a9689ce3c 100644 --- a/src/RestSharp/RestClient.Async.cs +++ b/src/RestSharp/RestClient.Async.cs @@ -42,7 +42,7 @@ public async Task ExecuteAsync(RestRequest request, CancellationTo response.Request = request; response.Request.IncreaseNumAttempts(); - return Options.ThrowOnAnyError ? ThrowIfError(response) : response; + return Options.ThrowOnAnyError ? response.ThrowIfError() : response; } async Task ExecuteInternal(RestRequest request, CancellationToken cancellationToken) { @@ -130,13 +130,6 @@ static RestResponse AddError(RestResponse response, Exception exception, Cancell bool TimedOut() => timeoutToken.IsCancellationRequested || exception.Message.Contains("HttpClient.Timeout"); } - internal static RestResponse ThrowIfError(RestResponse response) { - var exception = response.GetException(); - if (exception != null) throw exception; - - return response; - } - static HttpMethod AsHttpMethod(Method method) => method switch { Method.Get => HttpMethod.Get, @@ -155,4 +148,20 @@ static HttpMethod AsHttpMethod(Method method) Method.Search => new HttpMethod("SEARCH"), _ => throw new ArgumentOutOfRangeException() }; +} + +public static class ResponseThrowExtension { + public static RestResponse ThrowIfError(this RestResponse response) { + var exception = response.GetException(); + if (exception != null) throw exception; + + return response; + } + + public static RestResponse ThrowIfError(this RestResponse response) { + var exception = response.GetException(); + if (exception != null) throw exception; + + return response; + } } \ No newline at end of file diff --git a/src/RestSharp/RestClientExtensions.Params.cs b/src/RestSharp/RestClientExtensions.Params.cs index eab49ad59..26ed45da1 100644 --- a/src/RestSharp/RestClientExtensions.Params.cs +++ b/src/RestSharp/RestClientExtensions.Params.cs @@ -40,8 +40,10 @@ public static RestClient AddDefaultParameter(this RestClient client, string name /// Value of the parameter /// The type of parameter to add /// This request - public static RestClient AddDefaultParameter(this RestClient client, string name, object value, ParameterType type) - => client.AddDefaultParameter(Parameter.CreateParameter(name, value, type)); + public static RestClient AddDefaultParameter(this RestClient client, string name, object value, ParameterType type) { + if (type == ParameterType.RequestBody) throw new ArgumentException("Default parameter cannot be Body", nameof(type)); + return client.AddDefaultParameter(Parameter.CreateParameter(name, value, type)); + } /// /// Adds a default header to the RestClient. Used on every request made by this client instance. diff --git a/src/RestSharp/RestClientExtensions.cs b/src/RestSharp/RestClientExtensions.cs index 22ac300da..bf6c56dbd 100644 --- a/src/RestSharp/RestClientExtensions.cs +++ b/src/RestSharp/RestClientExtensions.cs @@ -160,14 +160,12 @@ public static Task> ExecuteAsync( /// public static async Task GetAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteGetAsync(request, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response.Data; + return response.ThrowIfError().Data; } public static async Task GetAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteGetAsync(request, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response; + return response.ThrowIfError(); } /// @@ -181,8 +179,7 @@ public static async Task GetAsync(this RestClient client, RestRequ /// public static async Task PostAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecutePostAsync(request, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response.Data; + return response.ThrowIfError().Data; } public static RestResponse Post(this RestClient client, RestRequest request) @@ -190,8 +187,7 @@ public static RestResponse Post(this RestClient client, RestRequest request) public static async Task PostAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecutePostAsync(request, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response; + return response.ThrowIfError(); } /// @@ -205,14 +201,12 @@ public static async Task PostAsync(this RestClient client, RestReq /// public static async Task PutAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Put, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response.Data; + return response.ThrowIfError().Data; } public static async Task PutAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Put, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response; + return response.ThrowIfError(); } /// @@ -226,14 +220,12 @@ public static async Task PutAsync(this RestClient client, RestRequ /// public static async Task HeadAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Head, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response.Data; + return response.ThrowIfError().Data; } public static async Task HeadAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Head, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response; + return response.ThrowIfError(); } /// @@ -247,14 +239,12 @@ public static async Task HeadAsync(this RestClient client, RestReq /// public static async Task OptionsAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Options, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response.Data; + return response.ThrowIfError().Data; } public static async Task OptionsAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Options, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response; + return response.ThrowIfError(); } /// @@ -268,14 +258,12 @@ public static async Task OptionsAsync(this RestClient client, Rest /// public static async Task PatchAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Patch, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response.Data; + return response.ThrowIfError().Data; } public static async Task PatchAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Patch, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response; + return response.ThrowIfError(); } /// @@ -289,14 +277,12 @@ public static async Task PatchAsync(this RestClient client, RestRe /// public static async Task DeleteAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Delete, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response.Data; + return response.ThrowIfError().Data; } public static async Task DeleteAsync(this RestClient client, RestRequest request, CancellationToken cancellationToken = default) { var response = await client.ExecuteAsync(request, Method.Delete, cancellationToken).ConfigureAwait(false); - RestClient.ThrowIfError(response); - return response; + return response.ThrowIfError(); } /// diff --git a/test/RestSharp.Tests/RestContentTests.cs b/test/RestSharp.Tests/RestContentTests.cs index 2e7744988..49aea4453 100644 --- a/test/RestSharp.Tests/RestContentTests.cs +++ b/test/RestSharp.Tests/RestContentTests.cs @@ -12,4 +12,17 @@ public void RestContent_CaseInsensitiveHeaders() { httpContent.Headers.ContentType!.MediaType.Should().Be(myContentType); } + + [Fact] + public void RestContent_supports_manual_json_body() { + const string myContentType = "application/json"; + const string myJsonString = "[]"; + + var request = new RestRequest("resource").AddParameter(myContentType, myJsonString, ParameterType.RequestBody); + var content = new RequestContent(new RestClient(), request); + + var httpContent = content.BuildContent(); + + httpContent.Headers.ContentType!.MediaType.Should().Be(myContentType); + } }