1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.ServiceModel.Dispatcher
6 {
7     using System;
8     using System.ServiceModel;
9     using System.ServiceModel.Channels;
10     using System.ServiceModel.Description;
11     using System.Collections.Generic;
12     using System.Xml;
13     using System.Runtime.Serialization;
14     using DiagnosticUtility = System.ServiceModel.DiagnosticUtility;
15     using System.ServiceModel.Web;
16 
17     abstract class SingleBodyParameterMessageFormatter : IDispatchMessageFormatter, IClientMessageFormatter
18     {
19         string contractName;
20         string contractNs;
21         bool isRequestFormatter;
22         string operationName;
23         string serializerType;
24 
SingleBodyParameterMessageFormatter(OperationDescription operation, bool isRequestFormatter, string serializerType)25         protected SingleBodyParameterMessageFormatter(OperationDescription operation, bool isRequestFormatter, string serializerType)
26         {
27             if (operation == null)
28             {
29                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operation");
30             }
31             this.contractName = operation.DeclaringContract.Name;
32             this.contractNs = operation.DeclaringContract.Namespace;
33             this.operationName = operation.Name;
34             this.isRequestFormatter = isRequestFormatter;
35             this.serializerType = serializerType;
36         }
37 
38         protected string ContractName
39         {
40             get { return this.contractName; }
41         }
42 
43         protected string ContractNs
44         {
45             get { return this.contractNs; }
46         }
47 
48         protected string OperationName
49         {
50             get { return this.operationName; }
51         }
52 
CreateXmlAndJsonClientFormatter(OperationDescription operation, Type type, bool isRequestFormatter, UnwrappedTypesXmlSerializerManager xmlSerializerManager)53         public static IClientMessageFormatter CreateXmlAndJsonClientFormatter(OperationDescription operation, Type type, bool isRequestFormatter, UnwrappedTypesXmlSerializerManager xmlSerializerManager)
54         {
55             IClientMessageFormatter xmlFormatter = CreateClientFormatter(operation, type, isRequestFormatter, false, xmlSerializerManager);
56             if (!WebHttpBehavior.SupportsJsonFormat(operation))
57             {
58                 return xmlFormatter;
59             }
60             IClientMessageFormatter jsonFormatter = CreateClientFormatter(operation, type, isRequestFormatter, true, xmlSerializerManager);
61             Dictionary<WebContentFormat, IClientMessageFormatter> map = new Dictionary<WebContentFormat, IClientMessageFormatter>();
62             map.Add(WebContentFormat.Xml, xmlFormatter);
63             map.Add(WebContentFormat.Json, jsonFormatter);
64             return new DemultiplexingClientMessageFormatter(map, xmlFormatter);
65         }
66 
CreateXmlAndJsonDispatchFormatter(OperationDescription operation, Type type, bool isRequestFormatter, UnwrappedTypesXmlSerializerManager xmlSerializerManager, string callbackParameterName)67         public static IDispatchMessageFormatter CreateXmlAndJsonDispatchFormatter(OperationDescription operation, Type type, bool isRequestFormatter, UnwrappedTypesXmlSerializerManager xmlSerializerManager, string callbackParameterName)
68         {
69             IDispatchMessageFormatter xmlFormatter = CreateDispatchFormatter(operation, type, isRequestFormatter, false, xmlSerializerManager, null);
70             if (!WebHttpBehavior.SupportsJsonFormat(operation))
71             {
72                 return xmlFormatter;
73             }
74             IDispatchMessageFormatter jsonFormatter = CreateDispatchFormatter(operation, type, isRequestFormatter, true, xmlSerializerManager, callbackParameterName);
75             Dictionary<WebContentFormat, IDispatchMessageFormatter> map = new Dictionary<WebContentFormat, IDispatchMessageFormatter>();
76             map.Add(WebContentFormat.Xml, xmlFormatter);
77             map.Add(WebContentFormat.Json, jsonFormatter);
78             return new DemultiplexingDispatchMessageFormatter(map, xmlFormatter);
79         }
80 
DeserializeReply(Message message, object[] parameters)81         public object DeserializeReply(Message message, object[] parameters)
82         {
83             if (isRequestFormatter)
84             {
85                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.FormatterCannotBeUsedForReplyMessages)));
86             }
87             return ReadObject(message);
88         }
89 
DeserializeRequest(Message message, object[] parameters)90         public void DeserializeRequest(Message message, object[] parameters)
91         {
92             if (!isRequestFormatter)
93             {
94                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.FormatterCannotBeUsedForRequestMessages)));
95             }
96 
97             parameters[0] = ReadObject(message);
98         }
99 
SerializeReply(MessageVersion messageVersion, object[] parameters, object result)100         public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
101         {
102             if (isRequestFormatter)
103             {
104                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.FormatterCannotBeUsedForReplyMessages)));
105             }
106             Message message = Message.CreateMessage(messageVersion, (string)null, CreateBodyWriter(result));
107             if (result == null)
108             {
109                 SuppressReplyEntityBody(message);
110             }
111             AttachMessageProperties(message, false);
112             return message;
113         }
114 
SerializeRequest(MessageVersion messageVersion, object[] parameters)115         public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
116         {
117             if (!isRequestFormatter)
118             {
119                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.FormatterCannotBeUsedForRequestMessages)));
120             }
121             Message message = Message.CreateMessage(messageVersion, (string)null, CreateBodyWriter(parameters[0]));
122             if (parameters[0] == null)
123             {
124                 SuppressRequestEntityBody(message);
125             }
126             AttachMessageProperties(message, true);
127             return message;
128         }
129 
CreateClientFormatter(OperationDescription operation, Type type, bool isRequestFormatter, bool useJson, UnwrappedTypesXmlSerializerManager xmlSerializerManager)130         internal static IClientMessageFormatter CreateClientFormatter(OperationDescription operation, Type type, bool isRequestFormatter, bool useJson, UnwrappedTypesXmlSerializerManager xmlSerializerManager)
131         {
132             if (type == null)
133             {
134                 return new NullMessageFormatter(false, null);
135             }
136             else if (useJson)
137             {
138                 return CreateJsonFormatter(operation, type, isRequestFormatter);
139             }
140             else
141             {
142                 return CreateXmlFormatter(operation, type, isRequestFormatter, xmlSerializerManager);
143             }
144         }
145 
CreateDispatchFormatter(OperationDescription operation, Type type, bool isRequestFormatter, bool useJson, UnwrappedTypesXmlSerializerManager xmlSerializerManager, string callbackParameterName)146         internal static IDispatchMessageFormatter CreateDispatchFormatter(OperationDescription operation, Type type, bool isRequestFormatter, bool useJson, UnwrappedTypesXmlSerializerManager xmlSerializerManager, string callbackParameterName)
147         {
148             if (type == null)
149             {
150                 return new NullMessageFormatter(useJson, callbackParameterName);
151             }
152             else if (useJson)
153             {
154                 return CreateJsonFormatter(operation, type, isRequestFormatter);
155             }
156             else
157             {
158                 return CreateXmlFormatter(operation, type, isRequestFormatter, xmlSerializerManager);
159             }
160         }
161 
SuppressReplyEntityBody(Message message)162         internal static void SuppressReplyEntityBody(Message message)
163         {
164             WebOperationContext currentContext = WebOperationContext.Current;
165             if (currentContext != null)
166             {
167                 OutgoingWebResponseContext responseContext = currentContext.OutgoingResponse;
168                 if (responseContext != null)
169                 {
170                     responseContext.SuppressEntityBody = true;
171                 }
172             }
173             else
174             {
175                 object untypedProp;
176                 message.Properties.TryGetValue(HttpResponseMessageProperty.Name, out untypedProp);
177                 HttpResponseMessageProperty prop = untypedProp as HttpResponseMessageProperty;
178                 if (prop == null)
179                 {
180                     prop = new HttpResponseMessageProperty();
181                     message.Properties[HttpResponseMessageProperty.Name] = prop;
182                 }
183                 prop.SuppressEntityBody = true;
184             }
185         }
186 
SuppressRequestEntityBody(Message message)187         internal static void SuppressRequestEntityBody(Message message)
188         {
189             WebOperationContext currentContext = WebOperationContext.Current;
190             if (currentContext != null)
191             {
192                 OutgoingWebRequestContext requestContext = currentContext.OutgoingRequest;
193                 if (requestContext != null)
194                 {
195                     requestContext.SuppressEntityBody = true;
196                 }
197             }
198             else
199             {
200                 object untypedProp;
201                 message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out untypedProp);
202                 HttpRequestMessageProperty prop = untypedProp as HttpRequestMessageProperty;
203                 if (prop == null)
204                 {
205                     prop = new HttpRequestMessageProperty();
206                     message.Properties[HttpRequestMessageProperty.Name] = prop;
207                 }
208                 prop.SuppressEntityBody = true;
209             }
210         }
211 
AttachMessageProperties(Message message, bool isRequest)212         protected virtual void AttachMessageProperties(Message message, bool isRequest)
213         {
214         }
215 
GetInputSerializers()216         protected abstract XmlObjectSerializer[] GetInputSerializers();
217 
GetOutputSerializer(Type type)218         protected abstract XmlObjectSerializer GetOutputSerializer(Type type);
219 
ValidateMessageFormatProperty(Message message)220         protected virtual void ValidateMessageFormatProperty(Message message)
221         {
222         }
223 
GetTypeForSerializer(Type type, Type parameterType, IList<Type> knownTypes)224         protected Type GetTypeForSerializer(Type type, Type parameterType, IList<Type> knownTypes)
225         {
226             if (type == parameterType)
227             {
228                 return type;
229             }
230             else if (knownTypes != null)
231             {
232                 for (int i = 0; i < knownTypes.Count; ++i)
233                 {
234                     if (type == knownTypes[i])
235                     {
236                         return type;
237                     }
238                 }
239             }
240             return parameterType;
241         }
242 
CreateXmlFormatter(OperationDescription operation, Type type, bool isRequestFormatter, UnwrappedTypesXmlSerializerManager xmlSerializerManager)243         public static SingleBodyParameterMessageFormatter CreateXmlFormatter(OperationDescription operation, Type type, bool isRequestFormatter, UnwrappedTypesXmlSerializerManager xmlSerializerManager)
244         {
245             DataContractSerializerOperationBehavior dcsob = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
246             if (dcsob != null)
247             {
248                 return new SingleBodyParameterDataContractMessageFormatter(operation, type, isRequestFormatter, false, dcsob);
249             }
250             XmlSerializerOperationBehavior xsob = operation.Behaviors.Find<XmlSerializerOperationBehavior>();
251             if (xsob != null)
252             {
253                 return new SingleBodyParameterXmlSerializerMessageFormatter(operation, type, isRequestFormatter, xsob, xmlSerializerManager);
254             }
255             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.GetString(SR2.OnlyDataContractAndXmlSerializerTypesInUnWrappedMode, operation.Name)));
256         }
257 
CreateJsonFormatter(OperationDescription operation, Type type, bool isRequestFormatter)258         public static SingleBodyParameterMessageFormatter CreateJsonFormatter(OperationDescription operation, Type type, bool isRequestFormatter)
259         {
260             DataContractSerializerOperationBehavior dcsob = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
261             if (dcsob == null)
262             {
263                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR2.GetString(SR2.JsonFormatRequiresDataContract, operation.Name, operation.DeclaringContract.Name, operation.DeclaringContract.Namespace)));
264             }
265             return new SingleBodyParameterDataContractMessageFormatter(operation, type, isRequestFormatter, true, dcsob);
266         }
267 
CreateBodyWriter(object body)268         BodyWriter CreateBodyWriter(object body)
269         {
270             XmlObjectSerializer serializer;
271             if (body != null)
272             {
273                 serializer = GetOutputSerializer(body.GetType());
274                 if (serializer == null)
275                 {
276                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR2.GetString(SR2.CannotSerializeType, body.GetType(), this.operationName, this.contractName, this.contractNs, this.serializerType)));
277                 }
278             }
279             else
280             {
281                 serializer = null;
282             }
283             return new SingleParameterBodyWriter(body, serializer);
284         }
285 
ReadObject(Message message)286         protected virtual object ReadObject(Message message)
287         {
288             if (HttpStreamFormatter.IsEmptyMessage(message))
289             {
290                 return null;
291             }
292             XmlObjectSerializer[] inputSerializers = GetInputSerializers();
293             XmlDictionaryReader reader = message.GetReaderAtBodyContents();
294             if (inputSerializers != null)
295             {
296                 for (int i = 0; i < inputSerializers.Length; ++i)
297                 {
298                     if (inputSerializers[i].IsStartObject(reader))
299                     {
300                         return inputSerializers[i].ReadObject(reader, false);
301                     }
302                 }
303             }
304             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR2.GetString(SR2.CannotDeserializeBody, reader.LocalName, reader.NamespaceURI, operationName, contractName, contractNs, this.serializerType)));
305         }
306 
307         class NullMessageFormatter : IDispatchMessageFormatter, IClientMessageFormatter
308         {
309             bool useJson;
310             string callbackParameterName;
311 
NullMessageFormatter(bool useJson, string callbackParameterName)312             public NullMessageFormatter(bool useJson, string callbackParameterName)
313             {
314                 this.useJson = useJson;
315                 this.callbackParameterName = callbackParameterName;
316             }
317 
DeserializeReply(Message message, object[] parameters)318             public object DeserializeReply(Message message, object[] parameters)
319             {
320                 return null;
321             }
322 
DeserializeRequest(Message message, object[] parameters)323             public void DeserializeRequest(Message message, object[] parameters)
324             {
325             }
326 
SerializeReply(MessageVersion messageVersion, object[] parameters, object result)327             public Message SerializeReply(MessageVersion messageVersion, object[] parameters, object result)
328             {
329                 Message reply = Message.CreateMessage(messageVersion, (string)null);
330                 SuppressReplyEntityBody(reply);
331                 if (useJson && WebHttpBehavior.TrySetupJavascriptCallback(callbackParameterName) != null)
332                 {
333                     reply.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.JsonProperty);
334                 }
335                 return reply;
336             }
337 
SerializeRequest(MessageVersion messageVersion, object[] parameters)338             public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
339             {
340                 Message request = Message.CreateMessage(messageVersion, (string)null);
341                 SuppressRequestEntityBody(request);
342                 return request;
343             }
344         }
345 
346         class SingleParameterBodyWriter : BodyWriter
347         {
348             object body;
349             XmlObjectSerializer serializer;
350 
SingleParameterBodyWriter(object body, XmlObjectSerializer serializer)351             public SingleParameterBodyWriter(object body, XmlObjectSerializer serializer)
352                 : base(false)
353             {
354                 if (body != null && serializer == null)
355                 {
356                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
357                 }
358                 this.body = body;
359                 this.serializer = serializer;
360             }
361 
OnWriteBodyContents(XmlDictionaryWriter writer)362             protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
363             {
364                 if (body != null)
365                 {
366                     this.serializer.WriteObject(writer, body);
367                 }
368             }
369         }
370     }
371 }
372 
373