diff --git a/src/RestSharp/Request/RequestContent.cs b/src/RestSharp/Request/RequestContent.cs
index 4df106cb1..5a19f94fb 100644
--- a/src/RestSharp/Request/RequestContent.cs
+++ b/src/RestSharp/Request/RequestContent.cs
@@ -36,38 +36,60 @@ public RequestContent(RestClient client, RestRequest request) {
_parameters = new RequestParameters(_request.Parameters.Union(_client.DefaultParameters));
}
- public HttpContent BuildContent() {
- AddFiles();
+ public HttpContent BuildContent()
+ {
var postParameters = _parameters.GetContentParameters(_request.Method).ToArray();
- AddBody(postParameters.Length > 0);
- AddPostParameters(postParameters);
- AddHeaders();
-
- return Content!;
- }
-
- void AddFiles() {
- if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;
-
- var mpContent = CreateMultipartFormDataContent();
+ var postParametersExists = postParameters.Length > 0;
+ var bodyParametersExists = _request.TryGetBodyParameter(out var bodyParameter);
+ var filesExists = _request.Files.Any();
- foreach (var file in _request.Files) {
- var stream = file.GetFile();
- _streams.Add(stream);
- var fileContent = new StreamContent(stream);
+ if (filesExists)
+ AddFiles();
- fileContent.Headers.ContentType = file.ContentType.AsMediaTypeHeaderValue;
+ if (bodyParametersExists)
+ AddBody(postParametersExists, bodyParameter!);
- var dispositionHeader = file.Options.DisableFilenameEncoding
- ? ContentDispositionHeaderValue.Parse($"form-data; name=\"{file.Name}\"; filename=\"{file.FileName}\"")
- : new ContentDispositionHeaderValue("form-data") { Name = $"\"{file.Name}\"", FileName = $"\"{file.FileName}\"" };
- if (!file.Options.DisableFileNameStar) dispositionHeader.FileNameStar = file.FileName;
- fileContent.Headers.ContentDisposition = dispositionHeader;
+ if (postParametersExists)
+ AddPostParameters(postParameters);
- mpContent.Add(fileContent);
- }
+ AddHeaders();
- Content = mpContent;
+ return Content!;
+ }
+
+ void AddFiles()
+ {
+ // File uploading without multipart/form-data
+ if (_request.AlwaysSingleFileAsContent && _request.Files.Count == 1)
+ {
+ var fileParameter = _request.Files.First();
+ Content = ToStreamContent(fileParameter);
+ return;
+ }
+
+ var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
+
+ foreach (var fileParameter in _request.Files)
+ mpContent.Add(ToStreamContent(fileParameter));
+
+ Content = mpContent;
+ }
+
+ StreamContent ToStreamContent(FileParameter fileParameter)
+ {
+ var stream = fileParameter.GetFile();
+ _streams.Add(stream);
+ var streamContent = new StreamContent(stream);
+
+ streamContent.Headers.ContentType = fileParameter.ContentType.AsMediaTypeHeaderValue;
+
+ var dispositionHeader = fileParameter.Options.DisableFilenameEncoding
+ ? ContentDispositionHeaderValue.Parse($"form-data; name=\"{fileParameter.Name}\"; filename=\"{fileParameter.FileName}\"")
+ : new ContentDispositionHeaderValue("form-data") { Name = $"\"{fileParameter.Name}\"", FileName = $"\"{fileParameter.FileName}\"" };
+ if (!fileParameter.Options.DisableFileNameStar) dispositionHeader.FileNameStar = fileParameter.FileName;
+ streamContent.Headers.ContentDisposition = dispositionHeader;
+
+ return streamContent;
}
HttpContent Serialize(BodyParameter body) {
@@ -117,9 +139,8 @@ MultipartFormDataContent CreateMultipartFormDataContent() {
return mpContent;
}
- void AddBody(bool hasPostParameters) {
- if (!_request.TryGetBodyParameter(out var bodyParameter)) return;
-
+ void AddBody(bool hasPostParameters, BodyParameter bodyParameter)
+ {
var bodyContent = Serialize(bodyParameter);
// we need to send the body
diff --git a/src/RestSharp/Request/RestRequest.cs b/src/RestSharp/Request/RestRequest.cs
index bc3b68382..9748fa9cf 100644
--- a/src/RestSharp/Request/RestRequest.cs
+++ b/src/RestSharp/Request/RestRequest.cs
@@ -81,7 +81,12 @@ public RestRequest(Uri resource, Method method = Method.Get)
/// Always send a multipart/form-data request - even when no Files are present.
///
public bool AlwaysMultipartFormData { get; set; }
-
+
+ ///
+ /// Always send a file as request content without multipart/form-data request - even when the request contains only one file parameter
+ ///
+ public bool AlwaysSingleFileAsContent { get; set; }
+
///
/// When set to true, parameter values in a multipart form data requests will be enclosed in
/// quotation marks. Default is false. Enable it if the remote endpoint requires parameters
diff --git a/src/RestSharp/Request/RestRequestExtensions.cs b/src/RestSharp/Request/RestRequestExtensions.cs
index ce33b2611..b2ff8469e 100644
--- a/src/RestSharp/Request/RestRequestExtensions.cs
+++ b/src/RestSharp/Request/RestRequestExtensions.cs
@@ -501,4 +501,21 @@ static void CheckAndThrowsDuplicateKeys(ICollection
if (duplicateKeys.Any()) throw new ArgumentException($"Duplicate header names exist: {string.Join(", ", duplicateKeys)}");
}
+
+ public static void ValidateParameters(this RestRequest request) {
+
+ if (request.AlwaysSingleFileAsContent) {
+ var postParametersExists = request.Parameters.GetContentParameters(request.Method).Any();
+ var bodyParametersExists = request.Parameters.Any(p => p.Type == ParameterType.RequestBody);
+
+ if (request.AlwaysMultipartFormData)
+ throw new ArgumentException("Failed to put file as content because flag AlwaysMultipartFormData enabled");
+
+ if (postParametersExists)
+ throw new ArgumentException("Failed to put file as content because added post parameters");
+
+ if (bodyParametersExists)
+ throw new ArgumentException("Failed to put file as content because added body parameters");
+ }
+ }
}
diff --git a/src/RestSharp/RestClient.Async.cs b/src/RestSharp/RestClient.Async.cs
index c5da81007..d3701e221 100644
--- a/src/RestSharp/RestClient.Async.cs
+++ b/src/RestSharp/RestClient.Async.cs
@@ -77,6 +77,7 @@ async Task ExecuteRequestAsync(RestRequest request, CancellationTo
throw new ObjectDisposedException(nameof(RestClient));
}
+ request.ValidateParameters();
var authenticator = request.Authenticator ?? Options.Authenticator;
if (authenticator != null) await authenticator.Authenticate(this, request).ConfigureAwait(false);
diff --git a/test/RestSharp.Tests/RestRequestParametersTests.cs b/test/RestSharp.Tests/RestRequestParametersTests.cs
new file mode 100644
index 000000000..7370644d3
--- /dev/null
+++ b/test/RestSharp.Tests/RestRequestParametersTests.cs
@@ -0,0 +1,75 @@
+// Copyright (c) .NET Foundation and Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+namespace RestSharp.Tests;
+
+public class RestRequestValidateParametersTests {
+ [Fact]
+ public void RestRequest_AlwaysMultipartFormData_IsAllowed() {
+ var request = new RestRequest {
+ AlwaysMultipartFormData = true
+ };
+
+ request.ValidateParameters();
+ }
+
+ [Fact]
+ public void RestRequest_AlwaysSingleFileAsContent_IsAllowed() {
+ var request = new RestRequest {
+ AlwaysSingleFileAsContent = true
+ };
+
+ request.ValidateParameters();
+ }
+
+ [Fact]
+ public void RestRequest_AlwaysSingleFileAsContent_And_AlwaysMultipartFormData_IsNotAllowed() {
+ var request = new RestRequest {
+ AlwaysSingleFileAsContent = true,
+ AlwaysMultipartFormData = true
+ };
+
+ Assert.Throws(
+ () => { request.ValidateParameters(); }
+ );
+ }
+
+ [Fact]
+ public void RestRequest_AlwaysSingleFileAsContent_And_PostParameters_IsNotAllowed() {
+ var request = new RestRequest {
+ Method = Method.Post,
+ AlwaysSingleFileAsContent = true,
+ };
+
+ request.AddParameter("name", "value", ParameterType.GetOrPost);
+
+ Assert.Throws(
+ () => { request.ValidateParameters(); }
+ );
+ }
+
+ [Fact]
+ public void RestRequest_AlwaysSingleFileAsContent_And_BodyParameters_IsNotAllowed() {
+ var request = new RestRequest {
+ AlwaysSingleFileAsContent = true,
+ };
+
+ request.AddParameter("name", "value", ParameterType.RequestBody);
+
+ Assert.Throws(
+ () => { request.ValidateParameters(); }
+ );
+ }
+}
\ No newline at end of file