/// <summary> /// Reads a response from the service and converts it to the specified return type. /// </summary> /// <param name="result">The result object which contains information about which operation was performed.</param> /// <param name="returnType">Type which should be returned.</param> /// <returns></returns> /// <exception cref="OpenRiaServices.DomainServices.Client.DomainOperationException">On server errors which did not produce expected output</exception> /// <exception cref="FaultException{DomainServiceFault}">If server returned a DomainServiceFault</exception> private object ReadResponse(WebApiDomainClientAsyncResult result, Type returnType) { HttpResponseMessage response = ((Task<HttpResponseMessage>)result.InnerAsyncResult).Result; if (!response.IsSuccessStatusCode) { var message = string.Format(Resources.DomainClient_UnexpectedHttpStatusCode, (int)response.StatusCode, response.StatusCode); if (response.StatusCode == HttpStatusCode.BadRequest) throw new DomainOperationException(message, OperationErrorStatus.NotSupported, (int)response.StatusCode, null); else if (response.StatusCode == HttpStatusCode.Unauthorized) throw new DomainOperationException(message, OperationErrorStatus.Unauthorized, (int)response.StatusCode, null); else throw new DomainOperationException(message, OperationErrorStatus.ServerError, (int)response.StatusCode, null); } var ms = response.Content.ReadAsStreamAsync().Result; using (var reader = System.Xml.XmlDictionaryReader.CreateBinaryReader(ms, System.Xml.XmlDictionaryReaderQuotas.Max)) { reader.Read(); // Domain Fault if (reader.LocalName == "Fault") { throw ReadFaultException(reader, result.OperationName); } else { // Validate that we are no on ****Response node VerifyReaderIsAtNode(reader, result.OperationName, "Response"); reader.ReadStartElement(); // Read to next which should be ****Result // Validate that we are no on ****Result node VerifyReaderIsAtNode(reader, result.OperationName, "Result"); var serializer = GetSerializer(returnType); return serializer.ReadObject(reader, verifyObjectName: false); } } }
/// <summary> /// Initiates a GET request for the given operation and return the server respose (as a task). /// </summary> /// <param name="result">The result object which contains information about which operation was performed.</param> /// <param name="parameters">The parameters to the server method, or <c>null</c> if no parameters.</param> /// <param name="queryOptions">The query options if any.</param> /// <returns></returns> private Task<HttpResponseMessage> GetAsync(WebApiDomainClientAsyncResult result, IDictionary<string, object> parameters, IList<ServiceQueryPart> queryOptions) { int i = 0; var uriBuilder = new StringBuilder(); uriBuilder.Append(result.OperationName); // Parameters if (parameters != null && parameters.Count > 0) { foreach (var param in parameters) { uriBuilder.Append(i++ == 0 ? '?' : '&'); uriBuilder.Append(Uri.EscapeDataString(param.Key)); uriBuilder.Append("="); if (param.Value != null) { var value = QueryStringConverter.ConvertValueToString(param.Value, param.Value.GetType()); uriBuilder.Append(Uri.EscapeDataString(value)); } } } // Query options if (queryOptions != null && queryOptions.Count > 0) { foreach (var queryPart in queryOptions) { uriBuilder.Append(i++ == 0 ? "?$" : "&$"); uriBuilder.Append(queryPart.QueryOperator); uriBuilder.Append("="); uriBuilder.Append(Uri.EscapeDataString(queryPart.Expression)); } } // TODO: Switch to POST if uri becomes to long, we can do so by returning nul ...l var uri = uriBuilder.ToString(); return HttpClient.GetAsync(uri); }
/// <summary> /// Initiates a POST request for the given operation and return the server respose (as a task). /// </summary> /// <param name="result">The result object which contains information about which operation was performed.</param> /// <param name="parameters">The parameters to the server method, or <c>null</c> if no parameters.</param> /// <param name="queryOptions">The query options if any.</param> /// <returns></returns> private Task<HttpResponseMessage> PostAsync(WebApiDomainClientAsyncResult result, IDictionary<string, object> parameters, IList<ServiceQueryPart> queryOptions) { var ms = new System.IO.MemoryStream(); var writer = System.Xml.XmlDictionaryWriter.CreateBinaryWriter(ms); // Write message { var rootNamespace = "http://tempuri.org/"; bool hasQueryOptions = (queryOptions != null && queryOptions.Count > 0); if (hasQueryOptions) { writer.WriteStartElement("MessageRoot"); writer.WriteStartElement("QueryOptions"); foreach (var queryOption in queryOptions) { writer.WriteStartElement("QueryOption"); writer.WriteAttributeString("Name", queryOption.QueryOperator); writer.WriteAttributeString("Value", queryOption.Expression); writer.WriteEndElement(); } writer.WriteEndElement(); } writer.WriteStartElement(result.OperationName, rootNamespace); // <OperationName> // Write all parameters if (parameters != null && parameters.Count > 0) { foreach (var param in parameters) { writer.WriteStartElement(param.Key); // <ParameterName> if (param.Value != null) { var serializer = GetSerializer(param.Value.GetType()); serializer.WriteObjectContent(writer, param.Value); } else { // Null input writer.WriteAttributeString("i","nil", "http://www.w3.org/2001/XMLSchema-instance", "true"); } writer.WriteEndElement(); // </ParameterName> } } writer.WriteEndDocument(); // </OperationName> and </MessageRoot> if present writer.Flush(); } // TODO: Custom optimized implementation of StreamContent (like WebApi's PushStreamContent)? // so we don't have to have special dispose logic below ms.Seek(0, System.IO.SeekOrigin.Begin); var content = new StreamContent(ms); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/msbin1"); // Keep reference to dictionary so that we can dispose of it correctly after the request has been posted // otherwise there is a small risk that it will be finalized and that it might corrupt the stream return HttpClient.PostAsync(result.OperationName, content) .ContinueWith(res => { writer.Dispose(); return res; }) .Unwrap(); }
/// <summary> /// Invokes a web request for the operation defined by the <see cref="WebApiDomainClientAsyncResult"/> /// </summary> /// <param name="result">The result.</param> /// <param name="hasSideEffects">if set to <c>true</c> then the request will always be a POST operation.</param> /// <param name="parameters">The parameters.</param> /// <param name="queryOptions">The query options.</param> private IAsyncResult BeginWebRequest(WebApiDomainClientAsyncResult result, bool hasSideEffects, IDictionary<string, object> parameters, IList<ServiceQueryPart> queryOptions) { Task<HttpResponseMessage> response = null; // Add parameters to query string for get methods if (!hasSideEffects) { response = GetAsync(result, parameters, queryOptions); } // It is a POST if (response == null) { response = PostAsync(result, parameters, queryOptions); } result.InnerAsyncResult = response; response.ContinueWith(task => { result.Complete(); }, TaskContinuationOptions.ExecuteSynchronously); return result; }