/// <summary> /// Remote API method execution using HTTP "post" method. /// </summary> private DataType ExecutePostHttpRequest <ContractType, DataType>(ViddlerMethodAttribute methodAttribute, StringDictionary parameters) { StringBuilder requestPath = new StringBuilder(); StringBuilder requestData = new StringBuilder(); requestPath.Append((this.EnableSsl && methodAttribute.IsSecure) ? this.SecureBaseUrl : this.BaseUrl); requestPath.Append(methodAttribute.MethodName); requestPath.Append(".xml"); requestData.Append("key="); requestData.Append(this.ApiKey); if (methodAttribute.IsSessionRequired) { requestData.Append("&sessionid="); requestData.Append(this.SessionId); } if (parameters != null && parameters.Keys.Count > 0) { foreach (string key in parameters.Keys) { requestData.Append("&"); requestData.Append(key); requestData.Append("="); requestData.Append(ViddlerHelper.EncodeRequestData(parameters[key])); } } byte[] postDataBytes = System.Text.Encoding.UTF8.GetBytes(requestData.ToString()); try { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestPath.ToString()); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; request.UserAgent = string.Concat("Microsoft .NET, ", this.GetType().Assembly.FullName); request.AllowWriteStreamBuffering = true; request.Accept = "text/xml"; request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; request.KeepAlive = true; request.Timeout = int.MaxValue; request.ReadWriteTimeout = int.MaxValue; using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(postDataBytes, 0, postDataBytes.Length); requestStream.Flush(); } DataType responseObject = ViddlerService.HandleHttpResponse <ContractType, DataType>(request, methodAttribute, this.DumpFolder); return(responseObject); } catch (System.Net.WebException exception) { throw ViddlerService.HandleHttpError(exception); } }
/// <summary> /// Analyzes target response data object and collects properties of promitive type. /// </summary> internal static Dictionary <string, Type> GetExpectedPrimitiveTypeNodes(Type type) { Dictionary <string, Type> nodeNameCollection = new Dictionary <string, Type>(); ViddlerMethodAttribute methodAttribute = ViddlerHelper.GetMethodAttribute(type); if (methodAttribute != null) { string xpath = string.Concat("/", methodAttribute.ElementName, "/"); ViddlerHelper.CollectExpectedPrimitiveTypeNodes(xpath, type, nodeNameCollection, 0); } return(nodeNameCollection); }
/// <summary> /// Analyzes target response data object and collects properties of promitive type. /// </summary> private static void CollectExpectedPrimitiveTypeNodes(string xpath, Type currentType, Dictionary <string, Type> nodeNameCollection, int depth) { if (depth > 10) { throw new StackOverflowException(string.Concat("Potential stack overflow due to recursively nested data types: ", xpath, "; current depth: ", depth)); } foreach (PropertyInfo propertyInfo in currentType.GetProperties()) { if (propertyInfo.PropertyType.IsValueType) { string elementName = ViddlerHelper.GetXmlElementName(propertyInfo); if (!string.IsNullOrEmpty(elementName)) { nodeNameCollection.Add(string.Concat(xpath, elementName), propertyInfo.PropertyType); } } else if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable <>)) { Type genericArgumentType = propertyInfo.PropertyType.GetGenericArguments()[0]; if (genericArgumentType.IsValueType) { string elementName = ViddlerHelper.GetXmlElementName(propertyInfo); if (!string.IsNullOrEmpty(elementName)) { nodeNameCollection.Add(string.Concat(xpath, elementName), genericArgumentType); } } } else if (propertyInfo.PropertyType.Assembly == typeof(ViddlerHelper).Assembly) { string elementName = ViddlerHelper.GetXmlElementName(propertyInfo); ViddlerHelper.CollectExpectedPrimitiveTypeNodes(string.Concat(xpath, elementName, "/"), propertyInfo.PropertyType, nodeNameCollection, depth + 1); } else if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List <>) && propertyInfo.PropertyType.GetGenericArguments()[0].Assembly == typeof(ViddlerHelper).Assembly) { string elementName = ViddlerHelper.GetXmlElementName(propertyInfo); if (string.IsNullOrEmpty(elementName)) { elementName = ViddlerHelper.GetXmlArrayName(propertyInfo); if (!string.IsNullOrEmpty(elementName)) { elementName = string.Concat(elementName, "/", ViddlerHelper.GetXmlArrayItemName(propertyInfo)); } } ViddlerHelper.CollectExpectedPrimitiveTypeNodes(string.Concat(xpath, elementName, "/"), propertyInfo.PropertyType.GetGenericArguments()[0], nodeNameCollection, depth + 1); } } }
/// <summary> /// Handles a web response from the remote server. /// </summary> private static DataType HandleHttpResponse <ContractType, DataType>(HttpWebRequest request, ViddlerMethodAttribute methodAttribute, string dumpPath) { DataType responseObject = default(DataType); using (WebResponse response = request.GetResponse()) { using (Stream responseStream = response.GetResponseStream()) { using (MemoryStream stream = new MemoryStream()) { int count; byte[] buffer = new byte[1024]; do { count = responseStream.Read(buffer, 0, 1024); stream.Write(buffer, 0, count); } while (count > 0); // copy response stream to "seek-enabled" stream if (!string.IsNullOrEmpty(dumpPath)) { ViddlerService.DumpResposeToFile(dumpPath, stream, methodAttribute.MethodName, request.RequestUri.ToString()); } stream.Position = 0; string messageType = string.Empty; XmlReaderSettings readerSettings = new XmlReaderSettings(); readerSettings.CloseInput = false; readerSettings.IgnoreComments = true; readerSettings.IgnoreProcessingInstructions = true; using (XmlReader reader = XmlReader.Create(stream, readerSettings)) { if (reader.MoveToContent() == XmlNodeType.Element) { messageType = reader.LocalName; } } stream.Position = 0; if (messageType.Equals(methodAttribute.ElementName, StringComparison.Ordinal)) { try { XmlSerializer serializer = new XmlSerializer(typeof(ContractType)); responseObject = (DataType)serializer.Deserialize(stream); } catch (Exception exception) { if (exception.InnerException != null && exception.InnerException.GetType() == typeof(System.FormatException)) // XmlSerializer has problems with parsing primitive types - removing empty nodes, which are deserialized to properties of primitive type { stream.Position = 0; Dictionary <string, Type> nodesToCheck = ViddlerHelper.GetExpectedPrimitiveTypeNodes(typeof(ContractType)); XmlDocument responseXml = new XmlDocument(); responseXml.PreserveWhitespace = true; responseXml.Load(stream); foreach (string node in nodesToCheck.Keys) { foreach (XmlNode xmlNode in responseXml.SelectNodes(node)) { if (string.IsNullOrEmpty(xmlNode.InnerText.Trim())) { xmlNode.ParentNode.RemoveChild(xmlNode); } } } using (MemoryStream xmlStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(responseXml.OuterXml))) { XmlSerializer serializer = new XmlSerializer(typeof(ContractType)); responseObject = (DataType)serializer.Deserialize(xmlStream); } } else { throw; } } } else if (messageType.Equals("error", StringComparison.Ordinal)) { XmlSerializer serializer = new XmlSerializer(typeof(ViddlerResponseError)); ViddlerResponseError errorObject = (ViddlerResponseError)serializer.Deserialize(stream); throw new ViddlerRequestException(errorObject, null); } else { XmlDocument responseXml = new XmlDocument(); responseXml.PreserveWhitespace = true; responseXml.Load(stream); throw new System.InvalidOperationException(responseXml.OuterXml); } } } } return(responseObject); }
/// <summary> /// Remote API method execution. /// </summary> internal DataType ExecuteHttpRequest <ContractType, DataType>(StringDictionary parameters, string fileName, Stream fileStream, Data.UploadEndPoint endPoint) { ViddlerMethodAttribute methodAttribute = ViddlerHelper.GetMethodAttribute(typeof(ContractType)); DataType responseObject = default(DataType); try { if (methodAttribute == null) { throw new System.InvalidOperationException(string.Concat("ViddlerMethodAttribute missing for type \"", typeof(ContractType).ToString(), "\".")); } if (this.BeginRequest != null) { this.BeginRequest(this, new ViddlerRequestEventArgs(typeof(ContractType), parameters, (fileStream != null))); } if (methodAttribute.IsSessionRequired && !this.IsAuthenticated) { throw new System.InvalidOperationException("The current method requires authenticated user."); } if (fileStream != null && !fileStream.CanSeek) { throw new System.ArgumentException("The file stream does not support seeking.", "fileStream"); } switch (methodAttribute.RequestType) { case ViddlerRequestType.Post: { responseObject = this.ExecutePostHttpRequest <ContractType, DataType>(methodAttribute, parameters); break; } case ViddlerRequestType.Multipart: { responseObject = this.ExecuteMultipartHttpRequest <ContractType, DataType>(methodAttribute, parameters, fileName, fileStream, endPoint); break; } default: { responseObject = this.ExecuteGetHttpRequest <ContractType, DataType>(methodAttribute, parameters); break; } } if (this.EndRequest != null) { this.EndRequest(this, new ViddlerRequestEventArgs(typeof(ContractType), parameters, (fileStream != null))); } } catch (Exception exception) { if (this.Error != null) { this.Error(this, new ViddlerRequestErrorEventArgs(typeof(ContractType), parameters, (fileStream != null), exception)); } else { throw; } } return(responseObject); }