aaff3cea26afa649dfae46c54cce21f96044696a
[mono.git] / mcs / class / referencesource / System.ServiceModel.Web / System / ServiceModel / Web / WebOperationContext.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 #pragma warning disable 1634, 1691
5
6 namespace System.ServiceModel.Web
7 {
8     using System;
9     using System.Diagnostics.CodeAnalysis;
10     using System.IO;
11     using System.Net;
12     using System.Runtime.Serialization.Json;
13     using System.ServiceModel;
14     using System.ServiceModel.Channels;
15     using System.ServiceModel.Description;
16     using System.ServiceModel.Syndication;
17     using System.Text;
18     using System.Xml;
19     using System.Xml.Linq;
20     using System.Xml.Serialization;
21     using System.ServiceModel.Dispatcher;
22
23     public class WebOperationContext : IExtension<OperationContext>
24     {
25         internal static readonly string DefaultTextMediaType = "text/plain";
26         internal static readonly string DefaultJsonMediaType = JsonGlobals.applicationJsonMediaType;
27         internal static readonly string DefaultXmlMediaType = "application/xml";
28         internal static readonly string DefaultAtomMediaType = "application/atom+xml";
29         internal static readonly string DefaultStreamMediaType = WebHttpBehavior.defaultStreamContentType;
30
31         OperationContext operationContext;
32
33         public WebOperationContext(OperationContext operationContext)
34         {
35             if (operationContext == null)
36             {
37                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operationContext");
38             }
39             this.operationContext = operationContext;
40 #pragma warning disable 56506 // Microsoft, operationContext.Extensions is never null
41             if (operationContext.Extensions.Find<WebOperationContext>() == null)
42             {
43                 operationContext.Extensions.Add(this);
44             }
45 #pragma warning enable 56506
46         }
47
48         public static WebOperationContext Current
49         {
50             get
51             {
52                 if (OperationContext.Current == null)
53                 {
54                     return null;
55                 }
56                 WebOperationContext existing = OperationContext.Current.Extensions.Find<WebOperationContext>();
57                 if (existing != null)
58                 {
59                     return existing;
60                 }
61                 return new WebOperationContext(OperationContext.Current);
62             }
63         }
64
65         public IncomingWebRequestContext IncomingRequest
66         { 
67             get { return new IncomingWebRequestContext(this.operationContext); } 
68         }
69
70         public IncomingWebResponseContext IncomingResponse
71         { 
72             get { return new IncomingWebResponseContext(this.operationContext); } 
73         }
74
75         public OutgoingWebRequestContext OutgoingRequest
76         { 
77             get { return new OutgoingWebRequestContext(this.operationContext); } 
78         }
79
80         public OutgoingWebResponseContext OutgoingResponse
81         { 
82             get { return new OutgoingWebResponseContext(this.operationContext); } 
83         }
84
85         public void Attach(OperationContext owner)
86         {
87         }
88         
89         public void Detach(OperationContext owner)
90         {
91         }        
92
93         public Message CreateJsonResponse<T>(T instance)
94         {
95             DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
96             return CreateJsonResponse<T>(instance, serializer);
97         }
98
99         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "CreateJsonMessage requires the DataContractJsonSerializer.  Allowing the base type XmlObjectSerializer would let deveopers supply a non-Json Serializer.")]
100         public Message CreateJsonResponse<T>(T instance, DataContractJsonSerializer serializer)
101         {
102             if (serializer == null)
103             {
104                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
105             }
106             Message message = Message.CreateMessage(MessageVersion.None, (string)null, instance, serializer);
107             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.JsonProperty);
108             AddContentType(WebOperationContext.DefaultJsonMediaType, this.OutgoingResponse.BindingWriteEncoding);
109             return message;
110         }
111
112         public Message CreateXmlResponse<T>(T instance)
113         {
114             System.Runtime.Serialization.DataContractSerializer serializer = new System.Runtime.Serialization.DataContractSerializer(typeof(T));
115             return CreateXmlResponse(instance, serializer);
116         }
117
118         public Message CreateXmlResponse<T>(T instance, System.Runtime.Serialization.XmlObjectSerializer serializer)
119         {
120             if (serializer == null)
121             {
122                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
123             }
124             Message message = Message.CreateMessage(MessageVersion.None, (string)null, instance, serializer);
125             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
126             AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
127             return message;
128         }
129
130         public Message CreateXmlResponse<T>(T instance, XmlSerializer serializer)
131         {
132             if (serializer == null)
133             {
134                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
135             }
136             Message message = Message.CreateMessage(MessageVersion.None, (string)null, new XmlSerializerBodyWriter(instance, serializer));
137             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
138             AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
139             return message;
140         }
141
142         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Other XNode derived types such as XAttribute don't make sense in this context, so we are not using the XNode base type.")]
143         public Message CreateXmlResponse(XDocument document)
144         {
145             if (document == null)
146             {
147                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document");
148             }
149             Message message;
150             if (document.FirstNode == null)
151             {
152                 message = Message.CreateMessage(MessageVersion.None, (string)null);
153             }
154             else
155             {
156                 message = Message.CreateMessage(MessageVersion.None, (string)null, document.CreateReader());
157             }
158             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
159             AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
160             return message;
161         }
162
163         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Other XNode derived types such as XAttribute don't make sense in this context, so we are not using the XNode base type.")]
164         public Message CreateXmlResponse(XElement element)
165         {
166             if (element == null)
167             {
168                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("element");
169             }
170             Message message = Message.CreateMessage(MessageVersion.None, (string)null, element.CreateReader());
171             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
172             AddContentType(WebOperationContext.DefaultXmlMediaType, this.OutgoingResponse.BindingWriteEncoding);
173             return message;
174         }
175
176         public Message CreateAtom10Response(SyndicationItem item)
177         {
178             if (item == null)
179             {
180                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
181             }
182             Message message = Message.CreateMessage(MessageVersion.None, (string)null, item.GetAtom10Formatter());
183             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
184             AddContentType(WebOperationContext.DefaultAtomMediaType, this.OutgoingResponse.BindingWriteEncoding);
185             return message;
186         }
187
188         public Message CreateAtom10Response(SyndicationFeed feed)
189         {
190             if (feed == null)
191             {
192                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("feed");
193             }
194             Message message = Message.CreateMessage(MessageVersion.None, (string)null, feed.GetAtom10Formatter());
195             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
196             AddContentType(WebOperationContext.DefaultAtomMediaType, this.OutgoingResponse.BindingWriteEncoding);
197             return message;
198         }
199
200         public Message CreateAtom10Response(ServiceDocument document)
201         {
202             if (document == null)
203             {
204                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("document");
205             }
206             Message message = Message.CreateMessage(MessageVersion.None, (string)null, document.GetFormatter());
207             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.XmlProperty);
208             AddContentType(WebOperationContext.DefaultAtomMediaType, this.OutgoingResponse.BindingWriteEncoding);
209             return message;
210         }
211
212         public Message CreateTextResponse(string text)
213         {
214             return CreateTextResponse(text, WebOperationContext.DefaultTextMediaType, Encoding.UTF8);
215         }
216
217         public Message CreateTextResponse(string text, string contentType)
218         {
219             return CreateTextResponse(text, contentType, Encoding.UTF8);
220         }
221
222         public Message CreateTextResponse(string text, string contentType, Encoding encoding)
223         {
224             if (text == null)
225             {
226                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("text");
227             }
228             if (contentType == null)
229             {
230                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
231             }
232             if (encoding == null)
233             {
234                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
235             }
236
237             Message message = new HttpStreamMessage(StreamBodyWriter.CreateStreamBodyWriter((stream) =>
238             {
239                 byte[] preamble = encoding.GetPreamble();
240                 if (preamble.Length > 0)
241                 {
242                     stream.Write(preamble, 0, preamble.Length);
243                 }
244                 byte[] bytes = encoding.GetBytes(text);
245                 stream.Write(bytes, 0, bytes.Length);
246                 stream.Flush();
247             }));
248             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
249             AddContentType(contentType, null);
250             return message;
251         }
252
253         public Message CreateTextResponse(Action<TextWriter> textWriter, string contentType)
254         {
255             Encoding encoding = this.OutgoingResponse.BindingWriteEncoding;
256             if (encoding == null)
257             {
258                 encoding = Encoding.UTF8;
259             }
260             return CreateTextResponse(textWriter, contentType, encoding);
261         }
262
263         public Message CreateTextResponse(Action<TextWriter> textWriter, string contentType, Encoding encoding)
264         {
265             if (textWriter == null)
266             {
267                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("textWriter");
268             }
269             if (contentType == null)
270             {
271                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
272             }
273             if (encoding == null)
274             {
275                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("encoding");
276             }
277
278             Message message = new HttpStreamMessage(StreamBodyWriter.CreateStreamBodyWriter((stream) =>
279             {
280                 using (TextWriter writer = new StreamWriter(stream, encoding))
281                 {
282                     textWriter(writer);
283                 }
284             }));
285             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
286             AddContentType(contentType, null);
287             return message;
288         }
289
290         public Message CreateStreamResponse(Stream stream, string contentType)
291         {
292             if (stream == null)
293             {
294                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("stream");
295             }
296             if (contentType == null)
297             {
298                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
299             }
300             Message message = ByteStreamMessage.CreateMessage(stream);
301             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
302             AddContentType(contentType, null);
303             return message;
304         }
305
306         [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Using the StreamBodyWriter type instead of the BodyWriter type for discoverability.  The StreamBodyWriter provides a helpful overload of the OnWriteBodyContents method that takes a Stream")]
307         public Message CreateStreamResponse(StreamBodyWriter bodyWriter, string contentType)
308         {
309             if (bodyWriter == null)
310             {
311                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("bodyWriter");
312             }
313             if (contentType == null)
314             {
315                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
316             }
317             Message message = new HttpStreamMessage(bodyWriter);
318             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
319             AddContentType(contentType, null);
320             return message;
321         }
322
323         public Message CreateStreamResponse(Action<Stream> streamWriter, string contentType)
324         {
325             if (streamWriter == null)
326             {
327                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("streamWriter");
328             }
329             if (contentType == null)
330             {
331                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
332             }
333             Message message = new HttpStreamMessage(StreamBodyWriter.CreateStreamBodyWriter(streamWriter));
334             message.Properties.Add(WebBodyFormatMessageProperty.Name, WebBodyFormatMessageProperty.RawProperty);
335             AddContentType(contentType, null);
336             return message;
337         }
338
339         public UriTemplate GetUriTemplate(string operationName)
340         {
341             if (String.IsNullOrEmpty(operationName))
342             {
343                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("operationName");
344             }
345
346             WebHttpDispatchOperationSelector selector = OperationContext.Current.EndpointDispatcher.DispatchRuntime.OperationSelector as WebHttpDispatchOperationSelector;
347             if (selector == null)
348             {
349                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new InvalidOperationException(SR2.GetString(SR2.OperationSelectorNotWebSelector, typeof(WebHttpDispatchOperationSelector))));
350             }
351             return selector.GetUriTemplate(operationName);
352         }
353
354         void AddContentType(string contentType, Encoding encoding)
355         {      
356             if (string.IsNullOrEmpty(this.OutgoingResponse.ContentType))
357             {
358                 if (encoding != null)
359                 {
360                     contentType = WebMessageEncoderFactory.GetContentType(contentType, encoding);
361                 }
362                 this.OutgoingResponse.ContentType = contentType;
363             }
364         }
365
366         class XmlSerializerBodyWriter : BodyWriter
367         {
368             object instance;
369             XmlSerializer serializer;
370
371             public XmlSerializerBodyWriter(object instance, XmlSerializer serializer)
372                 : base(false)
373             {
374                 if (instance == null)
375                 {
376                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
377                 }
378                 if (serializer == null)
379                 {
380                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serializer");
381                 }
382                 this.instance = instance;
383                 this.serializer = serializer;
384             }
385
386             protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
387             {
388                 serializer.Serialize(writer, instance);
389             }
390         }
391     }
392 }
393