// Copyright © 2009-2021 John Sheehan, Andrew Young, Alexey Zimarev and RestSharp community
//
// 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.
using RestSharp.Extensions;
// ReSharper disable UnusedAutoPropertyAccessor.Global
namespace RestSharp;
///
/// Container for data used to make requests
///
public class RestRequest {
readonly Func? _advancedResponseHandler;
readonly Func? _responseWriter;
///
/// Default constructor
///
public RestRequest() => Method = Method.Get;
public RestRequest(string? resource, Method method = Method.Get) : this() {
Resource = resource ?? "";
Method = method;
if (string.IsNullOrWhiteSpace(resource)) return;
var queryStringStart = Resource.IndexOf('?');
if (queryStringStart >= 0 && Resource.IndexOf('=') > queryStringStart) {
var queryParams = ParseQuery(Resource.Substring(queryStringStart + 1));
Resource = Resource.Substring(0, queryStringStart);
foreach (var param in queryParams)
this.AddQueryParameter(param.Key, param.Value, false);
}
static IEnumerable> ParseQuery(string query)
=> query.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries)
.Select(
x => {
var position = x.IndexOf('=');
return position > 0
? new KeyValuePair(x.Substring(0, position), x.Substring(position + 1))
: new KeyValuePair(x, string.Empty);
}
);
}
public RestRequest(Uri resource, Method method = Method.Get)
: this(resource.IsAbsoluteUri ? resource.AbsoluteUri : resource.OriginalString, method) { }
readonly List _files = new();
///
/// Always send a multipart/form-data request - even when no Files are present.
///
public bool AlwaysMultipartFormData { get; set; }
///
/// When set to true, parameters in a multipart form data requests will be enclosed in
/// quotation marks. Default is false. Enable it if the remote endpoint requires parameters
/// to be in quotes (for example, FreshDesk API).
///
public bool MultipartFormQuoteParameters { get; set; }
public string? FormBoundary { get; set; }
///
/// Container of all HTTP parameters to be passed with the request.
/// See AddParameter() for explanation of the types of parameters that can be passed
///
public ParametersCollection Parameters { get; } = new();
///
/// Container of all the files to be uploaded with the request.
///
public IReadOnlyCollection Files => _files.AsReadOnly();
///
/// Determines what HTTP method to use for this request. Supported methods: GET, POST, PUT, DELETE, HEAD, OPTIONS
/// Default is GET
///
public Method Method { get; set; }
///
/// Custom request timeout
///
public int Timeout { get; set; }
///
/// The Resource URL to make the request against.
/// Tokens are substituted with UrlSegment parameters and match by name.
/// Should not include the scheme or domain. Do not include leading slash.
/// Combined with RestClient.BaseUrl to assemble final URL:
/// {BaseUrl}/{Resource} (BaseUrl is scheme + domain, e.g. http://example.com)
///
///
/// // example for url token replacement
/// request.Resource = "Products/{ProductId}";
/// request.AddParameter("ProductId", 123, ParameterType.UrlSegment);
///
public string Resource { get; set; } = "";
///
/// Serializer to use when writing request bodies.
///
public DataFormat RequestFormat { get; set; }
///
/// Used by the default deserializers to determine where to start deserializing from.
/// Can be used to skip container or root elements that do not have corresponding deserialzation targets.
///
public string? RootElement { get; set; }
///
/// When supplied, the function will be called before calling the deserializer
///
public Action? OnBeforeDeserialization { get; set; }
///
/// When supplied, the function will be called before making a request
///
public Func? OnBeforeRequest { get; set; }
///
/// When supplied, the function will be called after the request is complete
///
public Func? OnAfterRequest { get; set; }
internal void IncreaseNumAttempts() => Attempts++;
///
/// How many attempts were made to send this Request
///
///
/// This number is incremented each time the RestClient sends the request.
///
public int Attempts { get; private set; }
///
/// Completion option for
///
public HttpCompletionOption CompletionOption { get; set; } = HttpCompletionOption.ResponseContentRead;
///
/// Set this to write response to Stream rather than reading into memory.
///
public Func? ResponseWriter {
get => _responseWriter;
init {
if (AdvancedResponseWriter != null)
throw new ArgumentException(
"AdvancedResponseWriter is not null. Only one response writer can be used."
);
_responseWriter = value;
}
}
///
/// Set this to handle the response stream yourself, based on the response details
///
public Func? AdvancedResponseWriter {
get => _advancedResponseHandler;
init {
if (ResponseWriter != null)
throw new ArgumentException("ResponseWriter is not null. Only one response writer can be used.");
_advancedResponseHandler = value;
}
}
///
/// Adds a parameter object to the request parameters
///
/// Parameter to add
///
public RestRequest AddParameter(Parameter parameter) => this.With(x => x.Parameters.AddParameter(parameter));
///
/// Removes a parameter object from the request parameters
///
/// Parameter to remove
public RestRequest RemoveParameter(Parameter parameter) {
Parameters.RemoveParameter(parameter);
return this;
}
internal RestRequest AddFile(FileParameter file) => this.With(x => x._files.Add(file));
}