private Uri GetResourceUri(Data.RestRequest requestDetails) { string queryString = GetHttpRequestQueryString(requestDetails); // C# behaves differently from Java when concatenating nulls: // Java: "string" + null = "stringnull" (the string "null" is substituted for null); // C#: "string" + null = "string" (the empty string is substituted for null). string uriString = requestDetails.Resource + queryString; // .NET System.Uri constructor will always check whether all characters are escaped // and escape those that aren't so no need to specify whether the URI is escaped or // not. Uri uri = null; try { // Assume this is a relative URL as host address, passed into the Execute method // separately, is invalid if null or blank. uri = new Uri(uriString, UriKind.Relative); } catch (Exception ex) { throw new InvalidOperationException("Problem when building URI: " + uriString, ex); } return(uri); }
private void ValidateRequest(RestSharp.IRestClient client, Data.RestRequest requestDetails) { // Client cannot be null. if (client.BaseUrl == null || string.IsNullOrWhiteSpace(client.BaseUrl.AbsoluteUri)) { throw new ArgumentException("Host address is not set: " + "Please pass a valid host address."); } if (requestDetails == null) { throw new System.ArgumentException("HTTP request details not supplied."); } if (requestDetails.HttpMethod == Data.RestRequest.Method.NotSet) { throw new System.ArgumentException( "HTTP request method is not set (eg \"GET\", \"POST\")."); } if (string.IsNullOrWhiteSpace(requestDetails.Resource)) { throw new System.ArgumentException("Relative URL of resource is not set."); } }
private void AddHttpRequestHeaders(RestSharp.RestRequest requestToBuild, Data.RestRequest requestDetails) { // NB: RestSharp will default content type to XML if not set explicitly. foreach (Data.RestData.Header header in requestDetails.Headers) { requestToBuild.AddHeader(header.Name, header.Value); } }
private void ConfigureRequestMultipartFileUpload(RestSharp.RestRequest request, Data.RestRequest requestDetails, LinkedHashMap <string, Data.RestMultipart> multipartFiles) { request.AddHeader("Content-Type", "multipart/form-data"); foreach (KeyValuePair <string, RestMultipart> multipartFile in multipartFiles) { ConfigureMultipart(request, multipartFile.Key, multipartFile.Value); } }
private string GetHttpRequestQueryString(Data.RestRequest requestDetails) { string queryString = requestDetails.Query; if (StringHelper.IsNullOrBlank(queryString)) { return(string.Empty); } // Ensure query string starts with "?". queryString = queryString.TrimStart('?').Trim(); queryString = "?" + queryString; return(queryString); }
private RestSharp.Method GetHttpMethod(Data.RestRequest restRequest) { // No way to tell if restRequest.Method has not been set. If not set explicitly it // will have value 0 = Get. RestSharp.Method httpMethod; bool ignoreCase = true; if (Enum.TryParse(restRequest.HttpMethod.ToString(), ignoreCase, out httpMethod)) { return(httpMethod); } // As at v105.2.3, on 26 Feb 2017, RestSharp is unable to handle TRACE HTTP methods, // although the RestClient in the Java RestFixture can. string errorMessage = string.Format("RestRequst.HttpMethod {0} cannot be handled " + "by RestSharp.", restRequest.HttpMethod); throw new ArgumentException(errorMessage); }
/// <summary> /// Builds the HTTP request that will be sent. /// </summary> /// <param name="requestDetails">The RestClient.RestRequest containing details of the request /// to send.</param> /// <returns>A RestSharp.RestRequest object containing details of the request to send.</returns> /// <remarks>Loosely based on RestClient.RestClientImpl.configureHttpMethod, which is /// designed around the Apache HttpClient library, written in Java. This is very different /// from the .NET System.Net library so cannot be ported directly; the HttpClient must be /// rewritten from scratch.</remarks> private RestSharp.RestRequest BuildRequest(Data.RestRequest requestDetails) { Uri resourceUri = GetResourceUri(requestDetails); RestSharp.Method httpMethod = GetHttpMethod(requestDetails); RestSharp.RestRequest request = new RestSharp.RestRequest(resourceUri, httpMethod); AddHttpRequestHeaders(request, requestDetails); // Request type doesn't expect a body (eg GET, DELETE). if (!IsEntityEnclosingMethod(httpMethod)) { return(request); } // Simple file upload. string fileName = requestDetails.FileName; if (!string.IsNullOrWhiteSpace(fileName)) { if (!File.Exists(fileName)) { throw new ArgumentException("File not found: " + fileName); } request.AddFile("file", fileName); return(request); } // Multipart file upload. LinkedHashMap <string, Data.RestMultipart> multipartFiles = requestDetails.MultipartFileNames; if ((multipartFiles != null) && (multipartFiles.Count > 0)) { ConfigureRequestMultipartFileUpload(request, requestDetails, multipartFiles); return(request); } if (string.IsNullOrWhiteSpace(requestDetails.Body)) { return(request); } // Request includes a body, eg standard POST or PUT. string contentType = requestDetails.ContentType; if (contentType != null) { if (contentType.Trim().Length == 0) { contentType = null; } else { contentType = contentType.ToLower(); } } string body = requestDetails.Body.Trim(); if (string.IsNullOrWhiteSpace(contentType)) { // RestSharp has only two DataFormats: Json and Xml. if (body.StartsWith("{") || body.StartsWith("[")) { request.RequestFormat = DataFormat.Json; contentType = "application/json"; } else { request.RequestFormat = DataFormat.Xml; contentType = "application/xml"; } } else if (contentType.Contains("json")) { request.RequestFormat = DataFormat.Json; contentType = requestDetails.ContentType; } else { request.RequestFormat = DataFormat.Xml; contentType = requestDetails.ContentType; } // requestDetails.Body is a string, and RestRequest.AddBody, AddJsonBody // and AddXmlBody take objects as arguments, not strings. Although they // won't throw exceptions, the result is not as expected if an object is // serialized to JSON or XML before being passed to one of the AddBody // methods. // // For example, if a Book object is serialized to JSON and the JSON string // is passed to AddBody or AddJsonBody the body sent across the wire is: // // "{\"Id\":10,\"Title\":\"The Meaning of Liff\",\"Year\":1983}" // // when it should be: // // {Id: 10, Title: "The Meaning of Liff", Year: 1983} // The incorrect format of the JSON in the request body will prevent it // from being desrialized correctly at the server end. // Instead of using one of the AddBody methods, use // AddParameter(..., ..., ParameterType.RequestBody) // which can handle objects serialized to JSON or XML strings. request.AddParameter(contentType, body, ParameterType.RequestBody); return(request); }
public Data.RestResponse Execute(string baseAddress, Data.RestRequest requestDetails) { this.BaseUrlString = baseAddress; RestSharp.IRestClient client = this.Client; // We're using RestSharp to execute HTTP requests. RestSharp copes with trailing "/" // on BaseUrl and leading "/" on Resource, whether one or both of the "/" are present // or absent. ValidateRequest(client, requestDetails); if (requestDetails.TransactionId == null) { requestDetails.TransactionId = Convert.ToInt64(DateTimeHelper.CurrentUnixTimeMillis()); } client.FollowRedirects = requestDetails.FollowRedirect; RestSharp.RestRequest request = BuildRequest(requestDetails); if (LOG.IsDebugEnabled) { try { LOG.Info("Http Request : {0} {1}", request.Method, client.BuildUri(request).AbsoluteUri); } catch (Exception e) { LOG.Error(e, "Exception in debug : {0}", e.Message); } // Request Header LogRequestHeaders(request); // Request Body if (IsEntityEnclosingMethod(request.Method)) { try { Parameter body = request.Parameters .Where(p => p.Type == ParameterType.RequestBody).FirstOrDefault(); LOG.Debug("Http Request Body : {0}", body.Value); } catch (IOException e) { LOG.Error(e, "Error in reading request body in debug : {0}", e.Message); } } } // Prepare Response Data.RestResponse responseDetails = new Data.RestResponse(); responseDetails.TransactionId = requestDetails.TransactionId; responseDetails.Resource = requestDetails.Resource; try { IRestResponse response = client.Execute(request); foreach (RestSharp.Parameter responseHeader in response.Headers) { responseDetails.addHeader(responseHeader.Name, responseHeader.Value.ToString()); } responseDetails.StatusCode = (int)response.StatusCode; responseDetails.StatusText = response.StatusDescription; // The Java byte data type is actually a signed byte, equivalent to sbyte in .NET. // Data.RestResponse is a direct port of the Java class so it uses // the sbyte data type. The strange conversion from byte[] to sbyte[] was from // the answer to Stackoverflow question http://stackoverflow.com/questions/25759878/convert-byte-to-sbyte // TODO: Check RestFixture code to see whether Data.RestResponse.RawBody can be converted to byte[]. responseDetails.RawBody = response.RawBytes; // Debug if (LOG.IsDebugEnabled) { // Not necessarily the same as the request URI. The response URI is the URI // that finally responds to the request, after any redirects. LOG.Debug("Http Request Path : {0}", response.ResponseUri); LogRequestHeaders(request); // Apache HttpClient.HttpMethod.getStatusLine() returns the HTTP response // status line. Can't do this with RestSharp as it cannot retrieve the // protocol version which is at the start of the status line. So just log // the status code and description. // Looks like they added ProtocolVersion to RestSharp in issue 795 (commit // 52c18a8) but it's only available in the FRAMEWORK builds. LOG.Debug("Http Response Status : {0} {1}", (int)response.StatusCode, response.StatusDescription); LOG.Debug("Http Response Body : {0}", response.Content); } if (IsResponseError(response)) { LOG.Error(response.ErrorException, response.ErrorMessage); string message = "Http call failed"; if (!string.IsNullOrWhiteSpace(response.ErrorMessage)) { message = response.ErrorMessage.Trim(); } throw new System.InvalidOperationException(message, response.ErrorException); } } catch (Exception e) { string message = "Http call failed"; throw new System.InvalidOperationException(message, e); } LOG.Debug("response: {0}", responseDetails); return(responseDetails); }
// Unfortunately both this RestClient library and RestSharp have classes called // RestRequest and RestResponse. So we must always specify which RestRequest or // RestResponse class we're using. public Data.RestResponse Execute(Data.RestRequest requestDetails) { return(Execute(this.BaseUrlString, requestDetails)); }