1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 #pragma warning disable 1634, 1691 5 namespace System.ServiceModel.Dispatcher 6 { 7 using System; 8 using System.Collections; 9 using System.Collections.Generic; 10 using System.Collections.Specialized; 11 using System.Globalization; 12 using System.Reflection; 13 using System.ServiceModel; 14 using System.ServiceModel.Channels; 15 using System.ServiceModel.Description; 16 using System.Text; 17 using System.Xml; 18 using System.ServiceModel.Web; 19 20 class UriTemplateClientFormatter : IClientMessageFormatter 21 { 22 internal Dictionary<int, string> pathMapping; 23 internal Dictionary<int, KeyValuePair<string, Type>> queryMapping; 24 Uri baseUri; 25 IClientMessageFormatter inner; 26 bool innerIsUntypedMessage; 27 bool isGet; 28 string method; 29 QueryStringConverter qsc; 30 int totalNumUTVars; 31 UriTemplate uriTemplate; 32 UriTemplateClientFormatter(OperationDescription operationDescription, IClientMessageFormatter inner, QueryStringConverter qsc, Uri baseUri, bool innerIsUntypedMessage, string contractName)33 public UriTemplateClientFormatter(OperationDescription operationDescription, IClientMessageFormatter inner, QueryStringConverter qsc, Uri baseUri, bool innerIsUntypedMessage, string contractName) 34 { 35 this.inner = inner; 36 this.qsc = qsc; 37 this.baseUri = baseUri; 38 this.innerIsUntypedMessage = innerIsUntypedMessage; 39 Populate(out this.pathMapping, 40 out this.queryMapping, 41 out this.totalNumUTVars, 42 out this.uriTemplate, 43 operationDescription, 44 qsc, 45 contractName); 46 this.method = WebHttpBehavior.GetWebMethod(operationDescription); 47 isGet = this.method == WebHttpBehavior.GET; 48 } 49 DeserializeReply(Message message, object[] parameters)50 public object DeserializeReply(Message message, object[] parameters) 51 { 52 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.GetString(SR2.QueryStringFormatterOperationNotSupportedClientSide))); 53 } 54 SerializeRequest(MessageVersion messageVersion, object[] parameters)55 public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) 56 { 57 object[] innerParameters = new object[parameters.Length - this.totalNumUTVars]; 58 NameValueCollection nvc = new NameValueCollection(); 59 int j = 0; 60 for (int i = 0; i < parameters.Length; ++i) 61 { 62 if (this.pathMapping.ContainsKey(i)) 63 { 64 nvc[this.pathMapping[i]] = parameters[i] as string; 65 } 66 else if (this.queryMapping.ContainsKey(i)) 67 { 68 if (parameters[i] != null) 69 { 70 nvc[this.queryMapping[i].Key] = this.qsc.ConvertValueToString(parameters[i], this.queryMapping[i].Value); 71 } 72 } 73 else 74 { 75 innerParameters[j] = parameters[i]; 76 ++j; 77 } 78 } 79 Message m = inner.SerializeRequest(messageVersion, innerParameters); 80 bool userSetTheToOnMessage = (this.innerIsUntypedMessage && m.Headers.To != null); 81 bool userSetTheToOnOutgoingHeaders = (OperationContext.Current != null && OperationContext.Current.OutgoingMessageHeaders.To != null); 82 if (!userSetTheToOnMessage && !userSetTheToOnOutgoingHeaders) 83 { 84 m.Headers.To = this.uriTemplate.BindByName(this.baseUri, nvc); 85 } 86 if (WebOperationContext.Current != null) 87 { 88 if (isGet) 89 { 90 WebOperationContext.Current.OutgoingRequest.SuppressEntityBody = true; 91 } 92 if (this.method != WebHttpBehavior.WildcardMethod && WebOperationContext.Current.OutgoingRequest.Method != null) 93 { 94 WebOperationContext.Current.OutgoingRequest.Method = this.method; 95 } 96 } 97 else 98 { 99 HttpRequestMessageProperty hrmp; 100 if (m.Properties.ContainsKey(HttpRequestMessageProperty.Name)) 101 { 102 hrmp = m.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; 103 } 104 else 105 { 106 hrmp = new HttpRequestMessageProperty(); 107 m.Properties.Add(HttpRequestMessageProperty.Name, hrmp); 108 } 109 if (isGet) 110 { 111 hrmp.SuppressEntityBody = true; 112 } 113 if (this.method != WebHttpBehavior.WildcardMethod) 114 { 115 hrmp.Method = this.method; 116 } 117 } 118 return m; 119 } 120 GetUTStringOrDefault(OperationDescription operationDescription)121 internal static string GetUTStringOrDefault(OperationDescription operationDescription) 122 { 123 string utString = WebHttpBehavior.GetWebUriTemplate(operationDescription); 124 if (utString == null && WebHttpBehavior.GetWebMethod(operationDescription) == WebHttpBehavior.GET) 125 { 126 utString = MakeDefaultGetUTString(operationDescription); 127 } 128 if (utString == null) 129 { 130 utString = operationDescription.Name; // note: not + "/*", see 8988 and 9653 131 } 132 return utString; 133 } 134 Populate(out Dictionary<int, string> pathMapping, out Dictionary<int, KeyValuePair<string, Type>> queryMapping, out int totalNumUTVars, out UriTemplate uriTemplate, OperationDescription operationDescription, QueryStringConverter qsc, string contractName)135 internal static void Populate(out Dictionary<int, string> pathMapping, 136 out Dictionary<int, KeyValuePair<string, Type>> queryMapping, 137 out int totalNumUTVars, 138 out UriTemplate uriTemplate, 139 OperationDescription operationDescription, 140 QueryStringConverter qsc, 141 string contractName) 142 { 143 pathMapping = new Dictionary<int, string>(); 144 queryMapping = new Dictionary<int, KeyValuePair<string, Type>>(); 145 string utString = GetUTStringOrDefault(operationDescription); 146 uriTemplate = new UriTemplate(utString); 147 List<string> neededPathVars = new List<string>(uriTemplate.PathSegmentVariableNames); 148 List<string> neededQueryVars = new List<string>(uriTemplate.QueryValueVariableNames); 149 Dictionary<string, byte> alreadyGotVars = new Dictionary<string, byte>(StringComparer.OrdinalIgnoreCase); 150 totalNumUTVars = neededPathVars.Count + neededQueryVars.Count; 151 for (int i = 0; i < operationDescription.Messages[0].Body.Parts.Count; ++i) 152 { 153 MessagePartDescription mpd = operationDescription.Messages[0].Body.Parts[i]; 154 string parameterName = mpd.XmlName.DecodedName; 155 if (alreadyGotVars.ContainsKey(parameterName)) 156 { 157 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 158 SR2.GetString(SR2.UriTemplateVarCaseDistinction, operationDescription.XmlName.DecodedName, contractName, parameterName))); 159 } 160 List<string> neededPathCopy = new List<string>(neededPathVars); 161 foreach (string pathVar in neededPathCopy) 162 { 163 if (string.Compare(parameterName, pathVar, StringComparison.OrdinalIgnoreCase) == 0) 164 { 165 if (mpd.Type != typeof(string)) 166 { 167 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 168 SR2.GetString(SR2.UriTemplatePathVarMustBeString, operationDescription.XmlName.DecodedName, contractName, parameterName))); 169 } 170 pathMapping.Add(i, parameterName); 171 alreadyGotVars.Add(parameterName, 0); 172 neededPathVars.Remove(pathVar); 173 } 174 } 175 List<string> neededQueryCopy = new List<string>(neededQueryVars); 176 foreach (string queryVar in neededQueryCopy) 177 { 178 if (string.Compare(parameterName, queryVar, StringComparison.OrdinalIgnoreCase) == 0) 179 { 180 if (!qsc.CanConvert(mpd.Type)) 181 { 182 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( 183 SR2.GetString(SR2.UriTemplateQueryVarMustBeConvertible, operationDescription.XmlName.DecodedName, contractName, parameterName, mpd.Type, qsc.GetType().Name))); 184 } 185 queryMapping.Add(i, new KeyValuePair<string, Type>(parameterName, mpd.Type)); 186 alreadyGotVars.Add(parameterName, 0); 187 neededQueryVars.Remove(queryVar); 188 } 189 } 190 } 191 if (neededPathVars.Count != 0) 192 { 193 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString( 194 SR2.UriTemplateMissingVar, operationDescription.XmlName.DecodedName, contractName, neededPathVars[0]))); 195 } 196 if (neededQueryVars.Count != 0) 197 { 198 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString 199 (SR2.UriTemplateMissingVar, operationDescription.XmlName.DecodedName, contractName, neededQueryVars[0]))); 200 } 201 } 202 MakeDefaultGetUTString(OperationDescription od)203 static string MakeDefaultGetUTString(OperationDescription od) 204 { 205 StringBuilder sb = new StringBuilder(od.XmlName.DecodedName); 206 //sb.Append("/*"); // note: not + "/*", see 8988 and 9653 207 if (!WebHttpBehavior.IsUntypedMessage(od.Messages[0])) 208 { 209 sb.Append("?"); 210 foreach (MessagePartDescription mpd in od.Messages[0].Body.Parts) 211 { 212 string parameterName = mpd.XmlName.DecodedName; 213 sb.Append(parameterName); 214 sb.Append("={"); 215 sb.Append(parameterName); 216 sb.Append("}&"); 217 } 218 sb.Remove(sb.Length - 1, 1); 219 } 220 return sb.ToString(); 221 } 222 } 223 } 224