Пример #1
0
        /// <summary>
        /// Process direct request with deserialized generic response. This method is called by all verb actions
        /// </summary>
        /// <typeparam name="T">Generic return type to deserialize response data in to</typeparam>
        /// <param name="options">RextOption to configure http call</param>
        /// <returns>Deserialized response of T</returns>
        public async Task <CustomHttpResponse <T> > MakeRequest <T>(RextOptions options)
        {
            var rsp = await ProcessRequest(options);

            var newRsp = new CustomHttpResponse <T>
            {
                StatusCode = rsp.StatusCode,
                Message    = rsp.Message,
                Content    = rsp.Content
            };

            bool deserializeSuccessOnly = options?.DeserializeSuccessResponseOnly ?? ConfigurationBundle.HttpConfiguration.DeserializeSuccessResponseOnly;

            if (newRsp.StatusCode == System.Net.HttpStatusCode.OK || !deserializeSuccessOnly)
            {
                if (!string.IsNullOrEmpty(rsp.Content))
                {
                    bool throwExOnFail = options.ThrowExceptionOnDeserializationFailure ?? ConfigurationBundle.HttpConfiguration.ThrowExceptionOnDeserializationFailure;
                    (bool status, string message, T result)output = (false, null, default(T));

                    if (options.ExpectedResponseFormat == ContentType.Application_JSON)
                    {
                        output = Helpers.DeserializeJSON <T>(rsp.Content, throwExOnFail);
                    }
                    else if (options.ExpectedResponseFormat == ContentType.Application_XML)
                    {
                        output = Helpers.DeserializeXML <T>(rsp.Content, throwExOnFail);
                    }

                    if (output.status)
                    {
                        newRsp.Data = output.result;
                    }
                    else
                    {
                        if (newRsp.StatusCode != System.Net.HttpStatusCode.OK && !deserializeSuccessOnly)
                        {
                            newRsp.Message = $"Type deserialization failed: To prevent deserialization of unsuccessful response types, set DeserializeSuccessResponseOnly=true";

                            if (throwExOnFail)
                            {
                                throw new RextException(newRsp.Message + " ---> To prevent deserialization of unsuccessful response types, set DeserializeSuccessResponseOnly = true");
                            }

                            return(newRsp);
                        }
                        else
                        {
                            newRsp.Message = output.message;
                        }
                    }
                }
            }
            else
            {
                newRsp.Message += " --> To allow deserialization even when response status code is not successful, set DeserializeSuccessResponseOnly = false";
            }

            return(newRsp);
        }
Пример #2
0
        /// <summary>
        /// Process direct request with plain string response. This method is called by all verb actions
        /// </summary>
        /// <param name="options">RextOption to configure http call</param>
        /// <returns>Deserialized response of T</returns>
        public async Task <CustomHttpResponse <string> > MakeRequest(RextOptions options)
        {
            var rsp = await ProcessRequest(options);

            rsp.Data    = rsp.Content;
            rsp.Content = null;

            return(rsp);
        }
Пример #3
0
        /// <summary>
        /// Patch JSON content for JSON result deserialized to custom type. Accepts advanced options. You can change request format with RextOptions.ContentType
        /// </summary>
        /// <typeparam name="T">Generic return type to deserialize response data in to</typeparam>
        /// <param name="options">RextOption to configure http call</param>
        /// <returns>Deserialized response of T</returns>
        public async Task <CustomHttpResponse <T> > PatchJSON <T>(RextOptions options)
        {
            options.Method = HttpMethod.Patch;
            options.ExpectedResponseFormat = ContentType.Application_JSON;

            var data = await MakeRequest <T>(options);

            return(data);
        }
Пример #4
0
        /// <summary>
        /// Get plain string result. Accepts advanced options
        /// </summary>
        /// <param name="options">RextOption to configure http call</param>
        /// <returns>String response</returns>
        public async Task <CustomHttpResponse <string> > GetString(RextOptions options)
        {
            options.Method = HttpMethod.Get;
            options.ExpectedResponseFormat = ContentType.Text_Plain;

            var data = await MakeRequest(options);

            return(data);
        }
Пример #5
0
        /// <summary>
        /// Patch JSON content for string result. Accepts advanced options. You can change request format with RextOptions.ContentType
        /// </summary>
        /// <param name="options">RextOption to configure http call</param>
        /// <returns>Deserialized response of T</returns>
        public async Task <CustomHttpResponse <string> > PatchJSONForString(RextOptions options)
        {
            options.Method                 = HttpMethod.Put;
            options.ContentType            = ContentType.Application_JSON;
            options.ExpectedResponseFormat = ContentType.Text_Plain;

            var data = await MakeRequest(options);

            return(data);
        }
Пример #6
0
        /// <summary>
        /// Post XML content for string result. Accepts advanced options. You can change request format with RextOptions.ContentType
        /// </summary>
        /// <param name="options">RextOption to configure http call</param>
        /// <returns>Deserialized response of T</returns>
        public async Task <CustomHttpResponse <string> > PostXMLForString(RextOptions options)
        {
            options.Method                 = HttpMethod.Post;
            options.ContentType            = ContentType.Application_XML;
            options.ExpectedResponseFormat = ContentType.Application_XML;

            var data = await MakeRequest(options);

            return(data);
        }
