// 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)); }