Пример #7
0
        /// <summary>
        /// Post content as form-data for JSON result deserialized to custom type. Uses multipart/form-data by default
        /// </summary>
        /// <typeparam name="T">Generic return type to deserialize response data in to</typeparam>
        /// <param name="options">RextOption to configure http call</param>
        /// <param name="isUrlEncoded">Set to true to send as application/x-www-form-urlencoded</param>
        /// <returns>Deserialized response of T</returns>
        public async Task <CustomHttpResponse <T> > PostForm <T>(RextOptions options, bool isUrlEncoded = false)
        {
            options.Method = HttpMethod.Post;
            options.IsForm = true;
            options.ExpectedResponseFormat = ContentType.Application_JSON;
            options.IsUrlEncoded           = isUrlEncoded;

            var data = await MakeRequest <T>(options);

            return(data);
        }
Пример #8
0
        public static Uri CreateUri(this RextOptions options, string baseUrl = null)
        {
            // construct uri
            string url         = string.Empty;
            string queryString = string.Empty;

            if (!string.IsNullOrEmpty(baseUrl))
            {
                if (options.Url.StartsWith("http://") || options.Url.StartsWith("https://"))
                {
                    throw new UriFormatException("Invalid url format. When using BaseUrl you only have to supply the url part");
                }

                // trim / from end and start to avoid double / in url
                url = $"{baseUrl.TrimEnd('/')}/{options.Url.TrimStart('/')}";
            }
            else
            {
                // use options.Url
                url = options.Url;
            }

            if (!url.StartsWith("http"))
            {
                throw new UriFormatException("Invalid url format. Url scheme is required e.g. HTTP or HTTPS");
            }

            // generate querystring from object if GET
            if (options.Method == HttpMethod.Get && options.Payload != null)
            {
                queryString = options.Payload.ToQueryString();
            }

            Uri uri = new Uri(url);

            string port = uri.Port > 0 ? ":" + uri.Port : string.Empty;

            if (!string.IsNullOrEmpty(uri.Query) && !string.IsNullOrEmpty(queryString))
            {
                queryString = $"&{queryString?.TrimStart('?')}";
            }

            uri = new Uri($"{uri.Scheme}://{uri.Host}{port}{uri.PathAndQuery}{queryString}");

            return(uri);
        }
Пример #9
0
        private async Task <CustomHttpResponse <string> > ProcessRequest(RextOptions options)
        {
            if (this.Client == null)
            {
                throw new ArgumentNullException("HttpClient object cannot be null");
            }

            // validate essential params
            if (string.IsNullOrEmpty(options.Url))
            {
                if (string.IsNullOrEmpty(ConfigurationBundle.HttpConfiguration.BaseUrl))
                {
                    throw new ArgumentNullException(nameof(options.Url), $"{nameof(options.Url)} is required. Provide fully qualified api endpoint.");
                }
                else
                {
                    throw new ArgumentNullException(nameof(options.Url), $"{nameof(options.Url)} is required. Provide the other part of api endpoint to match the BaseUrl.");
                }
            }

            if (options.Method == null)
            {
                throw new ArgumentNullException(nameof(options.Method), $"{nameof(options.Method)} is required. Use GET, POST etc..");
            }

            if (string.IsNullOrEmpty(options.ContentType))
            {
                throw new ArgumentNullException(nameof(options.ContentType), $"{nameof(options.ContentType) } is required. Use application/json, text.plain etc..");
            }

            if (string.IsNullOrEmpty(options.ExpectedResponseFormat))
            {
                throw new ArgumentNullException(nameof(options.ExpectedResponseFormat), $"{nameof(options.ExpectedResponseFormat)} is required. Use application/json, text.plain etc..");
            }


            // execute all user actions pre-call
            ConfigurationBundle.BeforeCall?.Invoke();

            var    rsp            = new CustomHttpResponse <string>();
            var    response       = new HttpResponseMessage();
            string responseString = string.Empty;

            try
            {
                Uri uri = options.CreateUri(ConfigurationBundle.HttpConfiguration.BaseUrl);
                if (uri == null)
                {
                    throw new UriFormatException("Invalid request Uri");
                }

                var requestMsg = new HttpRequestMessage(options.Method, uri);

                // set header if object has value
                if (this.Headers != null)
                {
                    requestMsg.SetHeader(this.Headers);
                }

                if (options.Header != null)
                {
                    requestMsg.SetHeader(options.Header);
                }

                if (ConfigurationBundle.HttpConfiguration.Header != null)
                {
                    requestMsg.SetHeader(ConfigurationBundle.HttpConfiguration.Header);
                }

                if (!string.IsNullOrEmpty(options.ContentType))
                {
                    requestMsg.SetHeader("Accept", options.ExpectedResponseFormat);
                }

                // POST request
                if (options.Method != HttpMethod.Get && options.Payload != null)
                {
                    string strPayload = string.Empty;

                    if (options.IsForm)
                    {
                        strPayload = options.Payload.ToQueryString()?.TrimStart('?');

                        // form-data content post
                        if (!options.IsUrlEncoded)
                        {
                            // handle multipart/form-data
                            var formData      = strPayload.Split('&');
                            var mpfDataBucket = new MultipartFormDataContent();

                            foreach (var i in formData)
                            {
                                var row = i.Split('=');
                                if (row.Length == 2) // check index to avoid null
                                {
                                    mpfDataBucket.Add(new StringContent(row[1]), row[0]);
                                }
                            }

                            requestMsg.Content = mpfDataBucket;
                        }
                        else
                        {
                            // handle application/x-www-form-urlencoded
                            requestMsg.Content = new StringContent(strPayload, Encoding.UTF8, "application/x-www-form-urlencoded");
                        }
                    }
                    else
                    {
                        // convert object to specified content-type
                        if (options.ContentType == ContentType.Application_JSON)
                        {
                            strPayload = options.Payload.ToJson();
                        }
                        else if (options.ContentType == ContentType.Application_XML)
                        {
                            strPayload = options.Payload.ToXml();
                        }
                        else
                        {
                            strPayload = options.Payload.ToString();
                        }

                        requestMsg.Content = new StringContent(strPayload, Encoding.UTF8, options.ContentType);
                    }
                }

                // use stopwatch to monitor httpcall duration
                if (ConfigurationBundle.EnableStopwatch)
                {
                    Stopwatch = new Stopwatch();
                    Stopwatch.Start();
                }

                // check if HttpCompletionOption option is used
                HttpCompletionOption httpCompletionOption = ConfigurationBundle.HttpConfiguration.HttpCompletionOption;
                if (options.HttpCompletionOption.HasValue)
                {
                    if (ConfigurationBundle.HttpConfiguration.HttpCompletionOption != options.HttpCompletionOption.Value)
                    {
                        httpCompletionOption = options.HttpCompletionOption.Value;
                    }
                }

                response = await this.Client.SendAsync(requestMsg, httpCompletionOption, CancellationToken.None).ConfigureAwait(false);

                // set watch value to public member
                if (ConfigurationBundle.EnableStopwatch)
                {
                    Stopwatch.Stop();
                }

                rsp.StatusCode = response.StatusCode;

                if (options.IsStreamResponse)
                {
                    var stream = await response.Content.ReadAsStreamAsync();

                    if (stream.Length > 0)
                    {
                        using (var rd = new StreamReader(stream))
                            responseString = rd.ReadToEnd();
                    }
                }
                else
                {
                    responseString = await response.Content.ReadAsStringAsync();
                }

                if (response.IsSuccessStatusCode)
                {
                    rsp.Content = responseString;
                    rsp.Message = "Http call successful";
                }
                else
                {
                    // this will always run before custom error-code actions
                    // always set ThrowExceptionIfNotSuccessResponse=false if you will use custom error-code actions
                    // perform checks for neccessary override
                    bool throwExIfNotSuccessRsp = options.ThrowExceptionIfNotSuccessResponse ?? ConfigurationBundle.HttpConfiguration.ThrowExceptionIfNotSuccessResponse;
                    if (throwExIfNotSuccessRsp)
                    {
                        throw new RextException($"Server response is {rsp.StatusCode}");
                    }

                    if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                    {
                        rsp.Content = $"Url not found: {requestMsg.RequestUri}";
                    }
                    else
                    {
                        rsp.Content = responseString;
                    }
                    rsp.Message = "Http call completed but not successful";

                    // handle code specific error from user
                    int code = (int)response.StatusCode;
                    if (code > 0 && ConfigurationBundle.StatusCodesToHandle != null && ConfigurationBundle.StatusCodesToHandle.Contains(code))
                    {
                        ConfigurationBundle.OnStatusCode?.Invoke(ReturnStatusCode);
                    }
                }

                // execute all user actions post-call
                ConfigurationBundle.AfterCall?.Invoke();
                return(rsp);
            }
            catch (Exception ex)
            {
                // execute all user actions on error
                ConfigurationBundle.OnError?.Invoke();

                if (ConfigurationBundle.SuppressRextExceptions)
                {
                    if (ex?.Message.ToLower().Contains("a socket operation was attempted to an unreachable host") == true)
                    {
                        rsp.Message = "Network connection error";
                    }
                    else if (ex?.Message.ToLower().Contains("the character set provided in contenttype is invalid") == true)
                    {
                        rsp.Message = "Invald response ContentType. If you are expecting a Stream response then set RextOptions.IsStreamResponse=true";
                    }
                    else
                    {
                        rsp.Message = ex?.Message; //{ex?.InnerException?.Message ?? ex?.InnerException?.Message}";
                    }
                    return(rsp);
                }
                else
                {
                    throw ex;
                }
            }
        }